序列器关系 – Django REST框架

序列器关系 – Django REST框架

序列化是RESTful Webservices中最重要的概念之一。它有利于将复杂的数据(如模型实例)转换为可以使用JSON、XML或其他内容类型呈现的本地Python数据类型。在Django REST框架中,我们有不同类型的序列化器来序列化对象实例,而序列化器有不同的序列化关系来表示模型关系。在本节中,我们将讨论Django REST框架序列化器所提供的不同序列化关系。

目录

  • Getting Started
  • 创建Django模型和视图
  • PrimaryKeyRelatedField
  • StringRelatedField
  • SlugRelatedField
  • HyperlinkedIndetityField
  • HyperlinkedRelatedField
  • Nested Relationship

开始

在进行Django REST框架序列化工作之前,你应该确保你已经在虚拟环境中安装了Django和Django REST框架。你可以查看下面的教程。

  • 使用venv创建虚拟环境 | Python
  • Django Installation
  • Django REST框架安装

接下来,你可以创建一个名为emt(雇员管理工具)的项目和一个名为employees的应用程序。

在这一部分,我们将使用PostgreSQL。你必须在PostgreSQL中创建一个名为emt的数据库。你可以查看下面的链接进行安装。

在Windows上安装PostgreSQL

Note: if you need to work with SQLite, you can continue using the default database.

为了使用PostgreSQL,我们需要安装一个Python-PostgreSQL数据库适配器(psycopg2)。这个软件包可以帮助Django与PostgreSQL数据库进行交互。你可以使用下面的命令来安装psycopg2软件包。在执行该命令之前,请确保PATH环境变量中包含PostgreSQL bin文件夹,并且虚拟环境已经激活。

pip install psycopg2

现在,你需要在你的Django项目中配置PostgreSQL数据库。默认情况下,数据库配置有一个SQLite数据库引擎和数据库文件名。你可以检查setting.py Python文件,用PostgreSQL数据库配置替换默认的数据库配置。

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

这里,name指的是数据库名称,user和password指的是Postgres的用户名和密码。

最后,我们需要在我们的虚拟环境中安装httpie包。我们将组成HTTPie命令进行CRUD操作。你可以激活虚拟环境并运行以下命令

pip install –upgrade httpie

创建Django模型和视图

创建Django模型

在Django中,模型是以面向对象的方式处理数据库的类。每个模型类指的是一个数据库表,模型类中的每个属性指的是一个数据库列。这里,我们将在Django中创建一个Employee模型和EmployeeTask模型。EmployeeTask模型与Employee模型保持着ManyToOne的关系。 我们的Employee实体需要以下属性。

  • emp_id
  • name
  • gender
  • designation

我们的EmployeeTask模型的属性如下。

  • task_name
  • employee (foreign key – Employee)
  • task_desc
  • created_date
  • deadline

让我们进入Django中雇员模型的实现。你可以用下面的代码替换models.py Python文件。

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

Employee模型是django.db.models.Model类的一个子类,定义了属性和一个Meta内类。它有一个排序属性,根据雇员ID以升序排列结果。

接下来,让我们进入EmployeeTask模型的实现。你可以在models.py文件中加入以下代码。

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

EmployeeTask模型是django.db.models.Model类的一个子类,定义了属性和一个Meta内类。它有一个排序属性,根据task_name将结果以升序排列。它有一个雇员字段,与雇员模型保持多对一的关系。

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

related_name有助于建立一种反向关系。反向关系意味着从Employee到EmployeeTask。employee字段代表EmployeeTaks中的Employee模型。同样地,related_name代表Employee模型中的EmployeeTask。

反向关系不会被序列化器类自动包含。我们必须明确地将其添加到字段列表中。如果你在关系上设置一个适当的 related_name 参数,那么你可以把它作为一个字段名(你将能够在序列化模型的时候理解反向关系)。

让我们使用下面的命令进行初始迁移。

python manage.py makemigrations

如果迁移成功,则使用下面的命令应用所有生成的迁移。

python manage.py migrate

如果成功,你可以检查你的数据库条目。现在,让我们来创建Django视图。

创建视图

