models查询学习

models查询学习


前言

​ 我们昨天学习了模板层的操作,建立表关系,外键字段操作和对父表从表的正反向查询,今天我们进一步学习models中的查询语句。


聚合查询


​ sql中我们已经学习过了一部分的聚合函数:max\min\sum\count\avg,在models中这些聚合函数也是同样的效果。

先从模块中导入聚合函数
from django.db.models import Max, Min, Sum, Avg, Count
res = models.表.objects.aggregate(聚合函数('目标字段'))

举个栗子
res = models.Book.object.aggregate(Max('price'))
这一步就是将Book表中,查找price字段值最高的对象

分组查询


​ sql中我们的分组操作是group by;而ORM执行分组操作时,如果出错,可能需要去修改sql_mode 移除only_full_group_by。

# 查询电影的出演人数
res = models.Film.objects.annotate(actor_num=Count('actor__pk')).values('name', 'actor_num')
print(res)
<QuerySet [{'name': '教父', 'actor_num': 3}, {'name': '闻香识女人', 'actor_num': 3}, {'name': '肖申克的救赎', 'actor_num': 0}, {'name': '海上钢琴师', 'actor_num': 0}, {'name': '血战钢锯岭', 'actor_num': 0}, {'name': '乱世佳人', 'actor_num': 0}, {'name': '指环王', 'actor_num': 0}]>

模板

res = models.表1(条件1).objects.annotate(表2_需要做聚合判断的字段=聚合函数('表2__pk')).values('需要展示出来的字段', '需要展示出来的字段1')

以表中的字段分组


res = models.表1.objects.values('表2_id').annotate(表1_需要做聚合函数判断的字段=('pk')).values('需要展示出来的字段', '需要展示出来的字段1')

F与Q查询


​ 当查询条件的左右两表的数据都需要表中的数据,如果只使用常规的方式是做不到的

res = models.表.objects.filter(字段1 > 字段2)  # 不行
res = models.表.objects.filter(字段1__字段2)  # 不行 
当查询条件的左右两表的数据都需要表中的数据 可以使用F查询

F查询

from django.db.models import F
res = models.表.objects.filter(字段1__双下查询方式=F('字段2'))

int群增:
res = models.表.objects.update(字段=F('字段1') + 1000)  # 群增

char字段群增:
from django.db.models.functions import Concat
from django.db.models import Value

res = models.表.objects.update(字段=Concat(F('字段'), Value('字符串')))

Q查询

​ 当我们需要对多个字段做不同判断时,也是做不到的,因为filter括号内多个条件默认是and关系 无法直接修改,我们就需要Q查询。

res = models.表.objects.filter(Q(字段__聚合函数=判断数据), Q(字段1__聚合函数=判断数据))  # 逗号是and关系
res = models.表.objects.filter(Q(字段__聚合函数=判断数据) | Q(字段1__聚合函数=判断数据))  # 管道符是or关系
res = models.表.objects.filter(Q(字段__聚合函数=判断数据) ~ Q(字段1__聚合函数=判断数据))  # ~是not关系

Q查询终极用法

​ 底层代码查询终归是局限了,如果能将条件转换成变量名,可以通过外部输入,那么这个查询功能就很完美了,而filter(name=''xxx'')字段只能是字符串,没法用变量, 但是可以用q传变量。

​ 应用场景:当我们在查询的条件中需要组合条件时(例如两个条件“且”或者“或”)时。我们可以使用Q()查询对象。

​ 这样就生成了一个Q()对象,我们可以使用符号&或者|将多个Q()对象组合起来传递给filter(),exclude(),get()等函数。当多个Q()对象组合起来时,Django会自动生成一个新的Q()。例如下面代码就将两个条件组合成了一个

from django.db.models import Q

this_q=Q(name="cox") | Q(name="Tom") # 或
models.Author.objects.filter(this_q) # 获取在Author表中,name等于cox和name等于cox的所有数据

this_q=Q(name="cox") & Q(age=12) # 且
models.Author.objects.filter(Q(name="cox") & Q(age=12))# 获取在Author表中,name等于cox并且age等于12的所有数据

Q(question__startswith='Who') | ~Q(pub_date__year=2005)  # 非
models.Author.objects.filter(this_q) # 获取在Author表中,question__startswith等于Who,或者 pub_date__year不低于2005的所有数据

Q表达式与普通表达式一起使用

Q()对象可以结合关键字参数一起传递给查询函数,不过需要注意的是要将Q()对象放在关键字参数的前面

News.objects.get(Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),question__startswith='Who')

这样我们可以使用 “&”或者“|”还有括号来对条件进行分组从而组合成更加复杂的查询逻辑。


Q()传入条件查询

​ 直接将判断条件传入Q中,可以用变量名去传入Q中;然后filter(q) 就可以满足查询条件是变量。

举个🌰:
q1 = Q()
q1.connector = 'OR'
q1.children.append(('name', "cox"))
q1.children.append(('name', "Tom"))
q1.children.append(('name', "Jeck"))
    
models.Author.objects.filter(q1) # 在Author表中,name等于cox/Tom/Jeck的所有数据


再举一个🌰:
con = Q()

q1 = Q()
q1.connector = 'OR'
q1.children.append(('name', "cox"))
q1.children.append(('name', "Tom"))
q1.children.append(('name', "Jeck"))

q2 = Q()
q2.connector = 'OR'
q2.children.append(('age', 12))

