序列器关系 – 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": []
}
]
分享命令提示的截图供你参考
你可以注意到名为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"
}
]
分享命令提示的截图供你参考
让我们看看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"
}
分享命令提示的截图供你参考。
现在让我们来探讨一下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": []
}
]
分享命令提示的截图供你参考
这里你可以注意到,任务字段显示的是来自函数的字符串值 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"
}
分享命令提示的截图供你参考
在这里,当创建一个新任务时,我们在雇员字段中给出了雇员的名字,而不是主键。你也可以在表示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/"
}
分享命令提示的截图供你参考
如果我们分析一下输出,我们可以注意到,它显示的不是主键,而是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/"
}
分享命令提示的截图供你参考
如果我们分析输出,我们可以注意到,任务字段显示了一组,或超链接。
"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/"
}
分享命令提示的截图供你参考
你可以注意到,雇员字段显示的是完整的雇员详细信息,而不仅仅是主键或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使用超链接表示关系的目标。