Relaciones de serializador – Django REST Framework

La serialización es uno de los conceptos más importantes en los servicios web RESTful. Facilita la conversión de datos complejos (como instancias de modelos) a tipos de datos nativos de Python que se pueden representar mediante JSON, XML u otros tipos de contenido. En Django REST Framework, tenemos diferentes tipos de serializadores para serializar instancias de objetos, y los serializadores tienen diferentes relaciones de serializador para representar relaciones de modelos. En esta sección, discutiremos las diferentes relaciones de serializador proporcionadas por Django REST Framework Serializers. 

Tabla de contenido

Empezando

Antes de trabajar en los serializadores Django REST Framework, debe asegurarse de que ya instaló Django y Django REST Framework en su entorno virtual. Puedes consultar los siguientes tutoriales:

A continuación, puede crear un proyecto llamado emt (herramienta de administración de empleados) y una aplicación llamada empleados. Consulte los siguientes artículos para comprobar cómo crear un proyecto y una aplicación en Django.

En esta sección, utilizaremos PostgreSQL. Tienes que crear una base de datos llamada emt en PostgreSQL. Puede consultar el siguiente enlace para la instalación.

Instalar PostgreSQL en Windows

Nota: si necesita trabajar con SQLite, puede continuar usando la base de datos predeterminada.

Para hacer uso de PostgreSQL, necesitamos instalar un adaptador de base de datos Python-PostgreSQL (psycopg2). Este paquete ayuda a Django a interactuar con una base de datos PostgreSQL. Puede usar el siguiente comando para instalar el paquete psycopg2. Asegúrese de que la carpeta bin de PostgreSQL esté incluida en las variables ambientales PATH y que el entorno virtual esté activado antes de ejecutar el comando.

pip instalar psycopg2

Ahora, necesita configurar la base de datos PostgreSQL en su proyecto Django. De forma predeterminada, la configuración de la base de datos tiene un motor de base de datos SQLite y un nombre de archivo de base de datos. Puede verificar el archivo de Python setting.py y reemplazar la configuración de la base de datos predeterminada con la configuración de la base de datos PostgreSQL.

DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.postgresql',
       'NAME': 'emt',
       'USER': 'username',
       'PASSWORD': 'password',
       'HOST': '127.0.0.1',
       'PORT': '5432',
   }
}

Aquí, el nombre se refiere al nombre de la base de datos, el usuario y la contraseña se refieren al nombre de usuario y la contraseña de Postgres.

Por último, necesitamos instalar el paquete httpie en nuestro entorno virtual. Estaremos componiendo comandos HTTPie para la operación CRUD. Puede activar el entorno virtual y ejecutar el siguiente comando

instalación de pip –actualización de httpie

Creación de modelos y vistas de Django

Crear modelos de Django

En Django, los modelos son clases que tratan con bases de datos de forma orientada a objetos. Cada clase de modelo hace referencia a una tabla de base de datos y cada atributo de la clase de modelo hace referencia a una columna de base de datos. Aquí, crearemos un modelo Employee y un modelo EmployeeTask en Django. El modelo EmployeeTask tiene una relación ManyToOne con el modelo Employee. Requerimos los siguientes atributos para nuestra entidad Employee:

  • emp_id
  • nombre
  • género
  • designacion

Los atributos de nuestro modelo EmployeeTask son los siguientes:

  • nombre de la tarea
  • empleado (clave externa – Empleado)
  • task_desc
  • Fecha de creación
  • plazo

Entremos en la implementación del modelo Employee en Django. Puede reemplazar el archivo Python models.py con el siguiente código:

Python3

GENDER_CHOICES = (('M','Male'),
                      ('F','Female'),)
  
class Employee(models.Model):
    emp_id = models.IntegerField()
    name = models.CharField(max_length=150)
    gender = models.CharField(max_length=1,
                              choices=GENDER_CHOICES,
                              default='M')
    designation = models.CharField(max_length=150)
  
    class Meta:
        ordering=('emp_id',)
  
    def __str__(self):
        return self.name

