一、django知识点
1、中间件
# 中间件的作用是,任何接口进来先走中间件的类,再去视图函数或者视图类执行,应用场景:判断哪些地址需要登录,哪些不需要
使用方法:写一个类,继承MiddlewareMixin
from django.utils.deprecation import MiddlewareMixin
# 中间件的作用是,任何借口进来先走中间件的类,再去视图函数或者视图类执行
class M1(MiddlewareMixin):
"""中间件1"""
def process_request(self, request):
# 排除不需要登录的页面
if request.path_info == "/api/v2/login/" or request.path_info == "/api/v2/register/" \
or request.path_info == "/api/v2/image/":
return
# 如果方法中没有返回值,可以继续往后走
info_dict = request.session.get("info")
print("用户信息:", info_dict)
if info_dict:
return
return redirect("/api/v2/login/")
def process_response(self, request, response):
return response
加载到settings文件的配置中
MIDDLEWARE = [
"utils.csrf_middleware.NotUseCsrfTokenMiddlewareMixin", # 解决session跨域问题
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'apps.middleware.auth.M1', # 自定义中间件
]
2、获取请求参数
get请求:
print(request.GET) # 值<QueryDict: {'name': ['zs'], 'age': ['19']}>
print(request.GET.get("name")) # zs
post请求:
Content-Type: multipart/form-data; boundary=<calculated when request is sent>
Content-Type: application/x-www-form-urlencoded
print(request.POST)
print(request.POST.get("name"))
# <QueryDict: {'name': ['zz'], 'age': ['12']}>
# zz
Content-Type :application/json
print(json.loads(request.body.decode('utf-8')))
# 结果:{'name': 'zs', 'age': '11'}
3、restframework获取请求参数
class CaseInfo(APIView):
def get(self, request, pk=None):
# 能接get请求的数据,值是字典
data = request.query_params
def post(self, request):
# 能接收json格式和urlencoded格式的数据,值是字典
data = request.data
二、django与restframework的CBV执行过程
1、django原生执行方式
1、类视图路由写法
from django.urls import path
from . import views
urlpatterns = [
path('dj2/', views.ClassDj.as_view()), # django原生cbv
]
2、django原生类视图函数
from django.views import View
class ClassDj(View):
def get(self, request):
return JsonResponse({"status": True, "method": "GET"})
def post(self, request):
return JsonResponse({"status": True, "method": "POST"})
def put(self, request):
return JsonResponse({"status": True, "method": "PUT"})
def delete(self, request):
return JsonResponse({"status": True, "method": "DELETE"})
类视图函数继承自django.views下的View,执行View的as_view()方法
内部又执行了view方法,返回的是 self.dispatch(request, *args, **kwargs)的执行结果
dispatch方法判断了请求方法是否在定义的那几种请求方法中,利用反射调用函数
2、restframework执行方式
1、类视图路由写法
from django.urls import path
from . import views
urlpatterns = [
path('dj1/', views.dj), # django原生fbv
path('dj2/', views.ClassDj.as_view()), # django原生cbv
path('dj3/', views.InfoView.as_view()), # rest_framework的cbv
]
2、使用restframework框架的视图函数
from rest_framework.views import APIView
# rest_framework的CBV,APIView继承django原生的View
class InfoView(APIView):
def get(self, request):
return Response({"status": True, "method": "GET"})
def post(self, request):
return Response({"status": True, "method": "post"})
def put(self, request):
return Response({"status": True, "method": "put"})
def delete(self, request):
return Response({"status": True, "method": "delete"})
类视图继承自rest_framework.views 下的 APIView,执行APIView下的as_view()方法
本质上还是执行django.views下的View的as_view()方法,因为APIView继承自View,super调用了父类的as_view()方法,去掉了csrf验证
然后还是调用dispatch()方法,此时APIView有自己的dispatch方法
dispatch对传过来的request进行了封装,
把request付给了_request,且定义了__getattr__方法,此时再去使用request时,不仅多了restframework新加的其他特性,依旧可以使用原生的request中的方法,例如,request.method,request.path_info等
请求相关的常用值:
path_info 返回用户访问url,不包括域名
method 请求中使用的HTTP方法的字符串表示,全大写表示。
GET 包含所有HTTP GET参数的类字典对象
POST 包含所有HTTP POST参数的类字典对象
body 请求体,byte类型 request.POST的数据就是从body里面提取到的
request.path,
请求的路径,这里的路径是指相对路径
request.encoding,请求的编码格式
request.META,r
equest.MATE获取的是一个标准的python字典。它包含了所有的HTTP首部。具体的头部信息取决于客户端和服务器
restframework新增的:data,auth,等
3、认证组件(用户授权)
1、写一个认证类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 1、读取请求传递的token
token = request.query_params.get("token")
# 2、校验合法性
if token:
# 3、返回值
# 3.1 返回元祖(11,22) 认证成功 request.user request.auth
return "wan", token
# 3.2 返回异常 认证失败
return AuthenticationFailed("认证失败")
# 3.3 返回None 多个认证类
pass
2、使用方法1:在类视图里边添加 authentication_classes(列表)
多个认证类,成功一个就结束了
3、使用方法2:配置在settings里,全局应用
REST_FRAMEWORK = {
'UNAUTHENTICATED_USER': None,
'DEFAULT_AUTHENTICATION_CLASSES': ["apps.app_restfwork.views.MyAuthentication",]
}
4、源码调用过程
认证组件封装(封装认证组件对象到request对象中,等待被调用):
接下来执行:initial方法
这里的request是drf的request,已经封装处理过的request,user方法被property封装,所以不用括号(调用),
self.authenticators是之前Request类已经定义好的,在dispatch方法里调用initialize_request(),封装进request里的
执行认证后最终会赋值,
4、权限组件
1、写一个权限类
返回True代表有权限,False代表无权限
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
code = 403
message = {"status": False, "msg": " 无权访问"} # 定义返回信息
def has_permission(self, request, view):
v1 = random.randint(1, 3)
if v1 == 2:
return True
return False
2、使用方法1:局部应用
写一个视图类,定义:permission_classes,多个权限组件必须全部通过才能正常返回
from apps.middleware.permission import MyPermission
class LoginView(APIView):
authentication_classes = [] # 加入认证组件
permission_classes = [MyPermission, ] # 加入权限组件
def get(self, request):
self.dispatch()
return Response("LoginView")
3、使用方法2:全局应用
配置在settings文件里
REST_FRAMEWORK = {
# 全局认证模块--如果需要生效,需要和全局权限模块一起使用
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
'apps.middleware.auth.MyAuthentication',
),
# 全局权限模块
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
'apps.middleware.permission.MyPermission',
),
}
4、源码调用过程
依旧是走这么个流程: as_view() --> dispatch()-->initial()-->self.check_permissions(request)
dispatch是APIView的方法
如果权限类返回的值是True,不需要走下一步,如果返回的是False,需要走:permission_denied()方法抛出异常
5、把权限验证 且的关系手动改成或的关系,一个权限类True就可以正常返回
# 方法一,写一个check_permissions方法
class LoginView(APIView):
authentication_classes = [] # 加入认证组件
permission_classes = [MyPermission, MyPermission1, MyPermission2] # 加入权限组件
def check_permissions(self, request):
for permission in self.get_permissions():
if permission.has_permission(request, self):
return
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
# 方法二,写一个类继承APIView,实现check_permissions方法,视图类再继承这个类
class NbApiView(APIView):
def check_permissions(self, request):
for permission in self.get_permissions():
if permission.has_permission(request, self):
return
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
class LoginView(NbApiView):
authentication_classes = [] # 加入认证组件
permission_classes = [MyPermission, MyPermission1, MyPermission2] # 加入权限组件
5、限流组件
1、写一个限流类,继承自SimpleRateThrottle,还需用到缓存,这里是用redis
from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache
# 限流类1,需要登录的用户
class MyThrottle(SimpleRateThrottle):
scope = "XXX" # 相当于标识
THROTTLE_RATES = {"XXX": "5/m"} # 访问频率,每分钟五次,也可放到settings配置中
cache = default_cache
def get_cache_key(self, request, view):
if request.user: # 如果登录了,就找用户id
ident = request.user.pk
else: # 如果没登录,就找访问的ip地址
ident = self.get_ident(request)
# cache_format = 'throttle_%(scope)s_%(ident)s'
print(self.cache_format % {'scope': self.scope, 'ident': ident})
return self.cache_format % {'scope': self.scope, 'ident': ident}
# 限流类2,无需登录的用户
class MyThrottle1(SimpleRateThrottle):
scope = "Xw" # 相当于标识
cache = default_cache
def get_cache_key(self, request, view):
# 如果没登录,就找访问的ip地址
ident = self.get_ident(request)
return self.cache_format % {'scope': self.scope, 'ident': ident}
2、settings文件中的redis配置
CACHES = {
'default': {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://ip:6379/5",
"TIMEOUT": 300,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "密码"
}
}
}
3、settings文件中限流时间配置
REST_FRAMEWORK = {
# 限流组件配置项
'DEFAULT_THROTTLE_RATES': {
"XXX": "5/m",
"Xw": "3/s",
}
}
4、在视图类中使用组件
class LoginView(APIView):
authentication_classes = [] # 加入认证组件
permission_classes = [MyPermission, MyPermission1, MyPermission2] # 加入权限组件
throttle_classes = [MyThrottle] # 加入限流组件
6、版本
1、通过get参数传递
(1)写一个视图类
class LoginView(APIView):
versioning_class = QueryParameterVersioning # 版本控制组件
def get(self, request):
# self.dispatch(request)
print("版本信息:", request.GET.get('version'))
return Response("LoginView")
(2)控制访问
访问地址:http://127.0.0.1:8000/api/v3/dj2/?version=1,需携带version参数,也可自定义,需要加settings配置
REST_FRAMEWORK = {
# 版本控制get参数配置项
'VERSION_PARAM': 'v1',
}
2、通过路由控制
(1)写一个视图类
class LoginView(APIView):
versioning_class = URLPathVersioning # 版本控制组件
def get(self, request, *args, **kwargs):
print("版本信息:", request.version)
print("版本class:", request.versioning_scheme)
url = request.versioning_scheme.reverse('hh', request=request)
print("反向生成url:", url)
return Response("LoginView")
(2)路由写法
访问地址:http://127.0.0.1:8000/v11/dj2/
from django.urls import path
from . import views
urlpatterns = [
path('dj2/', views.LoginView.as_view()),
path('<str:version>/dj2/', views.LoginView.as_view(), name="hh"),
]
(3)全局配置
REST_FRAMEWORK = {
# 版本控制配置类,用于url里的版本
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning'
}
3、请求头写法
请求头里加version:
Accept: application/json;version=1.0
7、解析器
框架默认是:[<class 'rest_framework.parsers.JSONParser'>, <class 'rest_framework.parsers.FormParser'>, <class 'rest_framework.parsers.MultiPartParser'>]三种
1、写一个视图类
(1)解析post请求参数
class LoginView(APIView):
parser_classes = [JSONParser, FormParser] # 解析器
# 根据请求,匹配对应的解析器,并寻找渲染器
content_negotiation_class = DefaultContentNegotiation
def post(self, request, *args, **kwargs):
print(type(request.data), request.data)
return Response("OK")
(2)解析单独上传文件的接口参数
from rest_framework.parsers import JSONParser, FormParser, FileUploadParser
class UploadHea(APIView):
parser_classes = [FileUploadParser]
def post(self, request):
file_obj = request.data.get("file")
with open(file_obj.name, mode="wb") as f:
for chunk in file_obj:
f.write(chunk)
f.close()
return Response("ok")
postman请求头:Content-Disposition:attachment;filename=文件名字.jpg
Content-Type:*/*:
请求参数:
(3)解析数据加文件的接口参数
from rest_framework.parsers import MultiPartParser
class UploadHea(APIView):
parser_classes = [MultiPartParser]
def post(self, request):
print(request.content_type) # multipart/form-data; boundary=--------------------------295243681590039374110669
print(request.data) # <QueryDict: {'name': ['zz'], 'age': ['12'], 'file': [<InMemoryUploadedFile: 2qsz.png (image/png)>]}>
file_obj = request.data.get("file")
with open(file_obj.name, mode="wb") as f:
for chunk in file_obj:
f.write(chunk)
f.close()
return Response("ok")
请求头:Content-Type:multipart/form-data; boundary=<calculated when request is sent>
接口参数:
2、手动配置settings,会修改默认配置的三个类
REST_FRAMEWORK = {
"DEFAULT_PARSER_CLASSES": "rest_framework.parsers.JSONParser"
}
8、序列化器
模型类
class TestDepart(models.Model):
title = models.CharField("部门", max_length=32)
order = models.IntegerField("顺序")
count = models.IntegerField("人数")
class TestUser(models.Model):
name = models.CharField("姓名", max_length=12)
age = models.IntegerField("年龄")
gender = models.SmallIntegerField("性别", choices=((1, "男"), (2, "女")), default=1)
depart = models.ForeignKey(TestDepart, on_delete=models.CASCADE, verbose_name="部门")
ctime = models.DateTimeField(auto_now_add=True)
tags = models.ManyToManyField(verbose_name="标签", to="Tag")
class Tag(models.Model):
caption = models.CharField(verbose_name="标签", max_length=32)
1、写一个基础序列化器
from rest_framework import serializers
class DepartSerializer(serializers.Serializer):
title = serializers.CharField()
count = serializers.IntegerField()
def update(self, instance, validated_data):
pass
def create(self, validated_data):
pass
2、简单初应用,序列化一条数据对象
class DepartView(APIView):
authentication_classes = []
permission_classes = []
def get(self, request):
depart_obj = TestDepart.objects.all().first()
ser = DepartSerializer(instance=depart_obj) # 序列化出结果
context = {"status": True, "data": ser.data}
return Response(context)
# context: {'status': True, 'data': {'title': '测试', 'count': 20}}
3、简单初应用,序列化多条数据对象
序列化时加上:many=True
class DepartView(APIView):
authentication_classes = []
permission_classes = []
def get(self, request):
queryset = depart_obj = TestDepart.objects.all()
ser = DepartSerializer(instance=queryset, many=True) # 序列化出结果
context = {"status": True, "data": ser.data}
return Response(context)
4、序列化器继承自ModelSerializer使用
from rest_framework import serializers
from apps.app_restfwork.models import TestDepart
class DepartSerializer(serializers.ModelSerializer):
class Meta:
model = TestDepart
fields = "__all__"
5、序列化器继承自ModelSerializer使用,外键和可选择字段的控制
模型类:
class TestUser(models.Model):
name = models.CharField("姓名", max_length=12)
age = models.IntegerField("年龄")
gender = models.SmallIntegerField("性别", choices=((1, "男"), (2, "女")), default=1)
depart = models.ForeignKey(TestDepart, on_delete=models.CASCADE, verbose_name="部门")
ctime = models.DateTimeField(auto_now_add=True)
序列化器:
class UserSerializer(serializers.ModelSerializer):
# 可以把源字段修改,也可以新添加一个字段,但这里定义的字段在下方必须被使用
gender_name = serializers.CharField(source="get_gender_display")
# gender = serializers.CharField(source="get_gender_display")
# source的值必须是和模型类相关的字段或字段方法
new_name = serializers.CharField(source="name")
# 外键id转成汉字显示
depart_name = serializers.CharField(source="depart.title")
# 格式化时间输出
ctime = serializers.DateTimeField(format="%Y-%m-%d")
# 自定义字段
xxx = serializers.SerializerMethodField()
class Meta:
model = TestUser
fields = ["name", "age", "gender", "gender_name", "depart", "new_name", "depart_name", "ctime", "xxx"]
def get_xxx(self, obj): # 钩子方法,返回自定义字段xxx的值
return "888"
6、序列化器继承自ModelSerializer使用,多对多关系字段的控制
繁琐写法
class UserSerializer(serializers.ModelSerializer):
# 多对多关系字段
tags = serializers.SerializerMethodField()
class Meta:
model = TestUser
fields = ["name", "age", "gender", "depart", 'ctime', "tags"]
def get_tags(self, obj):
# obj是每条模型类user对象,查出来的每条模型类user对象有的tags对象集合
queryset = obj.tags.all()
print(queryset)
return [{"id": i.id, "caption": i.caption} for i in queryset]
简易写法
from rest_framework import serializers
from apps.app_restfwork.models import TestDepart, TestUser, Tag
class DepartSerializer(serializers.ModelSerializer):
class Meta:
model = TestDepart
fields = "__all__"
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = "__all__"
class UserSerializer(serializers.ModelSerializer):
# 这种写法会把关联表中的所有字段全部显示,自定义字段需修改对应的序列化器
# 外键
depart = DepartSerializer()
# 多对多关系字段
tags = TagSerializer(many=True)
class Meta:
model = TestUser
fields = ["name", "age", "gender", "depart", 'ctime', "tags"]
7、继承,父序列化器是继承自:Serializer
定义字段name,相当于父类没有这个字段,只是给子类提前定义好
class Base(serializers.Serializer):
xx = serializers.CharField(source="name")
子类使用的时候,父类source定义的name,是子类自己的name字段
class UserSerializer(serializers.ModelSerializer, Base):
class Meta:
model = TestUser
fields = ["name", "age", "xx"]
8、源码调用流程
单个字段序列化
实例化过程
"""
# 第一步:加载字段
# 1、在类成员中删除并提取出来这些字段(CharField,IntegerField),“name = 123” 这种的不提取
# 2、汇总到BBSerializer._declared_fields ={"xx":对象,"yy":对象}
class Base(serializers.Serializer):
yy = serializers.CharField(source="name")
xx = serializers.IntegerField(source="age")
name = 123
# 1、在类成员中删除并提取字段,父类字段也提取
# 2、汇总到BBSerializer._declared_fields = {"xx":对象,"yy":对象,"zz": 对象}
class BBSerializer(serializers.ModelSerializer, Base):
zz = serializers.CharField(source="name")
# 第二步:序列化
queryset = TestUser.objects.all()
ser = UserSerializer(instance=queryset, many=True) # 序列化出ListSerializer结果
db->queryset = [{id:xxx,name:xxx,age:xxx}] => 循环查询集中的每个对象,再去调用UserSerializer序列化
queryset = TestUser.objects.all().first()
ser = UserSerializer(instance=queryset, many=False) # 序列化出UserSerializer结果
db->queryset = {id:xxx,name:xxx,age:xxx} => UserSerializer
序列化过程:
queryset = TestUser.objects.all().first()
ser = UserSerializer(instance=queryset, many=False)
ser.data # 会触发序列化
- 内部寻找对应关系
"""
class SerializerMetaclass(type):
def _get_declared_fields(cls, bases, attrs):
# fields吧所有的字段类型提取出来
fields = [(field_name, attrs.pop(field_name))
for field_name, obj in list(attrs.items())
if isinstance(obj, Field)]
# 根据_creation_counter排序
fields.sort(key=lambda x: x[1]._creation_counter)
known = set(attrs)
def visit(name):
known.add(name)
return name
base_fields = [
(visit(name), f)
for base in bases if hasattr(base, '_declared_fields')
for name, f in base._declared_fields.items() if name not in known
]
return OrderedDict(base_fields + fields)
def __new__(cls, name, bases, attrs):
# 创建类的时候,给每个对象多加了一个 _declared_fields 属性,是从父类那来的数据
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
return super().__new__(cls, name, bases, attrs)
class BaseSerializer(Field):
pass
def __init__(self, instance=None, data=empty, **kwargs):
self.instance = instance
# if data is not empty:
# self.initial_data = data
# self.partial = kwargs.pop('partial', False)
# self._context = kwargs.pop('context', {})
# kwargs.pop('many', None)
# super().__init__(**kwargs)
def __new__(cls, *args, **kwargs):
if kwargs.pop('many', False): #many在此时区分情况
return cls.many_init(*args, **kwargs)
return super().__new__(cls, *args, **kwargs) # 创建UserSerializer对象
def many_init(cls, *args, **kwargs):
allow_empty = kwargs.pop('allow_empty', None)
child_serializer = cls(*args, **kwargs) #UserSerializer实例化
list_kwargs = {
'child': child_serializer,
}
if allow_empty is not None:
list_kwargs['allow_empty'] = allow_empty
list_kwargs.update({
key: value for key, value in kwargs.items()
if key in LIST_SERIALIZER_KWARGS
})
meta = getattr(cls, 'Meta', None)
list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
return list_serializer_class(*args, **list_kwargs) # ListSerializer实例化,并把UserSerializer对象放进去
def to_representation(self, instance):
raise NotImplementedError('`to_representation()` must be implemented.')
def data(self):
if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):
msg = (
'When a serializer is passed a `data` keyword argument you '
'must call `.is_valid()` before attempting to access the '
'serialized `.data` representation.\n'
'You should either call `.is_valid()` first, '
'or access `.initial_data` instead.'
)
raise AssertionError(msg)
if not hasattr(self, '_data'):
if self.instance is not None and not getattr(self, '_errors', None):
# 真实序列化过程
self._data = self.to_representation(self.instance)
elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
self._data = self.to_representation(self.validated_data)
else:
self._data = self.get_initial()
return self._data
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
def fields(self):
fields = BindingDict(self)
for key, value in self.get_fields().items():
fields[key] = value
return fields
def _readable_fields(self):
for field in self.fields.values():
if not field.write_only:
yield field
def to_representation(self, instance):
ret = OrderedDict()
fields = self._readable_fields
for field in fields:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_none is None:
ret[field.field_name] = None
else:
ret[field.field_name] = field.to_representation(attribute)
return ret
class ModelSerializer(Serializer):
pass
class UserSerializer(serializers.ModelSerializer):
gender_name = serializers.CharField(source="get_gender_display")
new_name = serializers.CharField(source="name")
--------字段校验---------
继承Serializer的
from django.core.validators import RegexValidator
from rest_framework import serializers, exceptions
from apps.app_restfwork.models import TestDepart, TestUser, Tag
class DepartSerializer(serializers.Serializer):
title = serializers.CharField(required=True, max_length=12, min_length=2)
order = serializers.IntegerField(required=False)
count = serializers.CharField(required=False)
level = serializers.ChoiceField(choices=[("1", "高级"), ("2", "中级"), ("3", "初级")])
# email = serializers.EmailField()
# 正则校验字段
email = serializers.CharField(validators=[RegexValidator(r"\d", message="邮箱格式错误")])
# 字段校验钩子
def validate_email(self, value):
if len(value) > 6:
raise exceptions.ValidationError("email字段校验错误")
return value
继承ModelSerializer的
class DepartSerializer(serializers.ModelSerializer):
email = serializers.SerializerMethodField()
class Meta:
model = TestDepart
fields = ['title', 'order', 'email']
extra_kwargs = {
"title": {"max_length": 6, "min_length": 2},
"email": {"validators": [RegexValidator(r"\d+", message="邮箱格式错误")]},
}
class UserSerializer(serializers.ModelSerializer):
# gender_name = serializers.CharField(source="get_gender_display")
# new_name = serializers.CharField(source="name")
# depart_name = serializers.CharField(source="depart.title")
# depart = DepartSerializer()
# ctime = serializers.DateTimeField(format="%Y-%m-%d")
# 自定义字段
# xxx = serializers.SerializerMethodField()
# 多对多关系字段
# tags = serializers.SerializerMethodField()
# tags = TagSerializer(many=True)
tags = serializers.ListField()
class Meta:
model = TestUser
fields = ["name", "age", "gender", "depart", "tags"]
# 自定义验证
def validate_depart(self, value):
print(value)
if value.id > 1:
return value
raise exceptions.ValidationError("部门错误")
def validate_tags(self, value):
print("接收到tags数据:", value)
queryset = Tag.objects.filter(id__in=value)
print("插到的tags数据:", queryset)
return queryset
# def get_xxx(self, obj):
# return "888"
# def get_tags(self, obj):
# queryset = obj.tags.all()
# print(queryset)
#
# return [{"id": i.id, "caption": i.caption} for i in queryset]
9、序列化 增
(1)单表增
class DepartView(APIView):
authentication_classes = []
permission_classes = []
def get(self, request):
queryset = TestDepart.objects.all()
ser = DepartSerializer(instance=queryset, many=True) # 序列化出结果
context = {"status": True, "data": ser.data}
return Response(context)
def post(self, request, *args, **kwargs):
# 获取原始数据
print(request.data)
# 校验
ser = DepartSerializer(data=request.data)
# 复杂写法,但是自己好控制
# if ser.is_valid():
# return Response({"status": True, "data": ser.validated_data})
# else:
# return Response({"status": False, "data": ser.errors})
# 简单写法
ser.is_valid(raise_exception=True)
# 保存到数据库方法一
# TestDepart.objects.create(**ser.data)
# 保存到数据库方法二
# ser.save()
ser.save(count=100) # 这个也能自己传字段,当前端没传时
return Response({"status": True, "data": ser.validated_data})
(2)多表增(外键,多对多关系)
class UserSerializer(serializers.ModelSerializer):
tags = serializers.ListField() # tags多对多关系字段自定义
class Meta:
model = TestUser
fields = ["name", "age", "gender", "depart", "tags"]
# 自定义验证外键部门
def validate_depart(self, value):
print(value)
if value.id > 1:
return value
raise exceptions.ValidationError("部门错误")
# 自定义验证多对多关系字段
def validate_tags(self, value):
print("接收到tags数据:", value)
queryset = Tag.objects.filter(id__in=value)
print("插到的tags数据:", queryset)
return queryset
# 视图类
class UserView(APIView):
def post(self, request, *args, **kwargs):
print("用户数据:", request.data)
ser = UserSerializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save()
# 返回的是校验后的序列化数据,不是从库里读出来的
return Response({"status": True, "data": ser.initial_data})
10、序列化 改
11、序列化 删
12、同时校验和序列化(读写)
1、在序列化器里没有校验规则的字段,会走model模型类的规则 2、model模型类没定义的字段,序列化器自己通过SerializerMethodField定义的字段需要写get_email方法,obj是模型类对象 3、model定义的字段: (1)read_only设置为True,是只需从库里序列化输出时显示,新增时不用填 (2)write_only设置为True,是添加时需要填,从库里序列化输出时不显示 序列化器自定义的字段: 使用了def get_email(self, obj): 方法后,自读只写不受控制了
## 序列化器
class DepartSerializer(serializers.ModelSerializer):
email = serializers.SerializerMethodField()
class Meta:
model = TestDepart
fields = ['id', 'title', 'order', 'count', 'email']
extra_kwargs = {
"id": {"read_only": True},
# "title": {"max_length": 6, "min_length": 2},
"email": {"read_only": True, "validators": [RegexValidator(r"\d+", message="邮箱格式错误")]},
"count": {"write_only": True},
}
def get_email(self, obj):
print("写入的email:", obj)
return 666
## 视图类
class DepartView(APIView):
def post(self, request, *args, **kwargs):
# 校验
ser = DepartSerializer(data=request.data)
ser.is_valid(raise_exception=True)
# 保存
ser.save()
# 查询表数据
queryset = TestDepart.objects.all()
# 序列化输出
ser = DepartSerializer(instance=queryset, many=True) # 序列化出结果
return Response({"status": True, "data": ser.data})
class UserSerializer(serializers.ModelSerializer):
gender = serializers.CharField(write_only=True)
gender_name = serializers.CharField(source="get_gender_display", read_only=True)
depart_name = DepartSerializer(read_only=True, source="depart")
tags_info = TagSerializer(read_only=True, source="tags")
class Meta:
model = TestUser
fields = ["id", "name", "age", "gender", "gender_name", "depart", "depart_name", "tags","tags_info"]
# 自定义验证
def validate_depart(self, value):
print("部门:", value)
if value.id > 1:
return value
raise exceptions.ValidationError("部门错误")
def get_depart_name(self, obj):
return obj.depart.title
def get_tags_info(self, obj):
print("tags_info:", obj)
return obj.tags
13、同时校验和序列化(选择型字段),自定义字段
1、根据源码自定义字段
class NbCharField(serializers.IntegerField):
def __init__(self, method_name=None, **kwargs):
self.method_name = method_name
super().__init__()
def bind(self, field_name, parent):
if self.method_name is None:
self.method_name = 'xget_{field_name}'.format(field_name=field_name)
super().bind(field_name, parent)
def get_attribute(self, instance):
method = getattr(self.parent, self.method_name)
return method(instance)
def to_representation(self, value):
return str(value)
class UserSerializer(serializers.ModelSerializer):
# 使用自定义字段,满足输入时输入数字,返回是汉字内容的情况
gender = NbCharField()
depart_name = DepartSerializer(read_only=True, source="depart")
class Meta:
model = TestUser
fields = ["id", "name", "age", "gender", "depart", "depart_name", "tags"]
def xget_gender(self, obj):
return obj.get_gender_display()
2、写一个钩子,让其他序列化器类继承,可使用nb_字段名的方法
# 序列化字段时钩子函数
from collections import OrderedDict
from rest_framework.fields import SkipField
from rest_framework.relations import PKOnlyObject
class HookSerializer:
def to_representation(self, instance):
ret = OrderedDict()
fields = self._readable_fields
for field in fields:
if hasattr(self, 'nb_%s' % field.field_name):
value = getattr(self, 'nb_%s' % field.field_name)(instance)
ret[field.field_name] = value
else:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
check_for_name = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_name is None:
ret[field.field_name] = None
else:
ret[field.field_name] = field.to_representation(attribute)
return ret
class UserSerializer(HookSerializer, serializers.ModelSerializer):
depart_name = DepartSerializer(read_only=True, source="depart")
class Meta:
model = TestUser
fields = ["id", "name", "age", "gender", "depart", "depart_name", "tags"]
def nb_gender(self, obj):
return obj.get_gender_display()
14、案例
class UserInfo(models.Model):
username = models.CharField(verbose_name="用户名", max_length=32, db_index=True)
password = models.CharField(verbose_name="密码", max_length=64)
token = models.CharField("Token", max_length=64, null=True, blank=True)
class Blog(models.Model):
category_choices = ((1, "云计算"), (2, "Python全栈"), (3, "Java开发"))
category = models.SmallIntegerField("分类", choices=category_choices, default=2)
image = models.CharField("封面", max_length=225)
title = models.CharField("标题", max_length=32)
summary = models.CharField("简介", max_length=100)
text = models.TextField("内容")
ctime = models.DateTimeField("创建时间", auto_now_add=True)
creator = models.ForeignKey(UserInfo, on_delete=models.CASCADE)
comment_count = models.PositiveIntegerField("请论数据", default=0)
favor_count = models.PositiveIntegerField("赞数", default=0)
class Favor(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
user = models.ForeignKey(UserInfo, on_delete=models.CASCADE)
create_time = models.DateTimeField(auto_now_add=True)
class Meta: # 索引
constraints = [
models.UniqueConstraint(fields=['blog', 'user'], name='uni_favor_blog_user')
]
class Comment(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
user = models.ForeignKey(UserInfo, on_delete=models.CASCADE)
content = models.CharField('内容', max_length=150)
create_time = models.DateTimeField(auto_now_add=True)
三、问题记录
1、把app放到apps文件夹下后,需要指定对目录,不然报错:
RuntimeError: Model class apps.app_restfwork.models.TestDepart doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
可以在apps文件里加上apps
也可在settings文件里加配置:sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
2、序列化后返回的数据格式错误
TypeError: Object of type DepartSerializer is not JSON serializable
3、序列化器把选择字段转成对应的中文报错
原因:1、序列化器自己定义的字段写的不对
2、自定义了字段,在下边fields中没有使用
AssertionError: The field 'gender_name' was declared on serializer UserSerializer, but has not been included in the 'fields' option.