代码拉取完成,页面将自动刷新
对于Django、DRF的一些框架功能提示:
1、模型相关
字段的常用、通用Option
null = True # 该字段允许值为空,也就是MySQL上的NULL,默认为False
blank = True # 允许键为空,指定的字段可以不传,注意,这与null不同。null纯属数据库相关,而blank则与验证相关。默认为False
choices = ((0,'男'),('1','女'),) # 选项类型,类似于枚举类型,会被模型验证,如果传入的值不在选项中会引发验证异常。
default = 1 # 指定默认值,可以是一个值或者是个可调用的对象,如果是个可调用对象,每次实例化模型时都会调用该对象。
db_index=True # 为字段设置普通索引
verbose_name = '描述' # 指定label 或字段描述,相当于MySQL中字段的备注。
validators = [] # 接收一个验证器列表,用于对指定字段传入数据的验证。
db_column = 'test' # 指定这个字段要使用的数据库列名,如果没有给出,将使用字段名。
primary_key = False # 指定字段为主键,通常用不到,使用内置的ID作为主键即可。
unique = True # 指定字段唯一性约束也是唯一索引,通常会使用联合唯一,因此这个也不常用。
editable = True # 如果为False表示字段不会被修改,因为会被模型验证跳过。默认为True
常用模型字段类型:
CharField # 字符串类型,存在一个特殊的必传参数:
max_length = 255 # 指定字段容量,无论中英文值得都是字符总长度,必须要指定,并且最大为255。
那么需要更大一点但是又不需要使用大字段时怎么办呢?其实就是使用TextField设置max_length而已
TextField # 大文本类型,这个字段比较特殊,当你设置了max_length,并且没有超过65535个字节时,它实际在数据库使用的varchar类型。
JSONField # 存储json字符串类型,Django3.1开始新增的的一个类型,本质上是字符串类型。
DateField # 日期类型,存在两个特殊参数
auto_now = False # 每次保存对象时,自动将该字段设置为现在。对于 最后修改 的时间戳很有用。默认为False
auto_now_add = False # 当第一次创建对象时,自动将该字段设置为现在。对创建时间戳很有用。默认为False
注:目前 将 auto_now 或 auto_now_add 设置为 True,将导致该字段设置为 editable=False 和 blank=True。
auto_now_add、auto_now 和 default 选项是相互排斥的。这些选项的任何组合都会导致错误。
DateTimeField # 时间类型,同样支持 auto_now_add、auto_now
TimeField # 时间类型,同样支持 auto_now_add、auto_now
IntegerField # 一个整数类型,32位的,取值 -2147483648 到 2147483647
BigIntegerField # 一个大整数,64位的,取值 -9223372036854775808 到 9223372036854775807
BooleanField # bool类型,注:建议定义默认值,否则会导致null为True,就是允许为NULL
DecimalField # 一个固定精度的十进制数 可以指定长度和小数位数,存在两个必传参数:
max_digits = 15 # 指定这个数的最大长度,包括小数位在内
decimal_places = 2 # 指定小数点后小数位数
EmailField # 邮箱类型,本质上是字符串,好处是会自动进行邮箱格式验证。
FloatField # 浮点数类型
UUIDField # 一个用于存储唯一UUID的类型,可以通过default=uuid.uuid1 来赋默认值。
例如想指定主键为UUID:id = UUIDField(primary_key=True, default=uuid.uuid1, editable=False)
字段关系
ForeignKey(OtherModel, on_delete=PROTECT, verbose_name='label', related_name='related_name') # 外键类型
OneToOneField(OtherModel, on_delete=PROTECT, verbose_name='label', related_name='related_name') # 一对一的外键类型
ManyToManyField(OtherModel, verbose_name='label', blank=True, related_name='related_name') # 多对多类型,django会维护一个中间表。如果需求更多,可以自行使用中间表实现
related_name 的作用是将被外键关联的数据,通过related_name定义的命令来访问所有关联到它的数据,并且要求这个字段对于被关联表是唯一的。
on_delete属性选项的意义:
PROTECT:当外键数据删除时,指向外键的的数据不删除。
CASCADE:当外键数据删除时,指向外键的数据一起删除。
SET_NULL:当外键数据删除时,指向外键的数据该列设置为null。
SET_DEFAULT:当外键数据删除时,指向外键的数据该列恢复默认值。
SET(自定义方法):当外键数据删除时,指向外键的数据使用自定义方法来设置值。具体可参考官方文档。
模型元选项,也称Meta选项
abstract = True # 表示该模型是抽象基类,不能作为实际的模型,用于其他模型继承这个模型。
app_label = 'myapp' # 声明模型所属的app,可以不写,会默认获取。
db_table = 'Table_Name' # 自定义数据表在数据库中的名称
verbose_name = 'A表' # 自定义数据表的表述,官方叫 对象的人类可读名称 单数形式
verbose_name_plural = verbose_name # 对verbose_name的复数描述
ordering = ['-create_date'] # 指定数据的默认排序,该例表示根据创建时间倒序(不加-为升序)。建议不写,默认是主键升序。
unique_together = [['name1', 'name2']] # 设置联合唯一性约束、联合唯一索引,可以设置多个,为了方便起见,unique_together在处理一组字段时可以是一个一维列表。
index_together = [['name1', 'name2']] # 设置联合普通索引,可以设置多个,为了方便起见,index_together在处理一组字段时可以是一个一维列表。
indexes = [
models.Index(fields=['last_name', 'first_name']),
models.Index(fields=['first_name'], name='first_name_idx'),
] # 定义索引列表 索引列可以统一在这里定义
constraints = [
models.CheckConstraint(check=models.Q(age__gte=18), name='age_gte_18'), # 假设存在一个age字段,这个约束就是保证年龄必须大等于18
] # 定义模型的约束列表
2、ORM相关
ORM操作指南
由于本项目使用了假删除(或称软删除),因此在实际使用上会多出一些功能。
objects 返回没有删除的所有数据
all_objects 返回所有数据,包括已删除的数据
delete 删除数据,假删
undelete 恢复假删数据 意味着之前获取的实例是通过 all_objects 获取的
hard_delete 硬删除,数据会真的从数据表中删除
假设有一个 User 模型类如下示:
class User(models.Model):
gender_choice = (
('privacy', '保密'),
('male', '男'),
('female', '女')
)
name = models.CharField(max_length=32, verbose_name='用户账号')
mobile = models.CharField(max_length=12, verbose_name='用户手机号')
email = models.EmailField(default='', null=True, verbose_name='用户邮箱')
gender = models.CharField(max_length=16, choices=gender_choice, default='privacy', verbose_name='性别')
class Meta:
db_table = 'user'
verbose_name = '用户表'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['name']),
]
unique_together = [
['mobile'],
]
新增数据:
方法一:
user = User(name='asd', mobile='123').save()
方法二:
User.objects.create(name='asd', mobile='123')
方法三:
users = [User(name='asd', mobile='123'), User(name='qwe', mobile='345')]
User.objects.bulk_create(users) # 批量创建
理解QuerySet:
在Django中模型建立后,可以通过ORM和数据库交互,其中最重要的一个就是QuerySet对象,它包含在模型中,通过模型类的 Manager 构建一个 QuerySet。
一个 QuerySet 代表来自数据库中对象的一个集合。它可以有 0 个,1 个或者多个 filters. Filters,可以根据给定参数缩小查询结果量。
简单来说QuerySet就是专门用来通过ORM查询数据库数据,并且返回后会映射为你定义好的模型实例。
QuerySet支持链式调用,也就意味着你得到一个QuerySet对象后,可以返回的通过链式写法调用不同的QuerySet方法。
最简单的 例如 User.objects.all() 返回了一个 QuerySet。示例一个链式调用 User.objects.filter().all()
详细的QuerySet文档地址:https://docs.djangoproject.com/zh-hans/3.2/ref/models/querysets/#filter
主要的QuerySet方法释义:
filter(*args, **kwargs) 用于传递参数做where查询,多个字段为and
all() 用于返回所有数据
excluede(*args, **kwargs) 用于查询不匹配的数据
order_by(*fields) 用于对传入字段进行排序
reverse() 用于翻转查询得到的数据集
distinct(*fields) 用于查询去重,可以指定比较哪些字段,不传表示比较所有字段
union(*other_qs, all=False) 用于组合两个或多个 QuerySet 的结果 例如 qs1.union(qs2, qs3)
values(*fields, **expressions) 用于查询指定的字段,会得到一个字典数据集
raw(raw_query, params=(), translations=None, using=None) 用于执行原生SQL语句
select_for_update(nowait=False, skip_locked=False, of=(), no_key=False) 用于加排它锁查,例如 User.objects.select_for_update().filter(id=1).first()
查找数据:
User.objects.all() # 查询所有数据
User.objects.filter(id=1).exists() # 查询是否存在数据,如果 QuerySet 包含任何结果,则返回 True,如果不包含,则返回 False。
User.objects.filter(id=1).first() # 得到一个对象,如果没有返回None
User.objects.filter(id=1).last() # 查询最后一个数据
User.objects.get(id=1) # 得到一个对象,如果没有会抛出 Entry.DoesNotExist 异常
User.objects.filter(id__gte=1) # 查询id大等于1的数据 __gt 大于
User.objects.filter(id__lte=1) # 查询id小等于1的数据 __lt 小于
User.objects.filter(id__in=[1,2,3]) # 查询id指定区间的数据
User.objects.exclude(id=10) # 查询id不等于10的数据
User.objects.filter(name='test').exclude(id=10) # 查询name为test但id不等于10的数据,ORM支持链式调用 并且exclude内同样支持范围查询
User.objects.order_by('date') # 对结果集进行排序,使用date字段升序,-date表示倒序
User.objects.count() # 返回数据的总数
User.objects.filter(name__icontains='文') # 查询用户名字里面包含 文
__iexact 精确等于 忽略大小写 ilike 'aaa'
__contains 包含 like '%aaa%'
__icontains 包含 忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains
User.objects.filter(username__isnull=True) # 查询用户名为空的用户
User.objects.exclude(username__isnull=True) # 查询用户名不为空的用户
User.objects.filter(name='test', statu='1') # 表示与查询,名称为test且status为1的数据
User.objects.filter(Q(state=0) | Q(state=1)) # 利用django.db.models.Q实现或查询
User.objects.values('id', 'name').filter(id=1).first() # 只查询数据的id、name字段,返回不是模型实例,而是一个类似字典的结构,可以通过key获取数据
User.objects.values('id', 'name').filter(id=1)# 只查询数据的id、name字段,返回一个列表,列表内元素是类似字典的结构
更详细的字段查找参考:https://docs.djangoproject.com/zh-hans/3.2/ref/models/querysets/#field-lookups
更详细的聚合函数参考:https://docs.djangoproject.com/zh-hans/3.2/ref/models/querysets/#aggregation-functions
修改数据:
方法一:
user = User.objects.filter(id=1).first() # 得到一个对象,如果没有返回None
user.name = 'zxc'
user.save()
方法二:
User.objects.filter(id=1).update(name='zxc') # 这个可以批量更新一批数据,但是有个缺点是无法触发signals中的信号,如果需要触发信号需要使用save
官方对QuerySet.update的说法
QuerySet.update() 用于保存更改,所以这比遍历模型列表并对每个模型调用 save() 更有效,但它有一些注意事项:
你不能更新模型的主键。
每个模型的 save() 方法没有被调用,而且 pre_save 和 post_save 信号没有被发送。
如果更新大量行中的大量列,生成的 SQL 可能非常大。通过指定一个合适的 batch_size 来避免这种情况。
更新定义在多表继承祖先上的字段将给每个祖先带来额外的查询。
当单个批处理包含重复项时,该批处理中只有第一个实例会产生更新。
方法三:批量更新
users = User.objects.all()
users[0].name = 'test1'
users[1].name = 'test2'
Entry.objects.bulk_update(users, ['name']) # 比逐个更新性能更好
删除数据:
方法一:
user = User.objects.filter(id=1).first()
user.delete()
方法二:
User.objects.filter(id__gte=1).delete() # 批量删除一批数据
F对象的使用:
class Test(models.Model):
all_num= models.IntegerField(default=0, verbose_name='总数量 ')
have_num= models.IntegerField(default=0, verbose_name='已有数量')
class Meta:
db_table = 'A_Test_Table'
verbose_name = '测试表'
verbose_name_plural = verbose_name
要实现SQL语句:select * from a_test_tablewhere all_num > have_num
from django.db.models import F
test= Test.objects.filter(all_num__gt=F('have_num'))
执行原始SQL语句:
方法一:
Manager.raw(raw_query, params=(), translations=None)
该方法接受一个原生 SQL 查询语句,执行它,并返回一个 django.db.models.query.RawQuerySet 实例。这个 RawQuerySet 能像普通的 QuerySet 一样被迭代获取对象实例。
User.objects.raw("select * from user") 等价于 User.objects.all()
User.objects.raw("select * from user")[0] 等价于 User.objects.first()
users = Person.objects.raw('SELECT *, age(birth_date) AS age FROM user') # 支持别名
for u in users:
print("%s is %s." % (u.first_name, u.age)) # 支持别名
Person.objects.raw('SELECT * FROM user WHERE name = %s', ['lname']) # 支持传递参数
方法二:有时候,甚至 Manager.raw() 都无法满足需求:你可能要执行不明确映射至模型的查询语句,或者就是直接执行 UPDATE, INSERT 或 DELETE 语句。
from django.db import connection
def my_custom_sql(self):
with connection.cursor() as cursor:
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
return row
注意,要避免 SQL 注入,你绝对不能在 SQL 字符串中用引号包裹 %s 占位符。
注意,若要在查询中包含文本的百分号,你需要在传入参数使用两个百分号:
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])
显式的使用事务
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
from django.db import transaction
def viewfunc(request):
# This code executes in autocommit mode (Django's default).
do_stuff()
with transaction.atomic():
# This code executes inside a transaction.
do_more_stuff()
3、序列化器相关
序列化器字段的通用、常用Option
read_only = True # 表示字段只读,默认为False
write_only = True # 表示字段只写,默认为False
required = True # 表示字段必传,会经过序列化器验证,默认为True
default = None # 表示字段的默认值,需要传入一个明确的值或可调用的对象
allow_blank = False # 表示字段允许传入空值,包括None和空字符串,也可不传
label = "描述" # 用于对字段做描述,短文本描述
help_text = "长文本描述" # 和label的区别就是这个常用于长文本描述
error_messages = {
} # 接收一个异常信息的提示,错误消息的错误代码字典。
validators = [
] # 接收一个验证器列表,用对字段做特殊验证
序列化器常用字段类型
BooleanField() # bool类型
CharField() # 字符串类型,存在多个特殊选项
max_length = 255# 用于指定输入字符串的最大长度,可选
min_length = 10 # 用于指定输入字符串的最小长度,可选
trim_whitespace = True # 为真会自动去除字符串首位空格,默认为True
EmailField() # 邮件类型,本质上是字符串类型,使用它的好处是带有邮箱格式验证。同样支持 max_length min_length选项
IntegerField() # int类型,存在两个特殊选项
max_value = 1000 # 验证提供的数字不大于此值。
min_value = 1 # 验证提供的数字不小于此值。
FloatField(max_value=None, min_value=None) # float类型,同样支持 max_value min_value
DecimalField() # 一个固定精度的十进制数,存在多个选项
max_digits # 数字中允许的最大位数。它必须是None或大于或等于 的整数decimal_places。
decimal_places # 与数字一起存储的小数位数。
coerce_to_string # 设置为True是否应为表示返回字符串值,或者False是否Decimal应返回对象。COERCE_DECIMAL_TO_STRING默认为与设置键相同的值,True除非被覆盖。如果Decimal序列化器返回对象,则最终输出格式将由渲染器确定。请注意,设置localize会将值强制为True。
max_value # 验证提供的数字不大于此值。
min_value # 验证提供的数字不小于此值。
localize # 设置为True启用基于当前语言环境的输入和输出本地化。这也将coerce_to_string迫使True。默认为False. 请注意,如果您USE_L10N=True在设置文件中进行了设置,则会启用数据格式。
rounding # 设置量化为配置精度时使用的舍入模式。有效值为decimal模块舍入模式。默认为None.
DateTimeField() # 时间类型,支持多个特殊参数
format = "%Y-%m-%d %H:%:M:%S" # 用于指定输出时间的格式
input_format = ["%Y-%m-%d %H:%:M:%S", ] # 用于指定输入时间的格式字符串列表
DateField() # 日期格式,同样支持 format input_format
ChoiceField() # 选择类型或枚举类型,只能选择一个
choices = ((1, 'A'), (2, 'B')) # 有效值列表或(key, display_name)元组列表。
allow_blank = Fasle # 如果设置为,True则空字符串应被视为有效值。如果设置为,False则空字符串被视为无效并会引发验证错误。默认为False.
MultipleChoiceField() # 多选类型,和选择类型类似,区别在于这个允许选择多个选项。
ListField(child=IntegerField(min_value=0, max_value=100)) # 复合类型,用于验证数组类型数据,并且支持对数组内元素验证
child # 应用于验证列表中的对象的字段实例。如果未提供此参数,则不会验证列表中的对象。
allow_empty # 指定是否允许空列表。
min_length # 验证列表包含不少于此数量的元素。
max_length # 验证列表包含不超过此数量的元素。
DictField(child=<A_FIELD_INSTANCE>, allow_empty=True) DictField(child=CharField()) # 复合类型,用于类似于字典的数据,会验证key和value
child # 应该用于验证字典中的值的字段实例。如果未提供此参数,则不会验证映射中的值。
allow_empty # 指定是否允许空字典。
JSONField() # 复合类型,用于验证json数据,实际中常用的是通过多个序列化器的组合来实现,因为那样更友好、便捷
binary # 如果设置为,True则该字段将输出并验证 JSON 编码字符串,而不是原始数据结构。默认为False.
encoder # 使用此 JSON 编码器序列化输入对象。默认为None.
特殊的字段类型
HiddenField() # 隐藏字段,隐藏后验证器不可见、不可修改,例如 serializers.HiddenField(default=timezone.now) 一般情况会给默认值用于入库的数据初始化
它不根据用户输入获取值,而是从默认值或可调用值中获取值。
通常仅当HiddenField您需要基于某些预先提供的字段值运行一些验证时才需要该类,但您不希望将所有这些字段公开给最终用户。
注意:被HiddenField处理的字段,将会对前端无感,前端在增删改查时都将无感该字段
注意:HiddenField 工作在创建数据时,只有POST时才有效
注意:如果对同一个字段使用HiddenField和read_only_fields,只有HiddenField有效
SerializerMethodField(method_name=None) # 序列化器方法字段,这个字段用于序列化器返回数据时,可以对某些字段进行加工后再返回,可以用来追加返回的字段。
method_name # 指定利用的函数名称,如果不传,则默认为get_<field_name>.
示例 new_name = SerializerMethodField() 此时需要在对应的类中存在一个名为get_new_name的方法,并且返回了合适的值。
注意:SerializerMethodField 只在返回数据时有效,也就是工作在 GET 请求的场景。
基于模型的序列化器元类选项Option
model = User # 用于指定模型
fields = '__all__' or ('id', 'name') # 用于指定哪些字段可以操作,__all__表示所有字段
注意:如果启用了fields,并且存在SerializerMethodField,要保证SerializerMethodField定义的字段包含在fields中
exclude = ('id', ) # 表示禁止序列化的字段,和fields左右是相反的。直接无视该字段的增删改查,一般用于字段被数据库或框架管理,例如是否被删除的标志字段。
注意:模型序列化器中 fields和exclude必须选择其中之一
read_only_fields = ('age', ) # 表示只读的字段,和字段中read_only=True效果一样,好处时可以统一设定只读字段。
注意:已editable=False设置的模型字段,和AutoField字段默认设置为只读,不需要添加到read_only_fields选项中。
注意:只读的字段可以在后头validate中用代码赋值。
depth = 2 # 表示对外键和嵌套关系的处理,例如有外键指向别的模型或别的模型指向自己,可通过depth快速进行层次序列化,缺点是无法指定字段,造成浪费和无法控制
因此建议自行实现管理数据的序列化。后面会给出实例
4、信号
什么是信号:
Django有一个 信号调度器(signal dispatcher),用来帮助解耦的应用获知框架内任何其他地方发生了操作。
简单地说,信号允许某些 发送器 去通知一组 接收器 某些操作发生了。当许多代码段都可能对同一事件感兴趣时,信号特别有用。
Django 提供了 内置信号集 使用户代码能够获得 Django 自身某些操作的通知。其中包括一些有用的通知:
django.db.models.signals.pre_save & django.db.models.signals.post_save
一个模型的 save() 方法被调用之前或之后发出。
django.db.models.signals.pre_delete & django.db.models.signals.post_delete
一个模型的 delete() 方法或查询结果集的 delete() 方法被调用之前或之后发出。
django.db.models.signals.m2m_changed
一个模型的 ManyToManyField 更改后发出。
django.core.signals.request_started & django.core.signals.request_finished
Django 发起或结束一个 HTTP 请求后发出。
详细的信号文档参考:https://docs.djangoproject.com/zh-hans/3.2/topics/signals/
内置信号文档:https://docs.djangoproject.com/zh-hans/3.2/ref/signals/
定义和发送信号:https://docs.djangoproject.com/zh-hans/3.2/topics/signals/#defining-and-sending-signals
5、如何使用自定义缓存
什么是自定义缓存:
本项目实现了一个动态缓存装饰器,可以用来装饰视图函数,并写了一个基于缓存的viewset类,可以实现接口完全基于缓存,获取数据时只有首次是查库,剩余的都是存在缓存中,只有发生变更时才会更新缓存。
实现方案:
1、对所有请求加锁,并且进行读写分离加锁,互斥锁,当有读请求时,会阻塞写操作,同读不阻塞。
2、第一次请求后,会根据请求的参数生成一个指纹,会将这个请求缓存起来,直到发生写操作才将缓存失效,失效后,当新的查询请求结束后会设置上新的缓存。如此往复。
3、对于CRUD操作,可以直接继承内置的MyViewSet来实现。对于自定义的接口,可以通过缓存装饰器来实现。
# 针对自定义缓存的示例:
from extensions.MyCacheViewset import MyModelViewSet
class UserViewSet(MyModelViewSet): # 基于自定义缓存的ViewSet类
'''
partial_update: 更新指定ID的用户,局部更新
create: 创建用户
retrieve: 检索指定ID的用户
update: 更新指定ID的用户
destroy: 删除指定ID的用户
list: 获取用户列表
'''
queryset = User.objects.filter()
serializer_class = UserViewsetSerializer
authentication_classes = (JwtAuthentication, )
permission_classes = (AllowAny, )
throttle_classes = (VisitThrottle, )
pagination_class = Pagination
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
search_fields = ('name', 'desc') # 注意 要针对有索引的字段进行搜索
# filterset_fields = ('status', )
ordering_fields = ('id', 'create_timestamp', 'update_timestamp', 'sort_timestamp')
is_public = True # 针对自定义缓存的一个配置项,如果为真表示这个是公共接口不会根据用户id来设置缓存,否则会根据用户id来设置缓存key
def get_serializer_class(self):
if self.action == 'create':
return CreateUserViewsetSerializer
if self.action in {'update', 'partial_update'}:
return UpdateUserViewsetSerializer
return ReturnUserViewsetSerializer
class GetAllEnumDataView(GenericAPIView):
authentication_classes = (JwtAuthentication, )
permission_classes = (AllowAny, )
throttle_classes = (VisitThrottle, )
@swagger_auto_schema(responses={200: GetAllEnumDataResponse})
@RedisCacheForDecoratorV1('r')
def get(self, request):
'''针对自定义的视图方法使用自定义的缓存装饰器类'''
res = MyJsonResponse(res_data={'msg': gettext_lazy('测试成功')})
pass
return res.data
# 针对序列化器的详细示例:
from rest_framework import serializers
def get_ObjectFlow(type):
'''
返回对象
'''
if type == 0:
approval_flow = TableClass.objects.filter(flow_name='请假审批').first()
elif type == 1:
approval_flow = TableClass.objects.filter(flow_name='出差审批').first()
else:
return None
if approval_flow:
object_flow = TableClass()
return object_flow
else:
return None
class CurrentUser(object):
'''
返回指定的值
使用该方法可以做一些后端数据自动计算的工作
'''
def set_context(self, serializer_field):
self.user_obj = serializer_field.context['request'].user
def __call__(self):
return self.user_obj
class AddUserSerializer(serializers.ModelSerializer):
# 在源头上防止出现重复数据
name = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False,
validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")])
# 防止出现重复数据的同时,对数据进行验证
mobile = serializers.CharField(label="手机号", help_text="手机号", required=True, allow_blank=False,
validators=[UniqueValidator(queryset=User.all_objects.all(), message="手机号已经存在")],
error_messages={"required": '手机号不能为空', 'blank': '手机号不能为空', 'null': '手机号不能为空'})
# 将一个外键设为只读,防止前端传入值进来修改
object_flow = serializers.PrimaryKeyRelatedField(read_only=True)
# 在执行新增操作的时候,在前端隐藏user的传入,后端默认使用当前用户插入
# 第二种方法 user = serializers.HiddenField(default=serializers.CurrentUserDefault(), label='用户') 其实就是restframwork实现了CurrentUser
user = serializers.HiddenField(default=CurrentUser())
# 指定这个字段在返回给前端之前,也就是序列化时 可以按照指定的函数进行序列化
order_int = serializers.SerializerMethodField(label='订单数')
# 当有模型的外键指向当前模型时,可以通过related_name反向插入所有关联的子数据 接收的子的Serializer
oilcan_datas = DepotUseOilcanDataSerializer(read_only=True, many=True) # 通过related_name和模型的序列化器来返回序列化的反向数据集,也可以通过函数字段自行实现,父找子
# 将对应的外键通过 父Serializer序列化后返回给前端
depart = ReturnDepartSerializer() # 通过外键和外键模型的序列化器来返回序列化的外键数据,也可以通过函数字段自行实现,子找父
# 用于指定日期的输入格式、返回格式,注意可以在settings中统一设置后,可以省略这里的代码
start_date = serializers.DateTimeField(label='开始日期', format='%Y-%m-%d %H:%M:%S', input_formats=['%Y-%m-%d %H:%M:%S', '%Y-%m-%d'], required=False, allow_null=True)
class Meta:
model = User
fields = '__all__' # __all__用于序列化所有字段 or exclude = ('deleted',)指定,某些字段不被序列化返回 or fields = ['company','username',]指定序列化的字段
read_only_fields = ('user', 'object_flow',) # 指定只读的字段,这样就不会被前端修改
validators = [UniqueTogetherValidator(queryset=Auth.objects.all(), fields=['auth_type',], message='该权限已经存在')] # 多字段联合唯一
def validate(self, attrs):
# 重写验证器
now_user = self.context['request'].user
# 查看前端传来的所有数据
print('查看attrs:', attrs)
# 查看前端是否有通过pk检索数据 来做出相应的改变
print('查看pk:', self.context['view'].kwargs.get('pk'))
# 在这里可以获取body参数
print('查看body参数:', self.context['request'].data)
flow_obj = get_ObjectFlow(0)
if not flow_obj:
raise serializers.ValidationError("审批流不存在")
attrs['object_flow'] = flow_obj
return attrs
def validate_name(self, value):
try:
# 用于独立验证name字段
# do something
return value
except Exception as e:
raise serializers.ValidationError(e)
@transaction.atomic
def update(self, instance, validated_data):
# 重写更新方法
obj = super().update(instance, validated_data)
# do something
return obj
@transaction.atomic
def create(self, validated_data):
# 重写创建方法
obj = super().create(validated_data)
# do something
return obj
def get_order_int(self, obj):
# 这种方法只会在返回时被调用
# 在这里可以通过GET参数 做一些返回操作 注意:只有在使用viewset里面才有效
print('查看GET参数:', self.context['request'].query_params)
num = 0
return num
总结:
1、隐藏字段和只读字段的区别
隐藏字段会将字段完全隐藏,可以设置默认值,但是默认值前端不可见,例如在创建数据时 创建者 字段是一个隐藏字段,默认值是当前调用者,此时数据创建成功后,该字段也不会返回给用户。
只读字段会禁止前端传入数据给字段赋值,赋值可以在后台完成,这样的好处是被操作的字段可以在数据创建成功后返回给用户。
隐藏字段只在创建数据时有效,只读字段在数据创建和修改时都有效。
2、前端新增或修改时 即不需要设置也不需要返回 使用 exclude 包含进去
前端新增时 即不需要设置也不需要返回 但需要后端设置值的 使用 HiddenField 因为 HiddenField 只在新增时有效
前端新增或修改时 不需要设置但需要返回的 使用 read_only_fields 包含进去
2、单独验证字段和在validate中统一验证的区别
单独验证字段,可以进行同时验证,被验证的字段会被统一返回验证信息
在validate中统一验证,存在先后顺序,先验证的字段会触发异常,并返回验证信息
# 针对ViewSet的相关示例:
from rest_framework.viewsets import ModelViewSet
class UserViewSet(ModelViewSet):
'''
更新指定ID的用户,局部更新
create: 创建用户
retrieve: 检索指定ID的用户
update: 更新指定ID的用户
destroy: 删除指定ID的用户
list: 获取用户列表
'''
# 指定使用的QuerySet
queryset = User.objects.filter()
# 指定使用的序列化器
serializer_class = UserViewsetSerializer
# 指定使用的认证类
authentication_classes = (JwtAuthentication, )
# 指定使用的权限类
permission_classes = (AllowAny, )
# 指定使用的频率限制类
throttle_classes = (VisitThrottle, )
# 指定使用的分页类
pagination_class = Pagination
# 指定搜索、过滤、排序使用的类
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
# 指定支持搜索的字段
search_fields = ('name', 'desc') # 注意 尽量针对有索引的字段进行搜索
# 指定支持过滤的字段,搜索和过滤的区别:搜索是部分匹配,过滤是全匹配
filterset_fields = ('status', )
# filterset_class = YourFilter # 如果使用了filter类的写法,和filterset_fields只能存在其中一个
# 指定支持排序的字段,注意 尽量使用有索引的字段进行排序
ordering_fields = ('id', 'create_timestamp', 'update_timestamp', 'sort_timestamp')
def get_serializer_class(self):
'''重写返回序列化的方法,可以根据自己的需求动态的返回序列化器类'''
if self.action in ['create']:
return AddUserSerializer
if self.action in ['update', 'partial_update']:
return UpdateUserSerializer
return ReturnUserSerializer
# 针对Swagger可以指定描述和tag,可以提现在Swagger文档中
@swagger_auto_schema(operation_description="创建用户", operation_summary="创建用户")
def create(self, request, *args, **kwargs):
'''重写创建对象的类,可以在这里进行自定义,一般这里需要自定义的逻辑可以转移到序列化中,重写序列化器的创建方法'''
obj = super().create(request, *args, **kwargs)
return obj
def get_queryset(self):
'''重写get_queryset,这样就可以根据不同的情况返回不同的QuerySet'''
if self.request.auth:
return User.objects.filter(id=self.request.user.id)
return None
def list(self, request, *args, **kwargs):
'''重写返回数据方法,可以自定义实现,本例使接口返回的数据是一个对象而不是数组'''
instance = User.objects.filter(id=self.request.user.id).first()
serializer = self.get_serializer(instance)
return Response(serializer.data)
def destroy(self, request, *args, **kwargs):
'''重写删除数据的方法,在这里可以实现自己的逻辑,比如删除数据时校验是否支持删除'''
res = super().destroy(request, *args, **kwargs)
return res
def update(self, request, *args, **kwargs):
'''重写更新数据的方法,一般这里需要自定义的逻辑可以转移到序列化中,重写序列化器的更新方法'''
res = super().update(request, *args, **kwargs)
return res
ModelViewSet本质上就是
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
因此如果你的接口可以按需继承不同的类,来实现需要的请求方式。
例如现在需要一个接口通过用户的token返回用户信息,就可以这么写:
class OwnerUserInfoViewset(mixins.ListModelMixin, GenericViewSet):
'''
list: 获取自己的用户信息
'''
serializer_class = OwnerUserViewsetSerializer
authentication_classes = (JwtAuthentication, )
permission_classes = (IsAuthPermission, )
throttle_classes = (VisitThrottle, )
def get_queryset(self):
'''重写get_queryset,用来返回目标用户的数据,因为在token验证那里已经确定了用户是否存在'''
if self.request.auth:
return User.objects.filter(id=self.request.user.id)
return None
def list(self, request, *args, **kwargs):
'''重写list方法,使接口返回的数据是一个对象而不是数组'''
instance = User.objects.filter(id=self.request.user.id).first()
serializer = self.get_serializer(instance)
return Response(serializer.data)
# 针对过滤类的相关示例:
# 详细的官方文档:https://django-filter.readthedocs.io/en/main/
from django_filters.rest_framework import FilterSet, CharFilter
from django.db.models import Q
from django.db import transaction
class MyFilter(FilterSet):
'''
过滤类
'''
my_type = CharFilter(field_name='my_type', method='return_my_type', label='自定义类型过滤')
filter_date = CharFilter(field_name='filter_date', method='return_filter_date', label='自定义时间段过滤')
class Meta:
model = YourModel
fields = ['status', ] # 原本支持的过滤字段
def return_my_type(self, queryset, name, value):
logging.debug(f'查看传递的值:{value}')
if value == '0':
return queryset.filter(Q(my_type=0) | Q(my_type=2) | Q(my_type=4))
elif value == '1':
return queryset.filter(Q(my_type=1) | Q(my_type=3) | Q(my_type=5))
else:
return queryset.all()
def return_filter_date(self, queryset, name, value):
logging.debug(f'查看传递的值:{value}')
if ',' not in value:
return queryset.all()
else:
date_list = value.split(',')
start_date = datetime.strptime(date_list[0], '%Y-%m-%d')
end_date = datetime.strptime(date_list[1] + ' 23:59:59', '%Y-%m-%d %H:%M:%S')
return queryset.filter(update_time__gt=start_date, update_time__lt=end_date)
# 针对signals的相关示例:
import logging
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete
from django.dispatch import receiver
from django.db import transaction
@transaction.atomic
@receiver(pre_save, sender=YourModel)
def pre_save_object(sender, instance=None, **kwargs):
'''接收模型调用save前的信号
args:
sender Model:发送信号的模型类
instance object:实际被保存的实例
'''
logging.info('即将被保存的对象:{}'.format(instance))
@transaction.atomic
@receiver(post_save, sender=YourModel)
def create_update_object(sender, instance=None, created=False, **kwargs):
'''接收模型调用save后的信号
args:
sender Model:发送信号的模型类
instance object:实际被保存的实例
created bool:是否是创建数据
'''
if created:
logging.info('新增时:{}'.format(instance))
else:
logging.info('修改时:{}'.format(instance))
@transaction.atomic
@receiver(pre_delete, sender=YourModel)
def pre_delete_object(sender, instance=None, **kwargs):
'''接收模型调用delete前的信号
args:
sender Model:发送信号的模型类
instance object:实际被保存的实例
'''
logging.info('即将删除的对象:{}'.format(instance))
@transaction.atomic
@receiver(post_delete, sender=YourModel)
def delete_object(sender, instance=None, **kwargs):
'''接收模型调用delete后的信号
args:
sender Model:发送信号的模型类
instance object:实际被保存的实例
'''
logging.info('已经被删除的对象:{}'.format(instance))
# 针对urls的相关示例:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import YourViewSet, YourView
router = DefaultRouter()
router.register(r'yourpath', YourViewSet, basename="ViewSetPath")
urlpatterns = [
path('', include(router.urls)),
path('yourpath/', YourView.as_view(), name='ViewPath'),
]
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。