El modelo Employee es una subclase de la clase django.db.models.Model y define los atributos y una clase interna Meta. Tiene un atributo de ordenación que ordena el resultado en orden ascendente según la identificación del empleado.

A continuación, entremos en la implementación del modelo EmployeeTask. Puede agregar el siguiente código al archivo models.py.

Python3

class EmployeeTask(models.Model):
    task_name = models.CharField(max_length=150)
    employee = models.ForeignKey(Employee,
                                  related_name='tasks',
                                  on_delete=models.CASCADE)
    task_desc = models.CharField(max_length=350)
    created_date = models.DateTimeField(auto_now_add=True)
    deadline = models.DateTimeField()
  
    class Meta:
        ordering = ('task_name',)
  
    def __str__(self):
        return self.task_name

El modelo EmployeeTask es una subclase de la clase django.db.models.Model y define los atributos y una clase interna Meta. Tiene un atributo de ordenación que ordena el resultado en orden ascendente según nombre_tarea. Tiene un campo de empleado que tiene una relación de muchos a uno con el modelo Empleado.

employee = models.ForeignKey(Employee,
                                  related_name='tasks',
                                  on_delete=models.CASCADE)

El related_name ayuda en una relación inversa. Relación inversa significa referirse de Empleado a TareaEmpleado. El campo de empleado representa el modelo de empleado en EmployeeTaks. Del mismo modo, related_name representa EmployeeTask en el modelo Employee.

La clase de serializador no incluye automáticamente las relaciones inversas. Debemos agregarlo explícitamente a la lista de campos. Si establece un argumento de nombre relacionado apropiado en la relación, puede usarlo como un nombre de campo (podrá comprender la relación inversa mientras serializa los modelos). 

Hagamos las migraciones iniciales usando el siguiente comando.

python manage.py hacer migraciones

Si la migración es exitosa, aplique todas las migraciones generadas usando el siguiente comando:

python manage.py migrar

Si tiene éxito, puede verificar las entradas de su base de datos. Ahora, vamos a crear las vistas de Django.

Creación de vistas

Las vistas de Django facilitan el procesamiento de requests HTTP y proporcionan respuestas HTTP. Al recibir una solicitud HTTP, Django crea una instancia de HttpRequest y se pasa como primer argumento a la función de vista. La función de visualización comprueba el valor y ejecuta el código en función del verbo HTTP. Aquí, el código usa el decorador @csrf_exempt para configurar una cookie CSRF (falsificación de solicitud entre sitios). Esto hace posible publicar en esta vista desde clientes que no tendrán un token CSRF.

Puede agregar el siguiente código a su archivo view.py. 

Python3

from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
  
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
  
from employees.models import Employee, EmployeeTask
from employees.serializers import EmployeeSerializer, EmployeeTaskSerializer
  
  
@csrf_exempt
def employee_list(request):
    if request.method == 'GET':
        emp = Employee.objects.all()
        emp_serializer = EmployeeSerializer(emp, many=True)
        return JsonResponse(emp_serializer.data, safe=False)
  
    elif request.method == 'POST':
        emp_data = JSONParser().parse(request)
        emp_serializer = EmployeeSerializer(data=emp_data)
          
        if emp_serializer.is_valid():
            emp_serializer.save()
            return JsonResponse(emp_serializer.data,
                                status=201)
        return JsonResponse(emp_serializer.errors,
                            status=400)
  
@csrf_exempt
def employee_detail(request, pk):
    try:
        emp = Employee.objects.get(pk=pk)
    except Employee.DoesNotExist:
        return HttpResponse(status=404)
  
    if request.method == 'GET':
        emp_serializer = EmployeeSerializer(emp)
        return JsonResponse(emp_serializer.data)
    elif request.method == 'DELETE':
        emp.delete()
        return HttpResponse(status=204)
  
  
@csrf_exempt
def employeetask_list(request):
    if request.method == 'GET':
        emptask = EmployeeTask.objects.all()
        emptask_serializer = EmployeeTaskSerializer(emptask, many=True)
        return JsonResponse(emptask_serializer.data, safe=False)
    elif request.method == 'POST':
        emptask_data = JSONParser().parse(request)
        emptask_serializer = EmployeeTaskSerializer(data=emptask_data)
        if emptask_serializer.is_valid():
            emptask_serializer.save()
            return JsonResponse(emptask_serializer.data,
                                status=201)
        return JsonResponse(emptask_serializer.errors,
                            status=400)
  