Django视图有助于处理HTTP请求和提供HTTP响应。在收到一个HTTP请求时,Django会创建一个HttpRequest实例,并将其作为第一个参数传递给视图函数。视图函数检查该值并根据HTTP动词执行代码。这里的代码使用@csrf_exempt装饰器来设置一个CSRF(跨站请求伪造)cookie。这使得从没有CSRF令牌的客户端向该视图POST成为可能。

你可以在你的view.py文件中加入以下代码。

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)

在这里,我们有不同的函数来处理与Employee模型和EmployeeTask模型相关的请求–employee_list、employeeetask_list,以及employee_detail和employeeetask_detail。employee_list和employeeetask_list函数能够处理检索所有雇员和雇员任务的请求,或者创建一个新雇员和创建一个新的雇员任务。employee_detail和employeetask_detail函数能够处理诸如检索某个特定条目和删除某个条目的请求。

设置URL配置

现在,有必要将URLs路由到视图。你需要在应用程序(雇员)文件夹中创建一个新的Python文件,名为urls.py,并添加以下代码。

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'),

根据匹配的路径,URLs被路由到相应的视图。接下来,我们必须替换根文件夹中urls.py文件的代码(emt\emt\urls.py)。目前,它有根URL的配置。用下面的代码更新urls.py文件。

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

PrimaryKeyRelatedField

PrimaryKeyRelatedField表示使用主键(pk)的关系的目标。它可以通过使用 rest_framework.serializers.PrimaryKeyRelatedField() 字段生成关系来实现。 默认情况下,这个字段是读写的,但是你可以通过设置read_only属性为True来使其成为只读。PrimaryKeyRelatedField有以下参数。

  • queryset – 它在验证字段输入时用于模型实例查找。关系必须明确地设置一个queryset ( serializers.PrimaryKeyRelatedField(queryset=Employee.objects.all(),many=False) ) ,或者设置read_only=True。
  • many – 将此参数设置为True以序列化一个以上的关系
  • allow_null – 如果设置为True,该字段将接受None或空字符串的值,用于可忽略的关系。默认为 “假”。
  • pk_field – 设置为一个字段,控制主键值的序列化/反序列化。例如,pk_field=UUIDField(format=’hex’)将把UUID主键序列化为其紧凑的十六进制表示。

现在让我们来看看我们的序列化器代码。这里我们有两个序列化器类–EmployeeSerializer和EmployeeTaskSerializer。EmployeeSerializer类将Employee模型序列化,EmployeeTaskSerializer将EmployeeTask模型序列化。EmployeeTask模型与Employee模型持有ManyToOne关系。

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

同一任务不会被分配给多个雇员,但一个雇员可以有多个任务。因此,EmployeeTaskSerializer类应该只序列化一个雇员实例,而EmployeeSerializer类应该序列化一个或多个EmployeeTask实例(一个雇员可以被分配多个任务)。

EmployeeTaskSerializer中的关系生成器过程如下。

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

默认情况下,PrimaryKeyRelatedField字段有读写权限。queryset=Employee.objects.all() 参数检查Employee表中的特定模型实例。Many=False参数的设置是因为只有一个单一的关系需要序列化。

EmployeeSerializer类中的关系生成器过程如下。

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

任务属性是EmployeeTask模型中指定的related_name(外键关系)。由于每个雇员可以有一个以上的任务,我们设置many=True。read_only=True只允许检索EmployeeTask的权限。

你可以将下面的代码添加到serializers.py文件中。(如果你没有这个文件,你可以在你的应用程序[雇员]文件夹中创建一个名为serializers.py的文件)。

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')

让我们来填充雇员表。你可以执行下面的HTTPie命令。

http POST :8000/employees/ emp_id=128 name=”Mathew A” gender=”M” designation=”Software Engineer”

输出

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": []
}

你可以注意到该雇员的pk值。现在,让我们创建一个雇员任务,并使用PrimaryKeyRelatedField将其映射到雇员身上。在这里,名为Mathew A的雇员的主键值是8,你必须把它传递给字段名employee。执行下面的HTTPie命令。

http :8000/task/ task_name=”Interchange first and last elements in a list” employee=8 task_desc=”Write a Python program to interchange first and last element in a list” deadline=”2021-01-25 00:00:00.000000+00:00″

输出

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"
}

