djangoORM

正反向查询进阶操作

查询主键为3的书籍对应的出版社名称以及书名

image-20220906154457920

查询主键为4的书籍对应的作者姓名及书名

image-20220906154714223

查询老舍的作者的电话号码和地址

image-20220906154907221

查询上海出版社出版的书籍名称和价格

image-20220906155044473

查询jason写过的书的名称和日期

image-20220906155457722

查询电话是1188的作者姓名和年龄

image-20220906155726741

查询主键为6的书籍对应的作者和对应电话号码

image-20220906155916202

聚合查询

聚合函数主要有:max,min,sum,avg,count

使用这些函数前需要从`django.db.models模块中导入对应的类:

from django.db.models import Max,Min,Sum,Avg,Count

没有分组之前如果单纯的想要使用聚合函数,则需要关键字aggregate

image-20220906160848149

分组查询

分组之前设置了一个特性only_full_group_by特性,该特性为只能够直接获取分组的字段,其他字段需要使用方法,我们也可以忽略掉该特性。将sql_mode中only_full_group_by配置移除即可。

image-20220906161909831

分组查询

按照整条数据分组

models.Book.objects.annotate()  # 按照一条条数据记录分组

按照表中某个字段分组

models.Book.objects.value('title').annotate()  按照annotate之前values括号中指定的字段分组

统计每一本书的作者个数

image-20220906162742714

统计出每个出版社卖的最便宜的书的价格

image-20220906163921062

统计不止一个作者的图书

注意filterannotate前面则是where,在annotate后面是having

image-20220906165954796

查询各个作者出的书的总价格

image-20220906170338125

F与Q查询

F查询

Django中提供F(),当两个字段的值做比较的时候,F()的实例可以在查询中引用字段,来比较同一个model实例中两个不同字段的值。

查询库存数大于卖出数的书籍

F可以帮我们取到表中某个字段对应的值来作为筛选条件,而不是自定义常量的条件,实现了动态比较的效果。

image-20220906172350453

将所有书籍的价格上涨了100

Django支持F()对象之间以及F()对象和常熟之间的加减乘除和取模的操作。基于此可以对表中的数值类型进行数学运算。

models.Book.objects.Update(price=F('price')+100)

将所有书籍名称加上爆款后缀

如果要修改char字段的话,需要导入ConcatValue两个类

Concat表示进行字符串的拼接操作,参数位置决定了拼接是在头部拼接还是尾部拼接,Value里面是要新增的拼接值。

from django.db.models.functions import Concat
from django.db.models import Value

models.Book.objects.filter(pk=5).update(title=Concat('title',Value('爆款')))

Q查询

filter()等方法中逗号隔开的条件是与的关系。如果你需要执行更复杂的查询,可使用Q对象

查询卖出数大于600或者价格小于300块的书籍

对条件包裹一层Q的时候,filter即可支持交叉并的比较符

from django.db.models import Q
res = models.Book.objects.filter(Q(stock_num__gt=600)|Q(price__lt=300))

查询库存数是10000,并且卖出去不是0的数据

我们可以用组合&和|操作符以及使用括号进行分组来编写任意复杂的Q对象。同时,Q对象可以使用~操作符取反,这允许组合正常的查询和取反(not)查询。

from django.db.models import Q
models.Book.objects.filter(Q(stock_num=10000)&~Q(sale_num=0))

查询产品名包含新款,并且库存数大于900的书籍

查询函数可以混合使用Q对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q对象)都将AND在一起。但是,如果出现Q对象,它必须位于所有关键字参数的前面。

models.Book.objects.filter(Q(stock_num__gt=900),title__contains='爆款')

Q查询,还可以将查询条件的字段改为字符串形式

q_obj=Q()
q_obj.connector='or'  # q对象默认的多个条件也是and关系,可以修改为or
q_obj.children.append(('pk',1))
q_obj.children.append(('publish_id',2))
res=models.Book.object.filter(q_obj)

ORM查询优化

  • django orm默认都是惰性查询,当orm的语句在后续的代码中真正需要使用的时候才会执行
  • django orm自带limit分页,减轻数据库以及服务端的压力

only与defer

only

res=models.Book.objects.only('title','price')
    for obj in res:
        print(obj.title)
      	# print(obj.publish_time)

only会将括号内填写的字段封装成一个个数据对象,缓存在内存中,对象在点击的时候不会再走数据库查询,直接从内存中拿。但是对象也可以点击括号内没有的字段,只不过每次都会走数据库查询。

defer

res=models.Book.objects.defer('title','price')
    for obj in res:
       	# print(obj.title)
      	print(obj.publish_time)

defer与only刚好相反,数据对象点击考好内出现的字段,每次都会走数据库查询;数据对象点击括号内没有的字段,不会走数据库查询

select_related===>连表查询

select_related括号内只能接受外键字段(一对多,一对一)自动连表,得出的数据对象在点击表中数据的时候都不会再走数据库查询,因为第一次查询已经将整个表缓存到内存中。

res=models.Book.objects.select_related('publish')
    for obj in res:
        print(obj.publish)
      	print(obj.publish.name)

prefetch_related===>子查询

底层其实是子查询,将查询之后的结果也一次性封装到数据对象中,用户在使用的时候感觉不出来的

res=models.Book.objects.prefetch_related('authors')
    for obj in res:
        print(obj.title)
        print(obj.authors.all())

总结

一对多、一对一情况下使用selected_related;多对多情况下使用prefetch_related

ORM事务操作

事务操作

  • A:原子性
  • C:一致性
  • I:隔离性
  • D:持久性

事务隔离级别

  • 脏读
  • 幻读
  • 可重复读
  • 可串行读

原生sql事务语法

start transaction
rollback
commit
savepoint 

django中开启事务

from django.db import transaction
try:
  with transaction.atomic();
  	 # 多条ORM语句
except Exception as e:
  # 执行事务失败后的操作
  # rollback

模型层常见字段

AutoField()       int自增列,必须填入参数primary_key=True。当model中如果没有自增列,则自动创建一个列表名为id的列。

IntergerField()   一个整数类型,范围在-2147483648 to 2147483647  一般不用它来存手机号(位数也不够),直接用字符串存
BigIntegerField()
CharField()       字符类型,必须提供max_length参数,max_length表示字符长度

DateField()       时间类型,有两个参数:auto_now,auto_now_add
DateTimeField()   时间类型,有两个参数:auto_now,auto_now_add
DecimalField()    小数,有两个参数:max_degits,decimal_places
EmailField()
BooleanField()    传布尔值,存0或1
TextField()       传大段文本
FileField()       存储文件数据,自动找执行位置存储,字段存具体路径
ForeignKey()      
OneToOneField()
ManyToManyField()

ORM自定义字段

django没有设置对应的char类型的字段,但是django允许我们自定义新的字段,下面为自定义对应于数据库的char类型。

from django .db.models import Field
class MyChar(Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length=max_length
        super().__init__(max_length=max_length,*args,**kwargs)
    def tb_type(self,connection):
        """
        限定生成的数据表字段类型char,长度为max_length指定的值
        :param connection:
        :return:
        """
        return f'char({self.max_length})'

自定义字段在实际项目应用中可能会经常用到,我们需要有个印象。

ORM常见字段参数

max_length      字符长度
ver_bose_name   注释
auto_now        创建时间,每次修改row都会跟着修改
auto_now_add    创建时间后都不会自动修改
null            表示某个字段可以为空
default         设置默认值
max_digits      小数的总位数
decimal_places  小数的位数
unique=True     设置唯一
db_index=True   设置索引
to              设置要关联的表
to_field        设置要关联的表的字段

choices

当字段数据的可能性是可以完全列举出来的时候,应该考虑使用该参数

class UserInfo(models.Model):
  name CharField(max_length=32)
  gender_choice=(
     (1,男性),
     (2,女性),
     (3,其他)
  )
  gender=models.IntegerField(choices=gender_choice)
  
  user_obj=models.UserInfo.objects.filter(pk=1).first()
  user_obj.gender   # 1 
  user_obj.get_gender_display()  # 男性=

在通过一对多反向查询结果集给外键关联字段设置别名,就可以替代表名_set

publish=models.ForeignKey(to='Publish',on_delete=models.CASCADE,related_name='xxx')
res=models.Publish.objects.filter(pk=1).first()
# 原本是这样写
res.book_set.all()
# 用别名后
res.xxx.all()

on_delete

1、models.CASCADE
        级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2、models.SET_NULL
    当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3、models.PROTECT
    当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
4、models.SET_DEFAULT
    当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5、models.SET()
    当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6、models.DO_NOTHING
    什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似

多对多三种创建方法详解

自动创建

authors = models.ManyToManyField(to='Author')
	优点:第三张表自动创建
 	缺点:第三张表扩展性差

手动创建

	class Book(models.Model):
        pass
  class Author(models.Model):
        pass
 	class Book2Author(models.Model):
        book_id = models.ForeignKey(to="Book")
        author_id = models.ForeignKey(to="Author")
优点:第三张表扩展性强
缺点:无法使用正反向查询以及多对多四个方法

半自动创建

class Book(models.Model):
    authors = models.ManyToManyField(to='Author',
                  through='Book2Author'
                  through_fields=('book_id','author_id')
                                    )
class Author(models.Model):
    pass
class Book2Author(models.Model):
    book_id = models.ForeignKey(to="Book")
    author_id = models.ForeignKey(to="Author")

优点:扩展性强并且支持正反向查询
缺点:无法使用多对多四个方法
posted @ 2022-09-06 20:41  荀飞  阅读(175)  评论(0编辑  收藏  举报