@csrf_exempt
def employeetask_detail(request, pk):
    try:
        emptask = EmployeeTask.objects.get(pk=pk)
    except EmployeeTask.DoesNotExist:
        return HTTPResponse(status=404)
  
    if request.method == 'GET':
        emptask_serializer = EmployeeTaskSerializer(emptask)
        return JsonResponse(emptask_serializer.data)
      
    elif request.method == 'DELETE':
        emptask.delete()
        return HttpResponse(status=204)

Aquí, tenemos diferentes funciones para procesar requests relacionadas con el modelo Employee y el modelo EmployeeTask: employee_list, employeetask_list, y employee_detail y employeetask_detail. Las funciones employee_list y employeetask_list son capaces de procesar requests para recuperar todos los empleados y tareas de empleados o para crear un nuevo empleado y crear una nueva tarea de empleado. Las funciones employee_detail y employeetask_detail son capaces de procesar requests como recuperar una entrada en particular y eliminar una entrada.

Establecimiento de la configuración de URL

Ahora, es necesario enrutar las URL para ver. Debe crear un nuevo nombre de archivo de Python urls.py en la carpeta de la aplicación (empleados) y agregar el siguiente código.

Python3

from django.urls import path
from employees import views
  
urlpatterns = [
    path('employees/',
        views.employee_list,
        name = 'employee-list'),
    path('employees/<int:pk>/',
        views.employee_detail,
        name='employee-detail'),
    path('task/',
        views.employeetask_list,
        name = 'employeetask-list'),
    path('task/<int:pk>/',
        views.employeetask_detail,
        name='employeetask-detail'),

En función de la ruta coincidente, las direcciones URL se enrutan a las vistas correspondientes. A continuación, debemos reemplazar el código en el archivo urls.py en la carpeta raíz (emt\emt\urls.py). En la actualidad, tiene las configuraciones de URL raíz. Actualice el archivo urls.py con el siguiente código.

Python3

from django.urls import path, include
  
urlpatterns = [
    path('', include('employees.urls')),
]

Campo relacionado con la clave principal

PrimaryKeyRelatedField representa el objetivo de la relación utilizando la clave principal (pk). Se puede lograr generando la relación usando el campo De forma predeterminada, este campo es de lectura y escritura, pero puede convertirlo en solo lectura configurando el atributo read_only en True. El PrimaryKeyRelatedField tiene los siguientes argumentos:

  • queryset: se usa para búsquedas de instancias de modelos al validar la entrada del campo. Las relaciones deben establecer explícitamente un conjunto de consultas ( ) o establecer read_only=True.
  • many: establezca este argumento en True para serializar más de una relación
  • allow_null: si se establece en Verdadero, el campo aceptará valores de Ninguno o la string vacía para relaciones anulables. El valor predeterminado es falso.
  • pk_field: establezca en un campo para controlar la serialización/deserialización del valor de la clave principal. Por ejemplo, pk_field=UUIDField(format=’hex’) serializaría una clave principal UUID en su representación hexadecimal compacta.

Ahora vayamos a nuestro código serializador. Aquí tenemos dos clases de serializador: EmployeeSerializer y EmployeeTaskSerializer. La clase EmployeeSerializer serializa el modelo Employee y EmployeeTaskSerializer serializa el modelo EmployeeTask. El modelo EmployeeTask mantiene una relación ManyToOne con el modelo Employee. 

employee = models.ForeignKey(Employee,
                                  related_name='tasks',
                                  on_delete=models.CASCADE)

No se asignará la misma tarea a más de un empleado, pero un empleado puede tener varias tareas. Por lo tanto, la clase EmployeeTaskSerializer debe serializar solo una única instancia de empleado, mientras que la clase EmployeeSerializer debe serializar una o más instancias de EmployeeTask (se puede asignar más de una tarea a un empleado).

El proceso del generador de relaciones en EmployeeTaskSerializer de la siguiente manera:

employee = serializers.PrimaryKeyRelatedField(queryset=Employee.objects.all(),
                                                  many=False) 

De forma predeterminada, el campo PrimaryKeyRelatedField tiene permiso de lectura y escritura. El argumento busca la instancia de modelo particular en la tabla Empleado. El argumento se establece porque solo hay una única relación para serializar.

El proceso del generador de relaciones en la clase EmployeeSerializer de la siguiente manera:

tasks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

El atributo de tareas es el nombre_relacionado especificado (relación de clave externa) en el modelo EmployeeTask. Dado que cada empleado puede tener más de una tarea, configuramos read_only=True solo otorga permiso para recuperar la tarea del empleado. 

Puede agregar el siguiente código al archivo serializers.py. (si no tiene el archivo, puede crear un archivo llamado serializers.py en la carpeta [empleados] de su aplicación).

Python3

from rest_framework import serializers
from employees.models import Employee, EmployeeTask
  
class EmployeeSerializer(serializers.ModelSerializer):
    # PrimaryKeyRelatedField
    tasks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
  
    class Meta:
        model = Employee
        fields = (
            'pk',
            'emp_id',
            'name',
            'gender',
            'designation',
            'tasks')
  
  
class EmployeeTaskSerializer(serializers.ModelSerializer):
    # PrimaryKeyRelatedField
    employee = serializers.PrimaryKeyRelatedField(queryset=Employee.objects.all(),
                                                  many=False)     
  
    class Meta:
        model = EmployeeTask
        fields = (
            'pk',
            'task_name',
            'employee',
            'task_desc',
            'created_date',
            'deadline')

Completemos la tabla de empleados. Puede ejecutar el siguiente comando HTTPie.

http POST :8000/employees/ emp_id=128 nombre=”Mathew A” género=”M” designación=”Ingeniero de Software”

Producción

HTTP/1.1 201 Created
Content-Length: 140
Content-Type: application/json
Date: Thu, 21 Jan 2021 09:16:50 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "designation": "Software Engineer",
    "emp_id": 128,
    "gender": "M",
    "name": "Mathew A",
    "pk": 8,
    "tasks": []
}

