Creación del primer endpoint

- Modelo contacto en core/models.py
- Creación utils/model_abstracts para heredar y usar id con valor uuid
- Serializador en core/serializers hereda de rest_frameworks.serializers
- Vista contacto en core/views.py
- Registro de app en admin
- Migraciones
This commit is contained in:
devfzn 2023-03-29 18:35:23 -03:00
parent a36f93289f
commit ca2156ef37
Signed by: devfzn
GPG Key ID: E070ECF4A754FDB1
10 changed files with 227 additions and 9 deletions

119
README.md
View File

@ -26,20 +26,21 @@ pip install djangorestframework
```py
pip install django-extensions djangorestframework djangorestframework-jsonapi \
inflection python-dotenv sqlparse
```
Django utiliza SQLite3 por defecto facilitar el desarrolo, en este proyecto se
utliza MariaDB, pero es opcional.
# MariaDB
```py
pip install mysqlclient
```
### Inicio del proyecto
**Creación del proyecto Django**
```sh
mkdir backend
# Creación del proyecto Django
django-admin startproject drf_course backend
```
@ -58,7 +59,7 @@ python manage.py startapp core
Archivo [./backend/drf_course/settings.py](./backend/drf_course/settings.py).
Importar `.env` usando *python-dotenv*.
Importar variables de entorno usando *python-dotenv* del archivo en `.backend/.env`.
```py
from dotenv import load_dotenv
@ -120,7 +121,7 @@ REST_FRAMEWORK = {
```
En caso de utilizar MariaDB, cambiar la declaración de *DATABASES*, para usar
las variables de entorno declaradas en [./backend/env](./backend/.env)
las variables de entorno declaradas en `./backend/.env`
```py
DATABASES = {
@ -151,3 +152,111 @@ urlpatterns += [
]
```
#### Migrar y probar aplicación
```sh
python manage.py migrate
python manage.py runserver
```
### Creación del primer endoint
Creación de endpoint de ***contacto***, para que un usuario pueda enviar su
*nombre*, *email*, y *mensaje* al backend.
Para ello se requiere:
- Un modelo que almacene la captura de los datos entrantes.
- Un serializador que procese los datos entrantes del usuario y envíe un
mensaje de respuesta.
- Una vista que encapsule las llamadas a los métodos REST HTTP comunes.
- Una ruta `/url` llamada `/contact/`.
#### Model
[./backend/core/models.py](./backend/core/models.py)
Utiliza modelos abstractos del modulo
*django_extensions*; `TimeStampedModel` (campos como *created*), `ActivatorModel`
(campos *status*, *activated date*, *deactivated date* ), `TitleDescriptionModel`
(campos de texto *textfield* y *charfield*).
Clase **Contact** hereda de estos modelos. Todas las tablas del proyecto tendrán
un campo *uuid* como campo id. Además de un campo *email* de Django. Y método de
representación del modelo en cadena de texto.
**Modelo abstracto**
Para implementar *uuid* en vez de *id* como campo identificador en todos los
modelos, se crea el modulo [model_abstracts.py](./backend/utils/model_abstracts.py)
que hereda de *models* de *django.db* y utliza el campo *id*. Utiliza el campo *UUID*.
#### Serializer
Para convertir los datos de entrada *json* en tipos de datos de python, y
viceversa, se crea el archivo [./backend/core/serializer.py](./backend/core/serializer.py)
en la app *core*. Este hereda de la clase *serializers* del modulo *rest_framework*
e implementa sus campos (*CharField* *EmailField*).
#### View
[./backend/core/views.py](./backend/core/views.py)
El uso de la clase *APIView* es muy similar al una vista regular, la petición
entrante es enviada a un manejador apropiado para el método, como `.get()` o
`.post()`. Además se pueden establecer otros atributos en la clase que controla
varios aspectos de las normas de la API.
#### Route & URL
[./drf_course/urls.py](./drf_course/urls.py)
El framework REST añade soporte para ruteo automático de URLs a Django y provee
al programador de una simple, rápida y consistente forma de enlazar la lógica
de la vista a un conjunto de URLs.
#### Registrar app en panel de administración
Importar modelo y registrar en [./backend/core/admin.py](./backend/core/admin.py).
Crear las migraciones y migrar.
```py
python manage.py makemigrations
python manage.py migrate
```
Finalmente, crear **super usuario**.
```py
python manage.py createsuperuser
```
#### Probar API
```sh
http http://127.0.0.1:8000/contact/ name="DevFzn" message="prueba" email="devfzn@mail.com"
HTTP/1.1 200 OK
Allow: POST, OPTIONS
Content-Length: 155
Content-Type: application/vnd.api+json
Cross-Origin-Opener-Policy: same-origin
Date: Wed, 29 Mar 2023 20:05:32 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.10.10
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"data": {
"attributes": {
"email": "devfzn@mail.com",
"message": "prueba",
"name": "DevFzn"
},
"id": "bef5e90c-821a-4d04-98ef-c0a0adde5ec1",
"type": "ContactAPIView"
}
}
```

