Django restframework学习

一、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,request.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.

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
学习 Django Rest Framework(DRF)需要掌握以下几个步骤: 1. Django 基础:首先需要了解 Django 框架的基础知识,包括 Django 的设置、路由、视图、模板等概念。 2. REST 架构:了解 REST(Representational State Transfer)架构的概念和基本原则,包括资源、路由、状态码、请求方法等。 3. DRF 安装和配置:在 Django 项目中安装 DRF,并熟悉其基本配置,包括设置路由、生成 API 文档等。 4. 创建 API 接口:使用 DRF 创建各种 API 接口,包括用户管理、博客文章管理、评论管理等。 5. 数据库操作:熟悉 Django ORM(对象关系映射)的使用,通过 ORM 进行数据库操作。 6. 过滤器和分页器:了解 DRF 的过滤器和分页器,以及如何在 API 接口中使用这些功能。 7. 安全性和认证:熟悉 DRF 的认证和安全机制,包括 Token、JWT 等认证方式,以及 CSRF 保护等安全措施。 8. 测试和调试:熟悉 DRF 的测试框架,编写测试用例并进行调试。 9. 进阶功能:了解 DRF 的进阶功能,如自定义渲染器、过滤器、视图等,以及如何使用 DRF 与其他框架集成。 学习路线可以按照以上步骤进行,具体可以参考以下建议: * 阅读 Django 和 DRF 的官方文档,了解其基本概念和用法。 * 学习 Django 中相关的第三方库,如 Django REST Auth(Django REST 的认证库)和 Django Rest Clients(用于与 API 进行交互的客户端库)。 * 通过实践案例来掌握 DRF 的使用,可以参考一些开源项目中的 API 接口,并尝试自己创建类似的接口。 * 参与社区讨论和问答平台,与其他开发者交流学习心得和解决问题。 * 参加线上或线下培训课程,获取更系统化的学习体验。 总之,学习 DRF 需要不断实践和积累经验,建议按照以上步骤逐步深入学习,并结合实际案例来加深理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值