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:
parent
a36f93289f
commit
ca2156ef37
119
README.md
119
README.md
@ -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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -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')
|
||||
|
33
backend/core/migrations/0001_initial.py
Normal file
33
backend/core/migrations/0001_initial.py
Normal 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',
|
||||
},
|
||||
),
|
||||
]
|
@ -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}'
|
||||
|
17
backend/core/serializers.py
Normal file
17
backend/core/serializers.py
Normal 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'
|
||||
)
|
@ -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)
|
||||
|
@ -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()),
|
||||
]
|
||||
|
0
backend/utils/__init__.py
Normal file
0
backend/utils/__init__.py
Normal file
8
backend/utils/model_abstracts.py
Normal file
8
backend/utils/model_abstracts.py
Normal 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
|
@ -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
|
Loading…
Reference in New Issue
Block a user