你可以再创建一个任务,并把它分配给同一个雇员,然后处理请求以检索雇员的详细信息。HTTPie的命令是

http :8000/employees/

输出

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": []
    }
]

分享命令提示的截图供你参考

序列器关系 - Django REST框架

你可以注意到名为Mathew A的雇员的任务字段,它显示任务模型的pk和many=True参数序列化的多个任务实例id。

让我们使用下面的HTTPie命令来检索任务的细节

http :8000/task/

输出

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"
    }
]

分享命令提示的截图供你参考

序列器关系 - Django REST框架

让我们看看HTTPie命令来创建一个新的任务。

http :8000/task/ task_name=”PrimaryKeyRelatedField” employee=8 task_desc=”Serialize relationship using PrimaryKeyRelateField” deadline=”2021-01-27 00:00:00.000000+00:00″

输出

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"
}

分享命令提示的截图供你参考。

序列器关系 - Django REST框架

现在让我们来探讨一下StringRelatedField如何表示一种关系。

StringRelatedField

StringRelatedField使用其str方法表示关系的目标。这个字段是只读的,如果有一个以上的实例要序列化,将’many’参数设置为true。让我们利用StringRelatedField来完成EmployeeSerializer类中存档的任务。关系生成器的过程如下。

tasks = serializers.StringRelatedField(many=True)

EmployeeSerializer类如下。

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

让我们检索一下雇员的详细资料,以了解StringRelatedField是如何显示关系字段的值的。检索雇员值的HTTPie命令是。

http :8000/employees/

输出

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": []
    }
]

分享命令提示的截图供你参考

序列器关系 - Django REST框架

这里你可以注意到,任务字段显示的是来自函数的字符串值 def str(self): 在 EmployeeTask 模型中。

    def __str__(self):
        return self.task_name

SlugRelatedField

SlugRelatedField使用目标上的一个字段表示关系的目标。默认情况下,该字段允许读-写操作。对于写操作,代表一个模型字段的slug字段应该是唯一的。SlugRelatedField有以下参数。

  • slug_field – 目标上的一个字段,它应该唯一地识别任何特定的实例。
  • queryset – 在验证字段输入时,它有利于模型实例的查找。关系必须明确地设置一个queryset,或者设置read_only=True。
  • many – 将此参数设置为True以序列化一个以上的关系
  • allow_null – 如果设置为True,该字段将接受None或空字符串的值,用于可忽略的关系。默认为 “假”。

在EmployeeTaskSerializer类中,你可以用下面的代码替换雇员字段的表示。

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

EmployeeTaskSerializer的代码如下。

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')

创建一个新的雇员任务的HTTPie命令是

http :8000/task/ task_name=”SlugRelatedField” employee=”Mathew A” task_desc=”Serialize relationship using SlugRelateField” deadline=”2021-01-27 00:00:00.000000+00:00″

输出

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"
}

分享命令提示的截图供你参考

序列器关系 - Django REST框架

在这里,当创建一个新任务时,我们在雇员字段中给出了雇员的名字,而不是主键。你也可以在表示SlugRelatedField时提到雇员ID。你应该确保它满足唯一约束。

HyperlinkedIndetityField

对于超链接的API,序列化器类应该扩展HyperlinkedModelSerializer。HyperlinkedModelSerializer类利用超链接来表示关系,而不是主键。默认情况下,从HyperlinkedModelSerializer类继承的序列化器类将包括一个url字段而不是一个主键字段。url字段将使用HyperlinkedIdentityField序列化器字段表示。它也可以被用于对象上的一个属性。

HyperlinkedIdentityField总是只读的。HyperlinkedIdentitField的参数是。

  • view_name – 视图名称被用作关系的目标。在标准路由器类中,这将是一个格式为-detail的字符串。
  • lookup_field – 目标上的字段,它被用于查找。它对应于被引用的视图上的一个URL关键字参数。默认为’pk’。
  • lookup_url_kwarg – 在URL conf中定义的与查找字段相对应的关键字参数名称。默认为使用与lookup_field相同的值。
  • format – 如果使用格式后缀,超链接字段将使用目标的相同格式后缀,除非通过使用格式参数覆盖。

在EmployeeTaskSerializer类中,你可以使用表示url字段,如下所示。

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