View File

@ -1,3 +1,6 @@
from django.contrib import admin
from .models import Contact
# Register your models here.
@admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
list_display = ('id', 'title', 'description', 'email')

View File

@ -0,0 +1,33 @@
# Generated by Django 4.1.7 on 2023-03-29 19:55
from django.db import migrations, models
import django_extensions.db.fields
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Contact',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('title', models.CharField(max_length=255, verbose_name='title')),
('description', models.TextField(blank=True, null=True, verbose_name='description')),
('status', models.IntegerField(choices=[(0, 'Inactive'), (1, 'Active')], default=1, verbose_name='status')),
('activate_date', models.DateTimeField(blank=True, help_text='keep empty for an immediate activation', null=True)),
('deactivate_date', models.DateTimeField(blank=True, help_text='keep empty for indefinite activation', null=True)),
('email', models.EmailField(max_length=254, verbose_name='Email')),
],
options={
'verbose_name_plural': 'Contacts',
},
),
]

View File

@ -1,3 +1,17 @@
from django.db import models
from utils.model_abstracts import Model
from django_extensions.db.models import (
TimeStampedModel,
ActivatorModel,
TitleDescriptionModel
)
# Create your models here.
class Contact(TimeStampedModel, ActivatorModel, TitleDescriptionModel, Model):
class Meta:
verbose_name_plural = "Contacts"
email = models.EmailField(verbose_name="Email")
def __str__(self):
return f'{self.title}'

View File

@ -0,0 +1,17 @@
from . import models
from rest_framework import serializers
from rest_framework.fields import CharField, EmailField
class ContactSerializer(serializers.ModelSerializer):
name = CharField(source="title", required=True)
message = CharField(source="description", required=True)
email = EmailField(required=True)
class Meta:
model = models.Contact
fields = (
'name',
'email',
'message'
)

View File

@ -1,3 +1,34 @@
from django.shortcuts import render
from json import JSONDecodeError
from django.http import JsonResponse
from .serializers import ContactSerializer
from rest_framework.parsers import JSONParser
from rest_framework import views, status
from rest_framework.response import Response
# Create your views here.
class ContactAPIView(views.APIView):
"""
Simple APIView para creación de entradas de contacto.
"""
serializer_class = ContactSerializer
def get_serializer_context(self):
return { 'request': self.request,
'format': self.format_kwarg,
'view': self }
def get_serializer(self, *args, **kwargs):
kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs)
def post(self, request):
try:
data = JSONParser().parse(request)
serializer = ContactSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except JSONDecodeError:
return JsonResponse({"resutl": "error", "message": "Json decoding error"},
status=400)

View File

@ -1,6 +1,7 @@
from django.contrib import admin
from django.urls import path
from rest_framework import routers
from core import views as core_views
router = routers.DefaultRouter()
@ -8,4 +9,5 @@ urlpatterns = router.urls
urlpatterns += [
path('admin/', admin.site.urls),
path('contact/', core_views.ContactAPIView.as_view()),
]

View File

View File

@ -0,0 +1,8 @@
import uuid
from django.db import models
class Model(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
class Meta:
abstract = True

View File

@ -7,6 +7,7 @@ djangorestframework==3.14.0
djangorestframework-jsonapi==6.0.0
inflection==0.5.1
Markdown==3.4.3
mysqlclient==2.1.1
Pygments==2.14.0
python-dotenv==1.0.0
pytz==2023.2