Django中基表的设置
通过图书管理系统引入多表操作:如果我们创建表的方式是先抽象出表与表之间相同的字段建一个父类,然后在用每个表类去继承这个父类,如下面的代码,我们将无法得到期望的表字段。
class BaseModel(models.Model):
created_time = models.BooleanField(auto_now_add=True)
id_delete = models.BooleanField(default=False)
class Book(BaseModel):
pass
class Publisher(BaseModel):
pass
通过这种继承方式无法完成表的继承,如果要实现表的继承,需要进行专门的设置将Meta的abstract设置为True下面这种写法就可以:
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
created_time = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
class Book(BaseModel):
pass
class Publisher(BaseModel):
pass
Meta类是Django的内嵌类,它的作用是给自己的上级类添加一些功能或者指定一些标准,abstract = True 将该基类定义为抽象类,即不生成数据库表单,只作为一个可以继承的基类,把一些子类必须的代码放在基类,避免重复代码也避免重复录入数据库。db_table = 'xxx'指定该类的数据库表单名字。当然如果不指定也没关系,Django会自动默认的按照一定规则生成数据模型对应的数据库表名。关于Django Meta的详细讲解可以参考该博客。
断关联表关系
断关联表关系不会影响连表查询操作、可以提升增删改效率、易于后期数据库表的重构,缺点:数据库没有连表检测,容易出现脏数据,需要通过严格的逻辑避免脏数据的产生。
举例:表A依赖表B,先插入表A的记录,该记录对应的表B记的录没产生,在没有关联的情况下,该操作可以实现,但是表A的数据就是脏数据。接着再将表B对应的数据添加,脏数据就得到处理了。反过来先操作表B后操作表A,更满足逻辑思维,一样可以执行。通过逻辑将A、B表进行连表查询,不会有任何异常。如两张表建立了一对一外键字段,外键在A表,那么先往B表写数据就更合理。
假设图书管理系统中书、出版社、作者、作者详细信息四张表之间的关系如下:
"""
表关系
1)Book 和 Publish 一对多:外键在多的一方 Book
2)Book 和 Author 多对多:外键在查询频率高的一方 Book
3)Author 和 AuthorDetail 一对一:外键要根据实际需求建立在合理的位置 AuthorDetail(外键在AuthorDetail方作者就可以没有AuthorDetail,更合理)
"""
Django orm中外键字段属性详解
在建表之前我们对外键字段属性进行了解:
1)related_name在外键中设置外键反向查询的字段名:正向找字段名,反向找related_name值,related_name的默认值是表名小写 + _set,这就是为什么在Django中跨表反向查询时我们使用表名小写 + _set去查另一张表的数据。
2)on_delete在外建中必须设置,表示级联关系,在Django1.x下系统默认提供(值为models.CASCADE),Django2.x下必须手动明确:
CASCADE:默认值,级联
例子:作者被删,作者详情一定没有
DO_NOTHING:外键不会被级联,假设A表依赖B表,B记录删除,A表的外键字段不做任何处理
例子:作者被删了,作者的书还存在,书还是该作者写的;出版社没了,出版社出版的书还在
SET_DEFAULT:假设A表依赖B表,B记录删除,A表的外键字段重置为default属性设置的值,所以必须配合default属性使用。
例子:部门没有了,部门员工里的部门字段改为未分组部门的id
SET_NULL使用的时候需要NULL=True;假设A表依赖B表,B记录删除,A表的外键字段重置为NULL,所以必须配合NULL=True使用。
例子:部门没有了,部门员工里的部门字段改为未分组部门的id字段为NULL
注:多对多字段不能设置on_delete级联关系,如果要处理级联关系,需要手动明确关系,处理表关系中的多个外键
3)db_constraint在外建中控制表关联,默认为True,设置为False则断开关联,所谓断开关联就是将外键字段从数据表中去掉,这样就可以随意操作两张表,主要用于后期添加新的表,构建新的表关系,断不断关联不会影响操作只会影响效率。
from django.contrib.auth.models import User
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
created_time = models.DateTimeField(auto_now_add=True)
class Meta:
# 基表,为抽象表,是专门用来被继承,提供公有字段的,自身不会完成数据库迁移
abstract = True
class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=10, decimal_places=2)
publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING, null=True)
#断开出版社与book之间的表关联,取消两表的级联,将默认值设置为空,外键反向查询的字段名设置为book
authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
def __str__(self):
return self.name
class Publish(BaseModel):
name = models.CharField(max_length=64)
address = models.CharField(max_length=64)
class Author(BaseModel):
name = models.CharField(max_length=64)
class AuthorDetail(BaseModel):
mobile = models.CharField(max_length=64)
author = models.OneToOneField(to=Author, related_name='detail', db_constraint=False, on_delete=models.CASCADE)
#断开Author、AuthorDetial表与表的关联,将两张表设置为级联,并将外键反向查询的字段名设置为detail
数据库中脏数据介绍
数据库中常见的并发操作所带来了一致性问题包括:丢失的修改,不可重复读,读“脏”数据,幻读。
1.丢失的修改:一个事物的更新覆盖了另一个事物的更新。例如:事物A和B读入同一数据并修改,B提交的结果破坏了A提交的结果,导致A的修改被丢失。
2.不可重复读:一个事物两次读取同一个数据,两次读取的数据不一致。不可重复读是指事物A读取数据后,事物B执行更新操作,事务A 无法再现前一次读取结果。
a.事物A读取某一数据后,事物B对其作了修改,当事物A再次读取数据时,得到与前一次不同的值。
b.事物A按一定的条件从数据库中读取了某些数据后,事物B删除了其中部分记录,当A再次以相同条件读取时,发现某些记录消失了。
3.脏读:一个事物读取了另一个事物未提交的数据。读“脏”数据是指事物A修改某一数据,并将其写回磁盘,事物B读取同一数据后,A由于某种原因被撤销,这时A已修改过的数据恢复原值,B读到的数据就与数据库中的数据不一致,则B读到的数据为“脏”数据,即不正确的数据。
4.幻读:一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。
a.事物A按一定的条件从数据库中读取某些数据记录后,事物B插入了一些记录,当B再次按照相同条件读取数据时,发现多了一些记录。(也叫做幻影读)。
产生上述三类数据不一致性的主要原因是并发操作破坏了事物的隔离性,并发控制就是要用正确的方式调度并发操作,使一个事物的执行不受其他事物的干扰,从而避免造成数据的不一致性。
子序列化
Django中的子序列化的功能是:通过跨表查询数据然后对跨表查到的数据反序列化。
如果涉及到通过外键进行跨表查询,然后再将查询数据反序列化到前台就需要用到子序列化,比如下面的例子:我们查询出版社信息的时候连带将book表中的该出版社所出版过的书名一并查出来。
子序列化的使用方法及注意事项:
1)只能在序列化中使用
2)字段名必须是外键(正向反向都可以)字段,相对于自定义序列化外键字段,自定义序列化字段不能参与反序列化,而子序列化必须为外键名,子序列化字段不写入数据库。
3)如果外键关联的表有多个字段时,需要设置子序列化字段many=True。
4)子序列化是单向操作,因为作为子系列的类必须写在上方,所以不能产生逆方向的子序列化。
# 多表操作
class BookModelSerializer(serializers.ModelSerializer):
#子系列的类
class Meta:
model = models.Book
fields = '__all__'#设置为__all__则book表中的所有字段都可以被publisher联表查询
class PublishModelSerializer(serializers.ModelSerializer):
book = BookModelSerializer(many=True)
#在本例中子查询就是当我们查publisher的时候可以,通过子序列化的方式将出版社所出版的书的信息也查询出来,这其实是一个联表查询。设置many=True后我们就可以查book表中的多个字段(book表中的fields中对应的字段才可以查)
class Meta:
model = models.Publish
# fields = '__all__'
fields = ['name', 'address', 'books']