目录
新建项目准备
三流
标准输出流, 标准输入流, 标准错误流
'''
C:...d_projscript est.py
import sys
sys.stdout.write('123
') # 默认不换行
sys.stdout.write('456
')
# 123123
# res = sys.stdin.readline() # 类似于input, 会暂停程序
# print(res)
sys.stderr.write('789
') # 打印结果飘红, 默认不换行
sys.stderr.write('101112
')
# 异步执行, 但123与456共用一个流, 456一定在123后, 同理101112一定在789后
# C:...d_projapiexception.py
...
def exception_handler(exc, context):
...
import sys
sys.stderr.write('异常: %s
' % response.data.get('detail'))
...
'''
内部类
'''
class A:
class B: # B类的名称空间通过A类访问
x = 10
print(A.B.x) # 10
# C:...d_projapimodels.py
...
class User(models.Model):
SEX_CHOICES = (
(1, '男'), # 元组的第一个元素存入数据库的对应字段, 第二个元素对第一个元素作说明
(0, '女')
)
# verbose_name 可以修改django admin 后台显示的字段名, blank=True表示django admin后台该字段可以不填写
username = models.CharField(max_length=64, verbose_name='用户名', blank=True)
password = models.CharField(max_length=64, verbose_name='密码')
sex = models.IntegerField(choices=SEX_CHOICES, default=1, verbose_name='性别')
avatar = models.ImageField(upload_to='img', default='default.png', verbose_name='头像') # ImageField继承FileField
is_delete = models.BooleanField(default=False, verbose_name='是否注销') # 开发中数据不会直接删除, 而是通过字段控制
created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
class Meta: # 配置类, 给所属类提供配置信息
db_table = 'old_boy_user' # 修改数据库显示的表名, 默认值: 所属app_类名小写
verbose_name_plural = '用户表' # 修改django admin 后台显示的表名
def __str__(self):
return self.username # __str__方法内不能进行联表操作
# C:...d_projapiadmin.py
...
from . import models
admin.site.register(models.User) # 在django admin后台注册User表
'''
在视图类中手动完成序列化操作
'''
# C:...d_projapiviews.py
from rest_framework.views import APIView
from rest_framework.response import Response
from . import models
from d_proj import settings
class UserV1APIView(APIView):
# 单查群查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
user_dic = models.User.objects.filter(is_delete=False, pk=pk).values('username', 'sex', 'avatar').first()
if not user_dic:
return Response({'status': 1, 'msg': 'pk error', }, 400)
user_dic['avatar'] = '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, user_dic.get('avatar'))
return Response({
'status': 0,
'msg': 'ok',
'result': user_dic
})
else:
...
'''
Serializer序列化
序列化
- 后端orm操作得到数据
- 将数据序列化成可以返回给前端的数据类型
- 返回数据给前端
封装
面向对象封装
- 数据需要多个方法处理,
- 入口为类中的
__init__
方法
面向过程封装:
- 一个方法完成数据处理
'''
# C:...d_projapiviews.py
from . import serializers
from . import models
class UserV2APIView(APIView):
# 单查群查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
... # user_obj为空时, 也能处理, 结果: 所有字段为空
else:
# 将对象对外提供的字段, 以及整个序列化过程封装, 形成序列化类
user_queryset = models.User.objects.filter(is_delete=False).all()
# 根据源码, 当处理的数据中的包含多个对象时需要设置many=true
user_ser = serializers.UserSerializer(user_queryset, many=True) # 序列化后的对象
user_list = user_ser.data # 序列化后的数据
return Response({
'status': 0,
'msg': 'ok',
'result': user_list
})
# C:...d_projapiserializers.py
from rest_framework import serializers
from django.conf import settings
class UserSerializer(serializers.Serializer):
"""
1. UserSerializer类中没有写的但是model表中有的字段不会序列化, 即不会在前端展示
2. 字段名与字段类型要与对应模型表中的字段名与字段类型一致
3. 可以自定义序列化字段, 用来调整对应的数据库字段返回给前端的数据,
4. 自定义序列化字段一定不参与反序列化
4. 不建议自定义系列化字段名与数据库字段名重名
5. 通过在类中自定义方法提供给自定义字段所需的字段值, 方法名为get_自定义字段名
"""
...
sex = serializers.IntegerField() # "sex": 1,
avatar = serializers.ImageField() # "avatar": "/media/img/111.jpg",
gender = serializers.SerializerMethodField() # "icon": "http://127.0.0.1:8000/media/img/111.jpg"
icon = serializers.SerializerMethodField() # "gender": "男"
def get_gender(self, obj): # obj为参与序列化的数据中的一个对象
return obj.get_sex_display()
def get_icon(self, obj): # 在高级序列化与高级视图类中, drf默认序列化处理图片等子资源
return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, obj.avatar)
# G:...
est_frameworkserializers.py
class BaseSerializer(Field):
...
def __init__(self, instance=None, data=empty, **kwargs):
...
kwargs.pop('many', None)
...
class Serializer(BaseSerializer, ...):
... # 该类中没有__init__方法
'''
Serializer反序列化
反序列化
- 从请求对象中获取前端提交的数据
- 序列化类完成数据的反序列化及校验
- 序列化类将反序列化后的数据同步到数据库
- 将处理结果反馈给前端
'''
# C:...d_projapimodels.py
...
class User(models.Model):
...
username = models.CharField(..., unique=True) # unique=True, 数据库层次限制不能有重复的用户名
# C:...d_projapiviews.py
...
class UserV2APIView(APIView):
...
def post(self, request, *args, **kwargs):
request_data = request.data # 从请求对象中获取前端提交的数据
user_ser = serializers.UserDeSerializer(data=request_data) # 反序列化及校验
if user_ser.is_valid(): # 判断数据校验是否通过
# 根据源码, save方法会判断具体是create还是update, 但因为没有绑定model中的数据库表, 所以没有具体实现方法
user_obj = user_ser.save() # 执行save方法会判断并执行对应的数据库操作, 返回操作后得到的新的数据对象
return Response({'status': 0, 'msg': 'ok', 'result': serializers.UserSerializer(user_obj).data}, )
else:
return Response({'status': 1, 'msg': user_ser.errors, }, )
# C:...d_projapiserializers.py
from . import models
class UserDeSerializer(serializers.Serializer):
# 系统校验字段
username = serializers.CharField(min_length=3, max_length=16, error_messages={ # 自定义校验出错的报错信息
'min_length': '太短',
'max_length': '太长'
})
...
# required=False的字段前端不提供则使用model表指定的默认值, 提供了则进行反序列化及校验
sex = serializers.BooleanField(required=False) # required参数默认值为True
# 自定义校验字段, 语法与系统字段没有区别, 但是需要在全钩子中将其取出, 不保存到数据库,
re_password = serializers.CharField(min_length=3, max_length=16)
# 局部钩子: 方法名为validate_校验的字段名
def validate_username(self, value): # values为通过系统校验后的对应字段值
# print(value) # egon
if 'g' in value:
raise serializers.ValidationError('用户名不能包含g')
return value # 字段值符合局部钩子的校验规则, 则将其返回
# 全局钩子: 通常用来进行多个字段的校验
def validate(self, attrs): # attrs为通过系统校验后的所有字段及字段值
# print(attrs) # OrderedDict([('username', 'nick'), ...])
password = attrs.get('password')
re_password = attrs.pop('re_password') # 获取并去除自定义校验字段
if password != re_password:
raise serializers.ValidationError({'re_password': '两次密码不一致'}) # key指定错误的字段
return attrs # 所有数据符合全局钩子的校验规则, 则将其返回
def create(self, validated_data):
return models.User.objects.create(**validated_data)
def update(self, instance: models.User, validated_data): # instance为要被修改的数据对象, validated_data为校验后用来修改instance的数据
models.User.objects.filter(pk=instance.id).update() # update方法的返回值为受影响的行
return instance
'''
ModerSerializer序列化
'''
# C:...d_projapiserializers.py
# ModelSerializer自动处理文件对象的完整路径拼接
class UserModelSerializer(serializers.ModelSerializer):
# # 自定义序列化字段的方式一, 但是定义了不序列化会报错, 无法实现插拔式
# gender = serializers.SerializerMethodField()
# def get_gender(self, obj):
# return obj.get_sex_display()
class Meta: # 配置类
model = models.User # UserModelSerializer要操作的表
fields = ('username', 'gender', 'icon') # 要序列化的字段, 元组形式
# C:...d_projapimodels.py
....
class User(models.Model):
...
@property
def gender(self): # 自定义序列化字段的方式二, 不仅可以实现插拔式设计而且可以联表操作
return self.get_sex_display()
@property
def icon(self):
from django.conf import settings
return '%s%s%s' % (settings.BASE_URL, settings.MEDIA_URL, self.avatar)
# C:...d_projapiviews.py
class UserV3APIView(APIView):
# 单查群查
def get(self, request, *args, **kwargs):
...
user_ser = serializers.UserModelSerializer(user_obj, many=False) # 单查序列化
...
# 根据源码, 当处理的数据中的包含多个对象时需要设置many=true
user_ser = serializers.UserModelSerializer(user_queryset, many=True) # 群查序列化
...
'''
ModerSerializer反序列化
'''
C:...d_projapiserializers.py
# 根据源码UserModelSerializer对create和update进行了具体实现, 并且支持所有关系下的联表操作
class UserModelSerializer(serializers.ModelSerializer):
...
# 自定义反序列化校验字段, 同Serializer, 且校验规则要么在定义时设置, 要么在钩子中设置, 在extra_kwargs中设置的无效
re_password = serializers.CharField(min_length=3, max_length=16, write_only=True) # 自定义反序列化字段必须设置write_only
class Meta:
model = models.User # UserModelSerializer要操作的表
fields = ('username', 'gender', 'icon', 'password', 'sex', 're_password') # UserModelSerializer要操作的字段, 元组形式
extra_kwargs = { # 设置额外参数将反序列化与序列化区分开来
'username': { # 系统字段如果不设置, 默认都参与
'min_length': 3,
'max_length': 10,
},
'password': { # 只参与反序列化
'write_only': True,
'min_length': 3,
'max_length': 16,
},
'gender': {'read_only': True}, # 自定义的序列化字段默认就是read_only, 且不能修改, 但可以省略
'sex': { # 有默认值的字段为选填字段, 'required': True可以设置为必填字段
'write_only': True,
# 'required': True,
},
}
# 局部钩子和全局钩子同Serializer, 和Meta同缩进
...
# C:...d_projapiviews.py
class UserV3APIView(APIView):
...
def post(self, request, *args, **kwargs):
...
user_ser = serializers.UserModelSerializer(data=request_data) # ModelSerializer反序列化及校验
user_ser.is_valid(raise_exception=True) # 校验不通过时, 系统会自动抛错
...
'''