drf序列化器中的read_only和write_only选项

  • 参考网址

https://www.cnblogs.com/jx-zzc/p/16522675.html

  • read_only: 表示只读,不能进行修改,也不会存入db(校验过后)

    • 使用场景: id 字段
  • write_only: 只能写,不能读,可以存入db,但是返回响应时,不会包含此字段

    • 使用场景: password_repeat 字段/sms_code
class RegisterSerializer(serializers.ModelSerializer):

    password2 = serializers.CharField(write_only=True, label='重复密码')
    sms_code = serializers.CharField(write_only=True, label='短信验证码')
    allow = serializers.CharField(write_only=True, label='是否同意协议')
    ......

Redis配置

  • 安装django-redis,并配置
# settings
......
# ------------redis配置:拆分为0/1 号数据库------------------#
CACHES = {
    "default": { # 保存 省市区数据
        "BACKEND": "django_redis.cache.RedisCache",
        # 使用0号数据库
        "LOCATION": "redis://192.168.11.38:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {
                "max_connections": 1000,
                "encoding": 'utf-8'
            },
            # "PASSWORD": "foobared" # redis密码
        }
    },

    "session": { # 保存session数据
        "BACKEND": "django_redis.cache.RedisCache",
        # 使用1号数据库
        "LOCATION": "redis://192.168.11.38:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {
                "max_connections": 1000,
                "encoding": 'utf-8'
            },
            # "PASSWORD": "foobared" # redis密码
        }
    },
    "verify_codes": { # 保存验证码
            "BACKEND": "django_redis.cache.RedisCache",
            # 使用2号数据库
            "LOCATION": "redis://192.168.11.38:6379/2",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
                "CONNECTION_POOL_KWARGS": {
                    "max_connections": 1000,
                    "encoding": 'utf-8'
                },
                # "PASSWORD": "foobared" # redis密码
            }
        },
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = 'session'
.....
'''
- 除了名为default的redis配置外,还补充了其他配置,例如名为session的redis配置,分别使用两个不同的redis库

- 同时修改了Django的Session机制使用redis保存,且使用名为'session'的redis配置

- 此处修改Django的Session机制存储主要是为了给Admin站点使用

- 关于django-redis 的使用,说明文档可见http://django-redis-chs.readthedocs.io/zh_CN/latest/
'''

# views demo演示
import logging

from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from django_redis import get_redis_connection

class TestView(APIView):

    def get(self,request,*args,**kwargs):
        ### 搭配 redis 管道和celery异步
        # redis_conn = get_redis_connection('default')
        # logger.info(sms_code)
        # pl = redis_conn.pipeline()
        # pl.set(mobile, sms_code, ex=60)  # 保存验证码
        # pl.set('sms_flag_{}'.format(mobile), 200, ex=60)  # 保存发送短信标志
        # pl.execute()
        # send_sms_code.delay(mobile, sms_code)  # 异步耗时任务

        ### 基础demo
        redis_conn = get_redis_connection('default')
        mobile = 18106951913
        code = 8888
        redis_conn.set(mobile,code,ex=60)

        return Response({'msg':'redis保存成功'},status=status.HTTP_200_OK)


日志