Puede notar el valor pk del empleado. Ahora, creemos una tarea de empleado y asignémosla al empleado usando PrimaryKeyRelatedField. Aquí, el valor de la clave principal del empleado llamado Mathew A es 8. Debe pasarlo al nombre de campo empleado. Ejecute el siguiente comando HTTPie.

http :8000/task/ task_name=”Intercambiar el primer y último elemento de una lista” employee=8 task_desc=”Escribir un programa de Python para intercambiar el primer y último elemento de una lista” fecha límite=”2021-01-25 00:00: 00.000000+00:00″ 

Producción

HTTP/1.1 201 Created
Content-Length: 285
Content-Type: application/json
Date: Thu, 21 Jan 2021 09:42:39 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "created_date": "2021-01-21T09:42:39.792788Z",
    "deadline": "2021-01-25T00:00:00Z",
    "employee": 8,
    "task_desc": "Write a Python program to interchange first and last element in a list",
    "task_name": "Interchange first and last elements in a list"
}

Puede crear una tarea más y asignarla al mismo empleado y procesar la solicitud para recuperar los detalles del empleado. El comando HTTPie es 

http :8000/empleados/

Producción

HTTP/1.1 200 OK
Content-Length: 219
Content-Type: application/json
Date: Fri, 22 Jan 2021 03:58:01 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "designation": "Software Engineer",
        "emp_id": 128,
        "gender": "M",
        "name": "Mathew A",
        "pk": 8,
        "tasks": [
            2,
            1
        ]
    },
    {
        "designation": "Test Engineer",
        "emp_id": 129,
        "gender": "F",
        "name": "Jeena R",
        "pk": 9,
        "tasks": []
    }
]

Compartiendo la captura de pantalla del símbolo del sistema para su referencia

Puede observar el campo de tareas para el empleado llamado Mathew A. Muestra el pk del modelo de tarea y el argumento identificador de instancia de tarea múltiple serializado.

