- RESTful 規范
- django rest framework 之 認證(一)
- django rest framework 之 權限(二)
- django rest framework 之 節流(三)
- django rest framework 之 版本(四)
- django rest framework 之 解析器(五)
- django rest framework 之 序列化(六)
- django rest framework 之 分頁(七)
- django rest framework 之 視圖(八)
一、前言
先建立數據庫,並添加相應的數據,用來后面序列化使用
1、建立數據庫模型
為數據建立相應的數據庫模型,並且有一對一,多對多,外鍵關聯。
from django.db import models
class UserGroup(models.Model):
title = models.CharField(max_length=32)
class UserInfo(models.Model):
user_type_choices = (
(1,'普通用戶'),
(2,'VIP'),
(3,'SVIP'),
)
user_type = models.IntegerField(choices=user_type_choices)
username = models.CharField(max_length=32,unique=True)
password = models.CharField(max_length=64)
group = models.ForeignKey("UserGroup", on_delete=models.CASCADE)
roles = models.ManyToManyField("Role")
class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE)
token = models.CharField(max_length=64)
class Role(models.Model):
title = models.CharField(max_length=32)
並執行數據庫遷移操作
python manage.py makemigrations
python manage.py migrate
2、添加少量數據
當數據遷移執行之后,會在sqlite數據庫中生活如下表
多對多關系的時候,django
自動生成第三張表維系表關系,字段分別是userinfo
和role
的id
,其中api_userinfo_roles
為多對多關系生成的表。
在表中添加少量數據
<1>、UserInfo表
<2>、UserGroup表
3、UserInfo_roles表
4、roles表
二、序列化的簡單使用
1、不使用序列化
<1>、路由
from django.conf.urls import url
from .views import RoleView
urlpatterns = [
url(r'^role/$', RoleView.as_view()),
]
<2>、視圖
from rest_framework.views import APIView
from .models import Role
import json
class RoleView(APIView):
def get(self, request, *args, **kwargs):
roles = Role.objects.all().values('id' ,'title')
print(roles, type(roles)) # roles為一個QuerySet對象
ret_roles = json.dumps(list(roles), ensure_ascii=False) # 多條數據
return HttpResponse(ret_roles)
2、簡單使用Serializer
<1>、定義序列化類
from rest_framework import serializers
class RoleSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField()
<2>、視圖
class RoleView(APIView):
def get(self, request, *args, **kwargs):
# 多條數據
# 將序列化后的數據都存到ser.data(OrderDict有序字典中)中
# roles = Role.objects.all()
# ser_roles = RoleSerializer(instance=roles, many=True)
# print(ser_roles, type(ser_roles)) # ListSerializer對象
# ret_roles = json.dumps(ser_roles.data, ensure_ascii=False) # 多條數據
# return HttpResponse(ret_roles)
# 單條數據
role = Role.objects.all().first()
ser_role = RoleSerializer(instance=role, many=False) # RoleSerializer對象
print(ser_role, type(ser_role)) # 單條數據
ret_roles = json.dumps(ser_role.data, ensure_ascii=False)
return HttpResponse(ret_roles)
總結:上面可以實現數據的簡單序列化,但是無法自定義字段,也無法對數據進行處理,不方便,限制較大
三、進一步使用Serializer
1、路由
from django.conf.urls import url
from .views import UserInfo
urlpatterns = [
url(r'^userinfo/$', UserInfo.as_view()),
]
2、視圖
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = UserInfo.objects.all()
users_ser = UserSerializer(instance=users, many=True)
users_ret = json.dumps(users_ser.data, ensure_ascii=False)
# print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
return HttpResponse(users_ret)
3、使用serializer
class UserSerializer(serializers.Serializer):
type = serializers.IntegerField(source='user_type')
user_type = serializers.CharField(source='get_user_type_display') # choices字段顯示
username = serializers.CharField()
pwd = serializers.CharField(source='password') # 自定義serializer中的key值
group_title = serializers.CharField(source='group.title') # 關聯對象屬性
roles = serializers.CharField(source='roles.all') # 多對多關系
roles_info = serializers.SerializerMethodField() # 表示自定義方法,顯示querytset對象詳情
def get_roles_info(self, row):
roles = row.roles.all()
ret = []
for item in roles:
ret.append(
{
'id': item.id,
'title': item.title
}
)
return ret
注:
- 如果沒有指定在
Filed
中沒有定義source
參數的時候,就自動與數據庫modles
定義的字段進行匹配,如上面的userrname
字段。在定義字段后,Serializer
類中可以自定義屬性如type - 當
models
中是以choice
定義時:需要定義source
參數定義get_字段名_display
才能獲取數據,這與在模板語言中的用法一樣,如上面的user_type
- 外鍵關聯的時候,直接 外鍵字段名.屬性 的方式定義傳參給source參數即可,如上面的
group.title
- 對於roles字段,想直接獲取所有的對象,但是無法做到細粒度的將對象的所有屬性展示出來,只能獲取到
QuerySet
對象 - 自定義字段,處理數據,如
roles_info
獲取所有的role
對象的屬性,處理數據可以定義方法,方法名格式為get_屬性
,並return
值最終返回值
執行結果:
另:自定義字段也可以采取繼承的方式,如:
class UsernameField(serializers.CharField):
def to_representation(self, value):
return 'username' + value
重寫to_representation
方法,value
為從數據庫取出的值,然后對value
進行處理,在返回即可
並將序列化類中的username改為
username = UsernameField()
四、使用ModelSerializer組件
1、包裝Serializer
class UserSerializer(serializers.ModelSerializer):
user_type = serializers.CharField(source='get_user_type_display')
roles = serializers.CharField(source='roles.all') # 外鍵關聯
roles_info = serializers.SerializerMethodField() # 表示自定義方法,顯示外鍵關聯詳情
group_title = serializers.CharField(source='group.title')
def get_roles_info(self, row):
roles = row.roles.all()
ret = []
for item in roles:
ret.append(
{
'id': item.id,
'title': item.title
}
)
return ret
class Meta:
model = UserInfo
# fields = '__all__' # 為全部的字段做匹配
fields = ['user_type', 'username', 'password', 'group', 'group_title', 'roles', 'roles_info'] # 自定義需要展示的字段
extra_kwargs = {'group': {'source': 'group_id'}}
ModelSerializer
與Serializer
區別在於:ModelSerializer
支持了Serializer
中所有的操作,並且通過自動生成所有數據字段與序列化類的一一對應關系,而不用自己手動添加。
即Serializer
是ModelSerializer
的父類,所以ModelSerializer
才會支持Serializer
的所有操作
返回結果
2、ModelSerializer深度控制
在上面,看到在進行連表查詢的時候,只能獲取到外鍵關聯對象,在當前表中存儲的id,怎樣拿到外鍵關聯對象的具體信息。
class UserSerializer(serializers.ModelSerializer):
# 自動向內部進行深度查詢 depth表示查詢層數
class Meta:
model = UserInfo
# fields = "__all__"
fields = ['id','username','password','group','roles']
depth = 1 # 0 ~ 10 默認的depth為0
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = UserInfo.objects.all()
users_ser = UserSerializer(instance=users, many=True)
users_ret = json.dumps(users_ser.data, ensure_ascii=False)
# print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
return HttpResponse(users_ret)
注:這里的depth就表示深度查詢的層數,默認的層數為0,層數越多查詢效率越慢。
返回結果
3、自動生成鏈接
在上面我們看到,在返回組group的時候是返回該組的id
,或者用depth
深度控制,返回組的詳細信息。在restful規范中,規定應該給出相應的詳情鏈接,可以通過url拼接,在django rest framework中也有相對應的實現。
首先改寫一下用戶信息序列化類,使之能夠提供用戶組詳情的有關url
class UserSerializer(serializers.ModelSerializer):
group = serializers.HyperlinkedIdentityField(view_name='api:gp', lookup_field='group_id', lookup_url_kwarg='xxx')
# view_name參數 進行傳參的時候是參考路由匹配中的name與namespace參數
# lookeup_field參數是根據在UserInfo表中的連表查詢字段group_id
# look_url_kwarg參數在做url反向解析的時候會用到
class Meta:
model = UserInfo
fields = ['id','username','password','group','roles']
depth = 1 # 0 ~ 10
class UserInfoView(APIView):
def get(self, request, *args, **kwargs):
users = UserInfo.objects.all()
users_ser = UserSerializer(instance=users, many=True, context={'request': request}) # 在做鏈接的時候需要添加context參數
users_ret = json.dumps(users_ser.data, ensure_ascii=False)
# print(users_ser.data, type(users_ser.data), type(users_ser.data[0]))
return HttpResponse(users_ret)
# 添加group序列化類
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = UserGroup
fields = "__all__"
# 返回用戶組的詳細信息
class GroupView(APIView):
def get(self,request,*args,**kwargs):
pk = kwargs.get('xxx')
obj = UserGroup.objects.filter(pk=pk).first()
ser = GroupSerializer(instance=obj,many=False)
ret = json.dumps(ser.data,ensure_ascii=False)
return HttpResponse(ret)
返回結果
當我們點解用戶組詳情鏈接后,返回結果
4、校驗數據
序列化不僅可以做數據的返回,也可以對前端提交的數據進行校驗。
<1>、類方法檢驗
class TitleValidator(object):
def __init__(self, base):
self.base = base
def __call__(self, value):
if not value.startswith(self.base):
message = '標題必須以 %s 為開頭。' % self.base
raise serializers.ValidationError(message)
def set_context(self, serializer_field):
# 執行驗證之前調用,serializer_fields是當前字段對象
pass
class UserGroupSerializer(serializers.Serializer):
title = serializers.CharField(error_messages={'required': '標題不能為空'}, validators=[TitleValidator('Django'),])
class UserGroupView(APIView):
def post(self,request,*args,**kwargs):
print(request.data)
ser = UserGroupSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data['title'])
else:
print(ser.errors)
return HttpResponse('提交數據')
上面的TitileValidator
類封裝了對request.data
前端傳來的數據的校驗,title
相對應的是數據中的key
為title
的值。TitileValidator
實現了call()
特殊方法,並把具體的驗證邏輯封裝到里邊,是一個可直接調用的對象。而self.base
則為具體的title
對應的數據,進行處理。
<2>、鈎子方法
class UserGroupSerializer(serializers.Serializer):
title = serializers.CharField()
def validate_title(self, value):
from rest_framework import exceptions
if not value:
raise exceptions.ValidationError('不可為空')
return value
class UserGroupView(APIView):
def post(self,request,*args,**kwargs):
print(request.data)
ser = UserGroupSerializer(data=request.data)
if ser.is_valid():
print(ser.validated_data['title'])
else:
print(ser.errors)
return HttpResponse('提交數據')
在定義鈎子方法的時候,鈎子函數是以validate_字段名
的方式進行命名的。只有遵循這樣的格式,在Serializer
內部會對鈎子函數的名字進行拆分並識別出來。在validate_title
內部封裝了對數據的校驗操作,value
則為具體的值