Note: By default, the serializer class that is inherited from HyperlinkedModelSerializer class will include a url field, and the view name will be a string with the format -detail.

EmployeeTaskSerializer类如下。

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__'

当实例化一个HyperlinkedModelSerializer时,必须在序列化器的上下文中包含当前的请求。在实例化EmployeeTaskSerializer时,你需要传递context={‘request’:request}作为参数。比如说。

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

请确保你在views.py文件中用上述代码编辑你的employee_list和employee_detail函数。在实例化EmployeeTaskSerializer(GET、POST、PUT和PATCH)时,你应该添加context={‘request’:request}参数。

Note: Note: This context along with the lookup_field is used for generating a fully qualified url by HyperlinkedIdentityField

让我们来检索EmployeeTask的值,以了解这些值是如何显示的。HTTPie的命令是

http GET :8000/task/1/

输出

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/"
}

分享命令提示的截图供你参考

序列器关系 - Django REST框架

如果我们分析一下输出,我们可以注意到,它显示的不是主键,而是url字段。

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

HyperlinkedRelatedField

HyperlinkedRelatedField用来表示使用超链接的关系的目标。默认情况下,这个字段是读写的,但我们可以通过设置只读标志True使其成为只读。HyperlinkedRelatedField的参数如下。

  • view_name – 视图名称被用作关系的目标。对于标准路由器类,这将是一个格式为-detail的字符串。
  • queryset – 在验证字段输入时,它有利于模型实例的查找。关系必须明确地设置一个queryset或设置read_only=True。
  • many – 要序列化一个以上的关系,你应该把这个参数设置为 True。
  • allow_null – 如果设置为True,该字段将接受None值或空字符串,用于可忽略的关系。默认为 “假”。
  • lookup_field – 目标上的字段被用于查找。它对应于被引用视图上的一个URL关键字参数。默认为’pk’。
  • lookup_url_kwarg – 在URL conf中定义的与查找字段相对应的关键字参数名称。默认情况下,它使用与lookup_field相同的值。
  • format – 如果使用格式后缀,超链接字段将使用目标的相同格式后缀,除非通过使用格式参数覆盖。

在我们的反向关系(Employee to EmployeeTask)中,我们看到默认情况下,序列化器(EmployeeSerializer)使用主键来表示关系的目标(要包含一个反向关系,你必须明确地将其添加到字段列表中)。我们还探索了其他的表示方法,如StringRelatedField和SlugRelatedField。这里,我们将使用HyperlinkedRelatedField。

在EmployeeSerializer类中,你可以使用表示任务字段,如下所示。

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

你可以用下面的EmployeeSerializer类来替换现有的EmployeeSerializer类。

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')

HTTPie的命令是。

http GET :8000/employees/8/

输出

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/"
}

分享命令提示的截图供你参考

序列器关系 - Django REST框架

如果我们分析输出,我们可以注意到,任务字段显示了一组,或超链接。

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

接下来,让我们了解如何通过使用序列化器作为字段来表达嵌套关系。

Nested Relationship

嵌套关系使得嵌入所指的实体成为可能。如果我们看一下EmployeeTask的例子,它引用了Employee。让我们使用嵌套关系来序列化EmployeeTask。

你可以使用下面的代码来引用EmployeeTaskSerializer中的雇员字段。

employee = EmployeeSerializer(read_only=True)

你可以用下面的代码替换EmployeeTaskSerializer。

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__'

我们来检索一个EmployeeTask条目。HTTPie的命令是

http GET :8000/task/1/

输出

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/"
}

分享命令提示的截图供你参考

序列器关系 - Django REST框架

你可以注意到,雇员字段显示的是完整的雇员详细信息,而不仅仅是主键或lug。

"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/"
    },

在本节中,我们探讨了DRF序列化器关系所提供的各种类型的关系字段。我们了解了每个关系字段如何表示与目标的关系。PrimaryKeyRelatedField使用它的主键来表示关系的目标,而StringRelatedField使用目标的str方法。在SlugRelatedField中,它使用一个目标字段来表示关系的目标。最后,HyperlinkedIdentityField被用作身份关系,而HyperlinkedRealtedField使用超链接表示关系的目标。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程