Recuperemos los detalles de la tarea usando el siguiente comando HTTPie

http :8000/tarea/

Producción

HTTP/1.1 200 OK
Content-Length: 454
Content-Type: application/json
Date: Fri, 22 Jan 2021 03:59:40 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "created_date": "2021-01-21T09:48:47.710707Z",
        "deadline": "2021-01-27T00:00:00Z",
        "employee": 8,
        "pk": 2,
        "task_desc": "Write a Python program for Binary Search",
        "task_name": "Binary Search"
    },
    {
        "created_date": "2021-01-21T09:42:39.792788Z",
        "deadline": "2021-01-25T00:00:00Z",
        "employee": 8,
        "pk": 1,
        "task_desc": "Write a Python program to interchange first and last element in a list",
        "task_name": "Interchange first and last elements in a list"
    }
]

Compartiendo la captura de pantalla del símbolo del sistema para su referencia

 Veamos el comando HTTPie para crear una nueva tarea.

http :8000/task/ task_name=”PrimaryKeyRelatedField” employee=8 task_desc=”Serializar relación usando PrimaryKeyRelateField” fecha límite=”2021-01-27 00:00:00.000000+00:00″

Producción

HTTP/1.1 201 Created
Content-Length: 213
Content-Type: application/json
Date: Fri, 22 Jan 2021 04:33:15 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "created_date": "2021-01-22T04:33:15.855264Z",
    "deadline": "2021-01-27T00:00:00Z",
    "employee": 8,
    "pk": 6,
    "task_desc": "Serialize relationship using PrimaryKeyRelateField",
    "task_name": "PrimaryKeyRelatedField"
}

Compartiendo la captura de pantalla del símbolo del sistema para su referencia:

 Ahora exploremos cómo StringRelatedField representa una relación. 

CampoRelacionadoDeString

StringRelatedField representa el objetivo de la relación utilizando su método __str__. Este campo es de solo lectura y establece el argumento ‘muchos’ en verdadero si hay más de una instancia para serializar. Hagamos uso de StringRelatedField para tareas archivadas en la clase EmployeeSerializer. El proceso del generador de relaciones es el siguiente:

tasks = serializers.StringRelatedField(many=True)

La clase EmployeeSerializer de la siguiente manera:

Python3

class EmployeeSerializer(serializers.ModelSerializer):    
    # StringRelatedField
    tasks = serializers.StringRelatedField(many=True)
  
    class Meta:
        model = Employee
        fields = (
            'pk',
            'emp_id',
            'name',
            'gender',
            'designation',
            'tasks')

Recuperemos los detalles del empleado para comprender cómo StringRelatedField muestra los valores del campo de relación. El comando HTTPie para recuperar los valores de los empleados es:

http :8000/empleados/

Producción

HTTP/1.1 200 OK
Content-Length: 279
Content-Type: application/json
Date: Fri, 22 Jan 2021 04:04:08 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "designation": "Software Engineer",
        "emp_id": 128,
        "gender": "M",
        "name": "Mathew A",
        "pk": 8,
        "tasks": [
            "Binary Search",
            "Interchange first and last elements in a list"
        ]
    },
    {
        "designation": "Test Engineer",
        "emp_id": 129,
        "gender": "F",
        "name": "Jeena R",
        "pk": 9,
        "tasks": []
    }
]

Compartiendo la captura de pantalla del símbolo del sistema para su referencia

Aquí puede notar que el campo de tareas muestra el valor de string de la función en el modelo EmployeeTask.

    def __str__(self):
        return self.task_name

SlugRelatedField

SlugRelatedField representa el destino de la relación mediante un campo en el destino. De forma predeterminada, el campo permite la operación de lectura y escritura. Para operaciones de escritura, el campo slug que representa un campo de modelo debe ser único. SlugRelatedField tiene los siguientes argumentos:  

  • slug_field : un campo en el objetivo y debe identificar de forma única cualquier instancia dada.
  • queryset : facilita las búsquedas de instancias de modelos al validar la entrada de campo. Las relaciones deben establecer explícitamente un conjunto de consultas o establecer read_only=True.
  • many : establezca este argumento en True para serializar más de una relación
  • allow_null : si se establece en Verdadero, el campo aceptará valores de Ninguno o la string vacía para relaciones anulables. El valor predeterminado es falso.