con.add(q1, 'AND')
con.add(q2, 'AND')

models.Author.objects.filter(con) # 在Author表中,name等于cox/Tom/Jeck的 并且 满足age等于12 的所有数据

ORM查询优化


惰性查询:ORM查询默认都是惰性查询,编写orm并不会直接指向SQL语句,之后后续代码用到了才会执行

分页功能:ORM查询默认自带分页功能,减少单次查询数据的压力


only与defer


values

res = models.表.objects.values('字段', '字段1')
需求:单个结果还是以对象的形式展示 可以直接通过句点符操作
for i in res:
     print(i.get('字段'))

only

res = models.表.objects.only('字段', '字段1')
for obj in res:
		print(obj.字段)

only会产生对象结果集 对象点括号内出现的字段不会再走数据库查询
但是如果点击了括号内没有的字段也可以获取到数据 但是每次都会走数据库查询

defer

res = models.表.objects.defer('字段', '字段1')
for obj in res:
		print(obj.字段)
defer与only刚好相反 对象点括号内出现的字段会走数据库查询
如果点击了括号内没有的字段也可以获取到数据 每次都不会走数据库查询


res = models.表.objects.select_related('字段')
    for obj in res:
    		print(obj.字段)

select_related括号内只能传一对一和一对多字段 不能传多对多字段
效果是内部直接连接表(inner join) 然后将连接之后的大表中所有的数据全部封装到数据对象中
后续对象通过正反向查询跨表 内部不会再走数据库查询
res = models.表.objects.prefetch_related('字段')
    for obj in res:
        print(obj.字段)
      
将多次查询之后的结果封装到数据对象中 后续对象通过正反向查询跨表 内部不会再走数据库查询

ORM常见字段

# 常用字段
AutoField
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

IntegerField
一个整数类型,范围在 -2147483648 to 2147483647。

CharField
字符类型,必须提供max_length参数, max_length表示字符长度。

DateField
日期字段,日期格式  YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例。


# 时间字段独有
DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。

auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段


# 进制数
DecimalField 带固定精度的十进制数
有两个必需的参数:

max_digits

号码中允许的最大位数。请注意,此数字必须大于decimal_places。

decimal_places

与数字一起存储的小数位数。


# 图片类型
ImageField 图片字段
upload_to 上传图片时指定位置
里面写了上传图片的方法,不需要自己写上传代码了。ImageField自动把图片写入到django后台指定的目录下,使用upload_to这个参数来指定上传位置


# 布尔类型
BooleanField 
真值和假值 0 和 1,判断状态时使用


# 邮箱类型
EmailField 邮箱字段
带邮箱正则验证的。自动判断邮箱的格式


# 大文本字段
TextField 
编辑新闻、小说网站, 商品详情介绍


# 自定义字段类型
class MyCharField(models.Field):
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super().__init__(max_length=max_length, *args, **kwargs)

    def db_type(self, connection):
        return 'char(%s)' % self.max_length

ORM重要参数


null                数据库中字段是否可以为空
db_column           数据库中字段的列名,默认是字段名
db_tablespace
default             数据库中字段的默认值
primary_key         数据库中字段是否为主键,不但加速查找,还限制值唯一,而且不能为空
db_index            数据库中字段是否可以建立索引,查找速度加快
unique              数据库中字段是否可以建立唯一索引,不但加速查找,还限制值唯一
unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
unique_for_year     数据库中字段【年】部分是否可以建立唯一索引,比如QQ邮箱,只需前半部分索引,节省空间

verbose_name        Admin中显示的字段名称
blank               Admin中是否允许用户输入为空
editable            Admin中是否可以编辑,Flase时隐藏
help_text           Admin中该字段的提示信息
choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)

error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;#Django的admin
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'}

validators          自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]

ORM执行原生SQL


# 方式1
from django.db import connection, connections
cursor = connection.cursor()  
cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
cursor.fetchone()

# 方式2
models.UserInfo.objects.extra(
                    select={'newid':'select count(1) from app01_usertype where id>%s'},
                    select_params=[1,],
                    where = ['age>%s'],
                    params=[18,],
                    order_by=['-age'],
                    tables=['app01_usertype']
                )

多对多三种创建方式


# 全自动(常见)
	orm自动创建第三张表 但是无法扩展第三张表的字段
	authors = models.ManyToManyField(to='Author')
# 全手动(使用频率最低)
	优势在于第三张表完全自定义扩展性高 劣势在于无法使用外键方法和正反向
	class Book(models.Model):
    title = models.CharField(max_length=32)
  class Author(models.Model):
    name = models.CharField(max_length=32)
  class Book2Author(models.Model):
    book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to='Author')
# 半自动(常见)
	正反向还可以使用 并且第三张表可以扩展 唯一的缺陷是不能用
  add\set\remove\clear四个方法
  
	class Book(models.Model):
    title = models.CharField(max_length=32)
    authors = models.ManyToManyField(
      					to='Author',
    						through='Book2Author',  # 指定表
      					through_fields=('book','author')  # 指定字段
    )
  class Author(models.Model):
    name = models.CharField(max_length=32)
    '''多对多建在任意一方都可以 如果建在作者表 字段顺序互换即可'''
    books = models.ManyToManyField(
      					to='Author',
    						through='Book2Author',  # 指定表
      					through_fields=('author','book')  # 指定字段
    )
  class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')

posted @ 2022-05-18 22:26  Eason辰  阅读(139)  评论(0)    收藏  举报