# settings
......
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,  # 是否禁用已经存在的日志器
    'formatters': {  # 日志信息显示的格式
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {  # 对日志进行过滤
        'require_debug_true': {  # django在debug模式下才输出日志
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {  # 日志处理方法
        'console': {  # 向终端中输出日志
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {  # 向文件中输出日志
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            # 'filename': os.path.join(BASE_DIR, 'logs/meiduo.log'),  # 日志文件的位置
            # 'filename': os.path.join(os.path.dirname(BASE_DIR), 'logs\\meiduo.log'),  # 日志文件的位置和manage.py同级
            'filename': os.path.join(BASE_DIR, 'logs\\meiduo.log'),  # 日志文件的位置和manage.py同级
            'maxBytes': 300 * 1024 * 1024,
            'backupCount': 10,
            'formatter': 'verbose'
        },
    },
    'loggers': {  # 日志器
        'django': {  # 定义了一个名为django的日志器
            'handlers': ['console', 'file'],  # 可以同时向终端与文件中输出日志
            'propagate': True,  # 是否继续传递日志信息
            'level': 'INFO',  # 日志器接收的最低日志级别
        },
    }
}

向DRF补充异常

  • 修改Django REST framework的默认异常处理方法,补充处理数据库异常和Redis异常
### exceptions.py
from rest_framework.views import exception_handler as drf_exception_handler
import logging
from django.db import DatabaseError
from redis.exceptions import RedisError
from rest_framework.response import Response
from rest_framework import status

# 获取在配置文件中定义的logger,用来记录日志
logger = logging.getLogger('django')

def exception_handler(exc, context):
    """
    自定义异常处理
    :param exc: 异常
    :param context: 抛出异常的上下文
    :return: Response响应对象
    """
    # 调用drf框架原生的异常处理方法
    response = drf_exception_handler(exc, context)

    if response is None:
        view = context['view']
        if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
            # 数据库异常
            logger.error('[%s] %s' % (view, exc))
            response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response

### settings
REST_FRAMEWORK = {
    # 异常处理
    'EXCEPTION_HANDLER': 'meiduo_mall.utils.exceptions.exception_handler',
}

Source参数的功能

  • 参考网址
https://zhuanlan.zhihu.com/p/274507545
  • 修改返回到前端的字段名
- 在序列化器参数字段参数设置(source='表模型字段名')

- 修改后的名字不能同字段名一样
### serializer.py
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from app01 import models

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False, ) 
    res_title = serializers.CharField(max_length=32, min_length=2,source='title') # 修改字段名称
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
    
### models.py
from django.db import models

class Publish_dateils(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=128)
    phone = models.IntegerField(max_length=16)
    emali = models.EmailField()

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=6,decimal_places=2)
    # 外键支持
    publish = models.ForeignKey(to=Publish_dateils,on_delete=models.CASCADE)
  • 如果表模型中有方法,会执行方法,并且把返回值赋值给新变量名

    作用: 可以自定义模型方法,修改原字段的值,并响应

### models.py
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=6,decimal_places=2)
    publish = models.ForeignKey(to=Publish_dateils,on_delete=models.CASCADE)

    def tesc(self):   # 表模型没有这个字段,只是一个方法,重写了title的值
        self.res_title = '我被表模型方法改变了'
        return self.res_title
        
### serializers
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False, )
    res_title = serializers.CharField(max_length=32, min_length=2, source='tesc')  # 这里指向表模型的方法
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
  • source支持跨表操作
### serializers
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False, )  
    res_title = serializers.CharField(max_length=32, min_length=2, source='tesc')
    price = serializers.DecimalField(max_digits=6, decimal_places=2,)
    res_sum = serializers.CharField(source='sum')
    # 跨表操作
    publish_name = serializers.CharField(source='publish.name')
    publish_addr = serializers.CharField(source='publish.addr')

DRF create()方法返回值解析

  • 源码是把数据进行保存以后,序列化返回保存后的数据
# 源码
class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
       ......
  • 实战中,数据保存在哪里,比如mysql或者redis,都是有开发者自己决定
  • 该方法返回什么类型的数据,也是由开发者自己决定,这个方法不像validate_field强制要求返回value
  • create()方法的返回值,就是序列化后的数据
# 注册类
 def create(self, validated_data):
        ......
        return user # 返回 用户对象

DRF项目之序列化器和视图重写方法的区别

  • 参考网址
https://www.cnblogs.com/chao666/p/12318467.html
- 在视图中重写方法: 接收请求,处理数据(业务逻辑),最后响应JSON数据

- 在序列化器中重写方法: 实现和Model的交互,主要实现对数据库的操作
  • 结论
- 如果逻辑是注重与model的交互,就把逻辑放在serilizer;注重响应的数据,就把逻辑放到'视图'

ModelViewSet 实现,,,

### urls
    from django.conf.urls import url
    from rest_framework.routers import DefaultRouter
    from . import views

    urlpatterns = [
        ......
    ]

    router = DefaultRouter()
    ......
    # 快速完成 增/删/改/查
    router.register(r'goods',views.GoodsModelViewSet,basename='goods')
    urlpatterns += router.urls	
    
### views
    class GoodsModelViewSet(ModelViewSet):
        queryset = Goods.objects.get_queryset()
        # '增'和'查'需指定不同的序列化器
        def get_serializer_class(self):
            if self.action == 'create':
                return GoodsCreateModelSerilizer
            return GoodsModelSerilizer
            
### serializers
    class GoodsModelSerilizer(serializers.ModelSerializer):

        class Meta:
            model = Goods
            fields = '__all__' # depth = 1 新增的时候,序列化器不会收到'外键字段','__all__'就不会包含外键字段
            depth = 1
    # 新增
    class GoodsCreateModelSerilizer(serializers.ModelSerializer):

        class Meta:
            model = Goods
            fields = '__all__'
    
  • (是'物理删除',如果换成'逻辑删除'就要重写'delete')和的逻辑默认即可