En la clase EmployeeTaskSerializer, puede reemplazar la representación del campo del empleado con el siguiente código:

employee = serializers.SlugRelatedField(
        queryset=Employee.objects.all(),
        slug_field='name')

El código EmployeeTaskSerializer de la siguiente manera:

Python3

class EmployeeTaskSerializer(serializers.ModelSerializer):
    # SlugRelatedField 
    employee = serializers.SlugRelatedField(
        queryset=Employee.objects.all(),
        slug_field='name')
          
  
    class Meta:
        model = EmployeeTask
        fields = (
            'pk',
            'task_name',
            'employee',
            'task_desc',
            'created_date',
            'deadline')

El comando HTTPie para crear una nueva tarea de empleado es

http :8000/task/ task_name=”SlugRelatedField” employee=”Mathew A” task_desc=”Serializar relación usando SlugRelateField” fecha límite=”2021-01-27 00:00:00.000000+00:00″

Producción

HTTP/1.1 201 Created
Content-Length: 210
Content-Type: application/json
Date: Fri, 22 Jan 2021 04:41:12 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "created_date": "2021-01-22T04:41:12.424001Z",
    "deadline": "2021-01-27T00:00:00Z",
    "employee": "Mathew A",
    "pk": 7,
    "task_desc": "Serialize relationship using SlugRelateField",
    "task_name": "SlugRelatedField"
}

Compartiendo la captura de pantalla del símbolo del sistema para su referencia

Aquí, al crear una nueva tarea, le hemos dado el nombre del empleado en el campo del empleado en lugar de la clave principal. También puede mencionar la identificación del empleado mientras representa a SlugRelatedField. Debe asegurarse de que satisfaga la restricción única.   

HyperlinkedIndetityField

Para hipervincular la API, la clase de serializador debe extender HyperlinkedModelSerializer. La clase HyperlinkedModelSerializer utiliza hipervínculos para representar relaciones, en lugar de claves principales. De forma predeterminada, la clase de serializador que se hereda de la clase HyperlinkedModelSerializer incluirá un campo de dirección URL en lugar de un campo de clave principal. El campo URL se representará mediante un campo serializador HyperlinkedIdentityField. También se puede utilizar para un atributo en el objeto.

HyperlinkedIdentityField siempre es de solo lectura. Los argumentos de HyperlinkedIdentitField son:

  • view_name : el nombre de la vista se utiliza como destino de la relación. En las clases de enrutadores estándar, será una string con el formato <nombre_del_modelo>-detalle.
  • lookup_field : el campo en el destino, que se utiliza para la búsqueda. Corresponde a un argumento de palabra clave de URL en la vista a la que se hace referencia. El valor predeterminado es ‘pk’.
  • lookup_url_kwarg : el nombre del argumento de palabra clave definido en la URL conf que corresponde al campo de búsqueda. El valor predeterminado es usar el mismo valor que lookup_field.
  • formato : si se usan sufijos de formato, los campos con hipervínculos usarán el mismo sufijo de formato para el destino, a menos que se anule con el argumento de formato.

En la clase EmployeeTaskSerializer, puede representar el campo de URL como se muestra a continuación:

url = serializers.HyperlinkedIdentityField(
        view_name='employeetask-detail',
        lookup_field='pk'
    )

Nota: De forma predeterminada, la clase de serializador que se hereda de la clase HyperlinkedModelSerializer incluirá un campo de URL y el nombre de la vista será una string con el formato <model_name>-detail.

La clase EmployeeTaskSerializer de la siguiente manera:

Python3

class EmployeeTaskSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='employeetask-detail',
        lookup_field='pk'
    )
    employee = serializers.SlugRelatedField(
        queryset=Employee.objects.all(), slug_field='name')
  
  
    class Meta:
        model = EmployeeTask
        fields = '__all__'

