Si ya terminaste de desarrollar tu proyecto web en el framework de Python, Django y ahora quiere entender que es lo que necesitas para subirlo a producción, en este tutorial te enseñare paso a paso a desplegar aplicaciones web creadas en Django gratuitamente usando un servicio llamado Railway.
Puedes seguir los mismos pases que se muestra en este tutorial para poder desplegar tanto proyectos de Django como proyectos de Django REST Framework
Requerimientos del Tutorial
- Tener una cuenta en Railway
- Tener Python instalado, de preferencia la version 3.9 o más novedosas
Creación del proyecto de Backend
Primero empecemos creando un proyecto en django:
mkdir django-railway
cd django-railway
Luego creemos un entorno virtual de Python, para esto puedes usar multiples paquetes como pipenv, conda, o virtualenv, entre otros. en mi caso estaré usando virtualenv.
Luego para crear el entorno virtual ejecuta:
python -m venv venv
Activemos el entorno con:
activate
o si estas en Linux
source ./venv/bin/activate
Instalano Django y creando proyecto
pip install django
django-admin startproject django_project .
Ejecutemos el proyecto:
python manage.py runserver
Ahora creemos una aplicacion
django-admin startapp blog
django-admin startapp home
Añadamos estas aplicaciones en settings.py
en django_project/settings.py
INSTALLED_APPS = [
"blog",
"home"
]
ejecutemos las migraciones
python manage.py migrate
Añde algunas modificacione
blog/views.py
from django.shortcuts import render
# Create your views here.
def render_articles(request):
return render(request, 'articles.html')
en blog/templates/articles.html
<h1>Article</h1>
blog/urls.py
from django.urls import path
from blog import views
urlpatterns = [
path('', views.render_articles, name='articles'),
]
django_project/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("articles/", include("blog.urls")),
]
Ahora hagamos lo mismo en la aplicacion home
Creando Pagina Home
creemos un archivo templates/home.html
<h1>Home</h1>
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/articles">Articles</a>
</li>
</ul>
home/urls.py
from django.urls import path
from home import views
urlpatterns = [
path("", views.render_home),
]
home/views.py
from django.shortcuts import render
# Create your views here.
def render_home(request):
return render(request, "home.html")
Luego en django_project/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('home.urls')),
path('articles/', include('blog.urls')),
]
Cargando Archivos esaticos
creemos un directorio estatico dentro de articles
blog/static/images/
blog/static/css/
colocar una imagen dentro de images, en mi caso colocare una imagen llamada fazt.png
Luego en settings añade lo siguiente:
STATICFILES_DIRS = [os.path.join(BASE_DIR,'blog/static'),]
y finalmente actuliza blog/templates/articles.html
{% load static %}
<h1>Article</h1>
<img src="{% static '/images/fazt.png' %}" style="width: 200px;">
Ahora vamos a desplegarlo
Primero creemos un archivo .gitignore, ignorando estos:
__pycache__
venv
db.sqlite3
runtime
añade un archivo runtime.txt con lo siguiente:
3.9.13
Puedes usar para obtener tu vrsion de Python
python --version
Configura el Procfile
pip install gunicorn
crea un archivo Procfile en tu proyecto con lo siguiente:
web: gunicorn django_project.wsgi
o tambien
web: gunicorn django_project.wsgi --log-file -
Crear archivo requeriments.txt
Luego generemos el archivo requirements.txt
pip freeze > requirments.txt
crea un commit
Luego creemos nuestro primer commit:
git add .
git commit -m "first commit"
Subelo a github, tambien puedes usar este comando si tines instalado github cli:
gh repo create django-railway --private --source=.
y luego puede subirlo usando
git push origin master
Subiendo a railway
Puedes subir a Railway usando tu cuenta de github y el repositorio que acabamos de crear
Primero crea un proyecto
selecciona tu repositorio
Añade una URL generada y Listo ya tienes desplegado tu proyecto.
es necesario tener en cuenta que Django 5.0 requiere una version superior a python 3.10, y tambien railway al estar basados en paquetes Nix solo soporta hasta la 11, asi que es necesario ver la version del paquete: https://nixpacks.com/docs/providers/python#setup
Solucionar Error ALLOWED_HOSTS
Ahora ya esta subido, pero si entramos en la URL obtendremos un error como este:
Invalid HTTP_HOST header: 'django-railway-production.up.railway.app'. You may need to add 'django-railway-production.up.railway.app' to ALLOWED_HOSTS.
esto significa que Django no permite a este dominio, pero podemos añadirlo
ALLOWED_HOSTS = ["django-railway-production.up.railway.app"]
si añades esto tambien tiene que añadir el localhost:
ALLOWED_HOSTS = ["localhost", "django-railway-production.up.railway.app"]
O si queremos que todos puedan acceder:
ALLOWED_HOSTS = ["*"]
Con esto si subes tus cambios con otro git push y esperas ahora veras tu sitio, pero no los archivos estaticos.
Archivos estaticos
si ejecutamos
python manage.py collectstatic
este dara un error diciendo que necesitamos añadir el STATIC_ROOT, asi que hagamoslo, en settings.py, cerca de static_url añadir:
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
STATIC_DIRS = [os.path.join(BASE_DIR, "static")] # esto ya lo habiamos añadido
Ahora Puedes volver a ejecutar la recoleccion de archivos estaticos:
python manage.py collectstatic
esto creara una carpeta llamada statifiles en la raiz del proyecto donde colocara todos los archivos estaticos, como las imagenes o css y javascript que hubieramos añadido.
Ahora muchos hacen un commit de estos archivos, pero en realidad en lo personal prefiero generarlos cada vez que hago un despliegue para mantenerlos al dia y sin la nacesidad de hacer un tracking con git
asi que añade a tu .gitignore
staticfiles
y actualiza el Procfile a:
web: python manage.py collectstatic && gunicorn django_project.wsgi
Instalando Whitenoise
este es un modulo que permite servir a nuestras aplicaciones web nuestro propio contenido estatico
pip install whitenoise
In your settigs.py:
INSTALLED_APPS = [
'django.contrib.staticfiles',
'whitenoise.runserver_nostatic',
"blog",
"home"
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
]
este tambien pide que tengamos configurado variables como:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
pero como ya lo teniamos simplemente vamos a añadir esta:
STATICFILES_STORAGE="whitenoise.storage.CompressedManifestStaticFilesStorage"
no olvides ejecutar
pip freeze > requirments.txt
y subelo otra vez, y si esperas un poco a que se ectualice ahora podras ver tus contenidos estaticos terminados.
incluso puedes entrar al panel administrador, pero aun necesitamos crear un usuario asi que la pregunta es como ejecutas un comando en producción
CSRF
intenta hacer login en /admin
sin embargo puedes obtener un error de csrf trusted origins
asi que añade el dominio en settings.py
CSRF_TRUSTED_ORIGINS = ['http://*', 'https://django-railway-production.up.railway.app']
https://docs.djangoproject.com/en/4.0/ref/settings/#csrf-trusted-origins
Ejecutar migraciones en produccion
no such table: auth_user
necesitamos ejecutar las migraciones, atcualiza el procfile
web: python manage.py collectstatic && python manage.py migrate && gunicorn django_project.wsgi
pero antes de ejecutarlo vamos a crear una base de datos de postgresql reemplazando a sqlit3
Primero añadamos una base de datos
railway add
escogemos postgresql
para ver las variables
railway service
railway variables
pip install psycopg2
tenemos dos formas de conectarnos a la base de datos de postgres usando las variables o una cadena de coneccion, algo como esto:
DATABASES = {
'default': {
#'ENGINE': 'django.db.backends.sqlite3',
'ENGINE': 'django.db.backends.postgresql',
'NAME': '',
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
}
}
en lo peronal prefiero la cadenas de conexion al ser más simple
pip install dj-database-url
DATABASES = {
'default': dj_database_url.config(default='postgresql://postgres:postgres@localhost:5432/mysite', conn_max_age=600)}
en lo personal prefiero esto, al permitir usar sqlite en desarrollo y en produccion postgresql
DATABASES = {
'default': dj_database_url.config(default='sqlite:///db.sqlite3')
}
pip freeze
Añadiendo variables de entorno
pip intsall python-dotenv
from dotenv import load_dotenv
load_dotenv()
DATABASES = { 'default': dj_database_url.config(default=os.getenv('DATABASE_URL')) }
luego subir y finalmente podemos ejecutar
railway run python manage.py migrate
railway run python manage.py createsuperuser
Por cierto, ejecutando el comando de railway me encontrado con este error
Unable to parse config file, regeneratingal ejecutar, esto asi que si quieres solucionar tienes que eliminar el archivo.railway/config.json
Listo con esto ya lo tenemos en producción.
Ejecutar comandos railway
instalar railway cli
npm i -g @railway/cli
Para autenticarse con Railway desde consola
railway login
esto abrira una pestaña del navegador.
Ahora enlacemos nuestro proyecto local con nuestro proyecto remoto creado con railway:
railway link
railway run python --version
railway run python manage.py createsuperuser
esto creara un usuario tanto en local como en remoto, ahora intenta hace un login.
- Documentación del CLI de Railway
- https://stackoverflow.com/questions/73843151/how-to-run-commands-in-cli-with-railway-app
Añadamos Django REST Framework
pip install djangorestframework
django-admin startapp tasks
añadamos la aplicacion de tareas en settings
INSTALLED_APPS = [
...
"whitenoise.runserver_nostatic",
"blog",
"home",
"tasks",
"rest_framework",
]
creemos un modelo en tasks/models.py
class Task(models.Model):
title = models.CharField(max_length=200)
completed = models.BooleanField(default=False, blank=True, null=True)
def __str__(self):
return self.title
luego en ejecuta
python manage.py makemigrations
python manage.py migrate
añade en admin lo siguiente, para poder crear tareas desde el administrador
from django.contrib import admin
from .models import Task
# Register your models here.
admin.site.register(Task)
python manage.py runserver
Creando API
crear un archivo tasks/serializers.py
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = '__all__'
luego desde tasks/views.py
from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializer
# Create your views here.
class TaskList(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
Luego desde tasks/urls.py
from rest_framework import routers
from django.urls import path, include
from . import views
router = routers.DefaultRouter()
router.register(r'tasks', views.TaskList, basename='tasks')
urlpatterns = router.urls
finalmente en django_project/urls.py
urlpatterns = [
...
path("api/", include("tasks.urls")),
]
visita http://localhost:8000/api/tasks
ejecuta pip frezee y despliegalo
finalmente puedes usar alguna web como httpie.io para poder probar la aplicacion REST
Recomendaciones Finales
- Siempre configurar variables de entorno para evitar colocar datos sensibles y reutilizar configuraciones
- usar un administrador de paquetes como pipenv o poetry
subir a Railway
de SQlite a Otra base de datos
vamos a cambiar de sql a postgresql:
https://stackoverflow.com/questions/20532504/django-app-deployment-with-sqlite-database
https://dev.to/osahenru/using-railway-app-to-deploy-your-django-project-3ah1
Más Recursos
https://dev.to/mr_destructive/django-postgresql-deployment-on-railway-app-d54
https://stackoverflow.com/questions/73843151/how-to-run-commands-in-cli-with-railway-app
https://medium.com/codex/deploying-react-through-djangos-static-files-part-1-dev-setup-8a3a7b93c809