Todo app Django + Reactjs Parte 1 (Back-End)
Guía Básica para crear una todo app con django y reactjs.
Nota: En el siguiente post, se asumirá que tiene el ambiente virtual de python listo, que ya se tiene experiencia previa en django(básica como mínimo) y ya tenga iniciado un paquete de nodejs en el mismo directorio.
Instalar Dependencias.
Python: instalaremos las dependencias que son necesarios para poder crear el Back-End.
pip install Django django-webpack-loader djangorestframework
Javascript: ahora instalaremos las dependencias del Front-End, puedes usar npm(node package manager) o yarn, para instalar dichas dependencias.
npm i --save-dev axios babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0 react react-hot-loader webpack webpack-bundle-tracker webpack-dev-server react-dom
Back-End.
Antes de iniciar con el Back-End, vamos a crear el proyecto llamado ptodo.
django-admin startporject ptodo
Ahora vamos a configurar el proyecto, para eso tenemos que abrir el archivo settings.py, que se encuentra en el directorio ptodo. Vamos a agregar el procesador de contexto csrf, que sirve para generar un token que se va a montar en el template home.html y será enviado por el cliente, solo cuando valla a agregar o actualizar una tarea. Nota: Dicha táctica es utilizada para evitar ataques Cross Site Request. Para mas información acerca del ataque CSRF apreté aquí y para saber como renderizar y utilizar el csrf token con django apreté aquí.
TEMPLATES = [
{
.
.
.'OPTIONS': {
'context_processors': [
.
.
. 'django.template.context_processors.csrf',
],
},
},
]
Ahora, agregaremos la aplicación webpack_loader a la lista INSTALLED_APPS. Dicha aplicación será utilizada para poder usar Reactjs con django, tanto en desarrollo como en producción.
INSTALLED_APPS = [
.
.
. 'webpack_loader'
]
Después se va a agregar la variable WEBPACK_LOADER, para configurar la aplicación webpack_loader. En dicha variable, vamos a declarar el nombre del directorio, el cual va a contener los módulos de javascript compilados y la ruta a los stat de cada compilación.
WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'js/',
'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
}
}
Ahora vamos a declarar las rutas a los templates y archivos estáticos del proyecto.
TEMPLATES = [
{
.
.
.'DIRS': [
os.path.join(BASE_DIR, 'ptodo', 'core', 'templates')
], .
.
. },
]STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'ptodo', 'core', 'static'),
)
Ya que hemos terminado de configurar el proyecto, vamos a crear el directorio core dentro del directorio que contiene el modulo settings.py y dentro de core, crearemos los siguientes módulos: views.py __init__.py. También crearemos los directorios templates y static; dentro del directorio templates, vamos a crear un template llamado home.html y dentro del directorio static, vamos a crear el directorio js(va a contener los módulos de javascript compilados). Después de eso, volvemos a la raíz del proyecto, se crea la aplicación tasks y se agrega la el nombre de la misma a la variable INSTALLED_APPS .
./manage.py startapp tasks# settings.py
INSTALLED_APPS = [
.
.
. 'tasks'
]
El árbol de directorios, va a quedar algo así:
ptodo/
|
| - ptodo/
| |
| | - core/
| | |
| | | - static/
| | | |
| | | | - js/
| | |
| | | - templates/
| | | |
| | | | - home.html
| | |
| | | - __init__.py
| | | - views.py
| |
| | - __init__.py
| | - settings.py
| | - urls.py
| | - wsgi.py
|
| - tasks/
| |
| | - __init__.py
| | - admin.py
| | - apps.py
| | - models.py
| | - tests.py
| | - views.py
| | - migrations/
| | |
| | | - __init__.py
|
Ahora vamos al modulo views.py, que se encuentra en ptodo/core/. En dicho modulo, vamos a crear una vista basada en clase de python(class based view). Dicha clase va a heredar del objeto TemplateView, ahora vamos a declarar un variable global, llamada template_name, el cual va a contener el nombre del template a renderizar.
from django.views.generic import TemplateViewclass HomeView(TemplateView):
template_name = "home.html"
Después, registramos dicha vista en el modulo urls.py, que se encuentra en el directorio padre del directorio actual.
.
.
.from .core.views import HomeViewurlpatterns = [
.
.
. url(r'^$', HomeView.as_view(), name='home'),
]
Ahora abrimos el template llamado home.html, ubicado en el directorio /ptodo/core/templates/home.html y agregamos lo siguiente:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Home - todoapp</title>
<base data-token="{{ csrf_token }}"/>
</head>
<body>
<div id="root"></div>
</body>
</html>
Si ahora corremos el servidor de desarrollo de django, navegas a la dirección http://localhost:8000/ o la dirección que le hallas asignado, podrás ver que el proyecto corre correctamente.
Ahora vamos a crear un modelo en la aplicación tasks, llamado TaskModel. Dicho modelo, va a contener el nombre de la tarea agregada y la fecha de su creación. Después de eso, vamos a mapear el modelo a la base de datos y vamos al modulo admin.py, donde vamos a registrar el modelo, para que sea visible en la vista del administrador.
# tasks/models.py
from django.db import modelsclass TaskModel(models.Model):
task = models.CharField(max_length=255, blank=False, null=False)
created = models.DateTimeField(auto_now_add=True) def __str__(self):
return self.task class Meta:
verbose_name = 'Task'
verbose_name_plural = 'Tasks'# shell
./manage.py makemigrations && ./manage.py migrate# admin.py
from django.contrib import admin
from .models import TaskModel@admin.register(TaskModel)
class TaskAdmin(admin.ModelAdmin):
pass
Ahora vamos a crear un modulo llamado serializers.py, en dicho modulo vamos a crear un objeto serializer, llamado TaskSerializer, el cual se va a encargar de serializar un objeto TaskModel o datos enviados por el cliente en formato json. En el serializer, se esta implementando los método create y update, el cual nos permite crear y actualizar tareas, respectivamente.
from rest_framework import serializersfrom .models import TaskModel# cambiando el formate de
# la fecha y hora.
FORMAT = {
'format': '%d/%m/%Y %H:%M:%S',
'input_formats': ['%d/%m/%Y %H:%M:%S']
}class TaskSerializer(serializers.Serializer):
task_name = serializers.CharField(source='task', max_length=255)
task_id = serializers.IntegerField(source='id', read_only=True, required=False)
task_created_time = serializers.DateTimeField(source='created', read_only=True, required=False, **FORMAT) def create(self, validated_data):
task_name = validated_data.pop('task')
task = TaskModel(task=task_name)
task.save()
return task def update(self, instance, validated_data):
instance.task = validated_data.get('task', instance.task)
instance.save()
return instance
Ahora vamos a crear los API view or endpoints, que se van a encargar de servir solicitudes ajax(mostrar, agregar, actualizar y borrar tareas).
ListTasksApiView:
Se encarga de devolver todas la tareas, registradas en la base de datos.
# Muestra todas las tareas agregadas.
class ListTasksApiView(TaskApiMixin, APIView):
model = TaskModel
serializer = TaskSerializer def get(self, request, format=None):
model = self.get_model
tmpTasks = model.objects.all().order_by('-id')
serializer = self.get_serializer(tmpTasks, many=True)
return Response({
'tasks': serializer.data,
'message': '',
'code': 200
})
AddTaskApiView:
Se encarga de agregar una tarea a la base de datos.
# Agregar una tarea.
class AddTaskApiView(TaskApiMixin, APIView):
serializer = TaskSerializer def post(self, request, format=None):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({
'code': 201,
'message': '',
'task': serializer.data
})
return Response({
'code': 400,
'message': 'Cannot add task.',
'task': ''
})
DeleteTaskApiView:
Se encarga de eliminar una tarea del sistema.
class DeleteTaskApiView(TaskApiMixin, APIView):
model = TaskModel
serializer = TaskSerializer def get(self, request, format=None):
model = self.get_model
task_id = request.query_params.get('task_id', None)
task = None try:
task = model.objects.get(id=task_id)
except model.DoesNotExist:
pass if task is None:
return Response({
'code': 404,
'message': 'task does not exists',
'task': ''
}) taskResponse = self.get_serializer(task).data
task.delete()
return Response({
'code': 200,
'message': '',
'task': taskResponse
})
UpdateTaskApiView:
Se encarga de actualizar una tarea(Solo el nombre de dicha tarea).
class UpdateTaskApiView(TaskApiMixin, APIView):
model = TaskModel
serializer = TaskSerializer def put(self, request, format=None):
task = None try:
task = self.get_model.objects.get(
id=request.data.get('task_id', -1))
except self.get_model.DoesNotExist:
pass if task is None:
return Response({'code': 404, 'message': 'task not found'}) serializer = self.get_serializer(task,
data=request.data, partial=True) if serializer.is_valid():
serializer.save()
return Response({'code': 202, 'message': ''})
return Response({'code': 404, 'message': 'cannot update task.'})
DeleteAllTasksApiView:
Se encarga de eliminar todas las tareas del sistema.
class DeleteAllTasksApiView(TaskApiMixin, APIView):
model = TaskModel def get(self, request, format=None):
model = self.get_model
all_tasks = model.objects.all() if len(all_tasks) == 0:
return Response({
'code': 404,
'message': 'There\'s no tasks to delete.'}) for task in all_tasks:
task.delete()
return Response({'code': 200, 'message': ''})
Ahora, en el directorio de la aplicacion tasks, vamos a crear un modulo, llamado urls.py, el cual, va a contener los endpoints para agregar, actualizar, borrar las tareas.
from django.conf.urls import url from .views import (
ListTasksApiView,
AddTaskApiView,
DeleteTaskApiView,
DeleteAllTasksApiView,
UpdateTaskApiView
)urlpatterns = [ url(r'^list/$', ListTasksApiView.as_view(), name='list_tasks'),
url(r'^add/$', AddTaskApiView.as_view(), name='add_task'),
url(r'^delete/$', DeleteTaskApiView.as_view(), name='delete_task'),
url(r'^update/$', UpdateTaskApiView.as_view(),name='update_task_name'),
url(r'^delete/all/$', DeleteAllTasksApiView.as_view(),
name='delete_all_task'),
]
Una vez agregado los endpoints, necesitamos registrar que la aplicación task esta declarando rutas a la api, para ello, tenemos que ir al modulo urls.py, que se encuentra en la ruta /ptodo/urls.py, dentro del mismo agregamos lo siguiente:
from django.conf.urls import url, include
from django.contrib import adminfrom .core.views import HomeViewurlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', HomeView.as_view(), name='home'),
url(r'^task/', include('tasks.urls', namespace="TASKS")),]
El backend esta listo, es muy sencillo de hacer y de leer, en caso de que tengan preguntas o se me he equivocado en algo, comenten =). Podrás encontrar la el codigo de la aplicación de ptodo en github.
El fuente del proyecto, ptodo se encuentra en el siguiente enlace:
Saludos.