Al crear una instancia de HyperlinkedModelSerializer, es imprescindible incluir la solicitud actual en el contexto del serializador. Debe pasar context={‘request’:request} como argumento al crear una instancia de su EmployeeTaskSerializer. Por ejemplo:

emp_serializer = EmployeeSerializer(emp,
                                    many=True,
                                    context={'request':request})

Asegúrese de editar su función employee_list y employee_detail en el archivo views.py con el código anterior. Debe agregar el argumento context={‘request’:request} al instanciar EmployeeTaskSerializer (GET, POST, PUT y PATCH).

Nota: Nota: este contexto, junto con el campo de búsqueda, se utiliza para generar una URL completa mediante HyperlinkedIdentityField.

Recuperemos los valores de EmployeeTask para comprender cómo se muestran los valores. El comando HTTPie es 

http OBTENER :8000/tarea/1/

Producción

HTTP/1.1 200 OK
Content-Length: 296
Content-Type: application/json
Date: Fri, 22 Jan 2021 16:16:42 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "created_date": "2021-01-21T09:42:39.792788Z",
    "deadline": "2021-01-25T00:00:00Z",
    "employee": "Mathew A",
    "task_desc": "Write a Python program to interchange first and last element in a list",
    "task_name": "Interchange first and last elements in a list",
    "url": "http://localhost:8000/task/1/"
}

Compartiendo la captura de pantalla del símbolo del sistema para su referencia

Si analizamos la salida, podemos notar que en lugar de la clave principal muestra el campo url. 

"url": "http://localhost:8000/task/1/"

Campo relacionado hipervinculado

HyperlinkedRelatedField se utiliza para representar el objetivo de la relación mediante un hipervínculo. De forma predeterminada, este campo es de lectura y escritura, pero podemos hacerlo de solo lectura configurando el indicador de solo lectura como True. Los argumentos de HyperlinkedRelatedField son los siguientes:

  • view_name : el nombre de la vista se utiliza como destino de la relación. Para las clases de enrutadores estándar, será una string con el formato <nombre del modelo>-detalle.
  • queryset : facilita las búsquedas de instancias de modelos al validar la entrada de campo. Las relaciones deben establecer explícitamente un conjunto de consultas o establecer read_only=True.
  • many : para serializar más de una relación, debe establecer este argumento en True.
  • allow_null : si se establece en Verdadero, el campo aceptará valores Ninguno o la string vacía para relaciones anulables. El valor predeterminado es falso.
  • lookup_field : el campo en el destino se usa para la búsqueda. Corresponde a un argumento de palabra clave de URL en la vista a la que se hace referencia. El valor predeterminado es ‘pk’.
  • lookup_url_kwarg : el nombre del argumento de palabra clave definido en la URL conf que corresponde al campo de búsqueda. De forma predeterminada, utiliza el mismo valor que lookup_field.
  • formato : si se usan sufijos de formato, los campos con hipervínculos usarán el mismo sufijo de formato para el destino, a menos que se anule con el argumento de formato.

En nuestra relación inversa (Employee to EmployeeTask) hemos visto que, de forma predeterminada, el serializador (EmployeeSerializer) usa la clave principal para representar el objetivo de una relación (para incluir una relación inversa, debe agregarla explícitamente a la lista de campos). También hemos explorado otras representaciones como StringRelatedField y SlugRelatedField. Aquí, haremos uso de HyperlinkedRelatedField.

En la clase EmployeeSerializer, puede representar el campo de tareas como se muestra a continuación:

tasks = serializers.HyperlinkedRelatedField(many=True,
                                            read_only=True,
                                            view_name='employeetask-detail')

Puede reemplazar la clase EmployeeSerializer existente con la siguiente:

Python3

class EmployeeSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='employee-detail',
        lookup_field='pk'
    )
    tasks = serializers.HyperlinkedRelatedField(many=True,
                                                read_only=True,
                                                view_name='employeetask-detail')
          
      
    class Meta:
        model = Employee
        fields = (
            'url',
            'pk',
            'emp_id',
            'name',
            'gender',
            'designation',
            'tasks')

El comando HTTPie es:

http OBTENER :8000/empleados/8/

Producción

HTTP/1.1 200 OK
Content-Length: 283
Content-Type: application/json
Date: Fri, 22 Jan 2021 16:18:59 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "designation": "Software Engineer",
    "emp_id": 128,
    "gender": "M",
    "name": "Mathew A",
    "pk": 8,
    "tasks": [
        "http://localhost:8000/task/2/",
        "http://localhost:8000/task/1/",
        "http://localhost:8000/task/6/",
        "http://localhost:8000/task/7/"
    ],
    "url": "http://localhost:8000/employees/8/"
}

Compartiendo la captura de pantalla del símbolo del sistema para su referencia

Si analizamos la salida, podemos notar que el campo de tareas muestra un conjunto o hipervínculos.

"tasks": [
        "http://localhost:8000/task/2/",
        "http://localhost:8000/task/1/",
        "http://localhost:8000/task/6/",
        "http://localhost:8000/task/7/"
    ]

A continuación, comprendamos cómo se puede expresar la relación anidada mediante el uso de serializadores como campos. 

Relación anidada

Una relación anidada hace posible incrustar la entidad referida. Si nos fijamos en el caso de EmployeeTask, se refiere al Empleado. Vamos a serializar EmployeeTask usando una relación anidada.

Puede usar el siguiente código para hacer referencia al campo de empleado en EmployeeTaskSerializer:

employee = EmployeeSerializer(read_only=True)

Puede reemplazar el EmployeeTaskSerializer con el siguiente código:

Python3

class EmployeeTaskSerializer(serializers.HyperlinkedModelSerializer):
    url = serializers.HyperlinkedIdentityField(
        view_name='employeetask-detail',
        lookup_field='pk'
    )
    employee = EmployeeSerializer(read_only=True)
  
    class Meta:
        model = EmployeeTask
        fields = '__all__'

Recuperemos una entrada de EmployeeTask. El comando HTTPie es

http OBTENER :8000/tarea/1/

Producción

HTTP/1.1 200 OK
Content-Length: 569
Content-Type: application/json
Date: Fri, 22 Jan 2021 16:33:51 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "created_date": "2021-01-21T09:42:39.792788Z",
    "deadline": "2021-01-25T00:00:00Z",
    "employee": {
        "designation": "Software Engineer",
        "emp_id": 128,
        "gender": "M",
        "name": "Mathew A",
        "pk": 8,
        "tasks": [
            "http://localhost:8000/task/2/",
            "http://localhost:8000/task/1/",
            "http://localhost:8000/task/6/",
            "http://localhost:8000/task/7/"
        ],
        "url": "http://localhost:8000/employees/8/"
    },
    "task_desc": "Write a Python program to interchange first and last element in a list",
    "task_name": "Interchange first and last elements in a list",
    "url": "http://localhost:8000/task/1/"
}

Compartiendo la captura de pantalla del símbolo del sistema para su referencia

Puede notar que el campo de empleado muestra los detalles completos del empleado en lugar de solo la clave principal o el slug.

"employee": {
        "designation": "Software Engineer",
        "emp_id": 128,
        "gender": "M",
        "name": "Mathew A",
        "pk": 8,
        "tasks": [
            "http://localhost:8000/task/2/",
            "http://localhost:8000/task/1/",
            "http://localhost:8000/task/6/",
            "http://localhost:8000/task/7/"
        ],
        "url": "http://localhost:8000/employees/8/"
    },

En esta sección, exploramos varios tipos de campos relacionales proporcionados por las relaciones del serializador DRF. Entendimos cómo cada campo relacional representa la relación con el objetivo. PrimaryKeyRelatedField representa el objetivo de la relación usando su clave principal, mientras que StringRelatedField usa el método __str__ del objetivo. En el caso de SlugRelatedField, representa el destino de la relación mediante un campo de destino. Finalmente, HyperlinkedIdentityField se usa como una relación de identidad, y HyperlinkedRealtedField representa el destino de la relación usando un hipervínculo. 

Publicación traducida automáticamente

Artículo escrito por SonuGeorge y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *