模型层之多表操作

一、创建模型

各模型字段

图书表book:id,title,price,publish_date

作者表author:id,name,age

出版社表publish:id,name,addr,email

作者详情表:id,phone,addr

表之间的关系

图书表和作者表是多对多关系

图书表和出版社表是多对一关系

作者表和作者详情表是一对一关系

class Book(models.Model):
    # 主键不指定默认创建id字段为主键
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)  # auto_now:每次操作改数据都会自动更新时间,auto_now_add:新增数据的时候会将当前时间自动添加,后续的修改该字段不会自动更新
    # 外键关系
    publish = models.ForeignKey(to='Publish')
    authors = models.ManyToManyField(to='Author')  # 虚拟字段, 信号字段

    def __str__(self):
        return '书籍对象的名字:%s'%self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    email = models.EmailField()  # 对应就是varchar类型

    def __str__(self):
        return '出版社对象的名字:%s'%self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    authordetail = models.OneToOneField(to='AuthorDetail')

    def __str__(self):
        return  '作者对象的名字:%s'%self.name


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

二、多对一(一对一)表关系记录操作

2.1 新增表记录

# 1.直接写数据:publish_id=1

models.Book.objects.create(title='红楼梦',price=66.66,publish_id=1)

# 2.传数据对象:publish=publish_obj
publish_obj = models.Publish.objects.filter(pk=2).first()  # pk指主键
models.Book.objects.create(title='三国演义',price=199.99,publish=publish_obj)

2.2 修改表数据

# 基于对象操作
book_object = models.Book.objects.get(pk = 1)
publish_object = models.Book.objects.get(pk = 2)
book.publish = publish_object  # 或者book.publish_id = 2
book.save()

# 基于Queryset操作
publish_object = models.Book.objects.filter(pk = 2).first()
book_object = models.Book.objects.filter(pk = 1).update(publish = publish_object)  # book_object = models.Book.objects.filter(pk = 1).update(publish_id = 2)

2.3 删除表记录

  同单表操作里的删除操作

三、多对多表关系记录操作

3.1 新增表记录

# 数字id,可传多个
表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()
表1_object.与表2的关联字段.add(数字(id),……)

# 对象,可传多个
表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()
表2_object = models.对应表2的类名.objects.filter(字段名 = 值[查找条件]).first()
表1_object.与表2的关联字段.add(表2_object,……)

3.2 删除表记录

# 数字id,可传多个
表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()
表1_object.与表2的关联字段.remove(数字(id),……)

# 对象,可传多个
表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()
表2_object = models.对应表2的类名.objects.filter(字段名 = 值[查找条件]).first()
表1_object.与表2的关联字段.remove(表2_object,……)

3.3 清空表记录

表1_object.与表2的关联字段.clear()  # 不需要传参数

3.4 修改表记录

  本质上就是先清空,再新增

# set:传的必须是可迭代对象(用列表或元祖形式)
# 数字id,可传多个
表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()
表1_object.与表2的关联字段.set([数字(id),……])

# 对象,可传多个
表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()
表2_object = models.对应表2的类名.objects.filter(字段名 = 值[查找条件]).first()
表1_object.与表2的关联字段.set([表2_object,……])

四、基于对象的跨表查询(SQL的子查询)

正向:关联字段在表内

反向:关联字段不在表内

4.1 一对一

正向:按字段查询

表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()

表2_object = 表1_object.与表2的关联字段

表2_object.表2需查询字段  # 查询结果

 

反向:按表名小写查询

表2_object = models.对应表2的类名.objects.filter(字段名 = 值[查找条件]).first()

表1_object = 表2_object.表1名小写

表1_object.表1需查询字段  # 查询结果

4.2 多对一

正向:按字段查询

表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()

表2_object = 表1_object.与表2的关联字段

表2_object.表2需查询字段  # 查询结果

反向:按表名小写_set.all()查询

表2_object = models.对应表2的类名.objects.filter(字段名 = 值[查找条件]).first()

表1数据 = 表2_object.表1名小写_set.all()  # Queryset对象

  若只想要拿出查询记录的某一列,因为是一个Queryset对象,Queryset对象可以无限用句点符.查询,所以可以用.values('字段')得到。

4.3 多对多

正向:按字段.all()查询

表1_object = models.对应表1的类名.objects.filter(字段名 = 值[查找条件]).first()

表2数据 = 表1_object.与表2的关联字段.all()  # Queryset对象

反向:按表名小写_set.all()查询

表2_object = models.对应表2的类名.objects.filter(字段名 = 值[查找条件]).first()

表1数据 = 表2_object.表1名小写_set.all()  # Queryset对象

4.4 连续跨表

栗子

#查询三国演义这本书所有作者的手机号
book_obj = models.Book.objects.filter(name ='三国演义').first()

authors = book_obj.authors.all() 

for author in authors:
    authordetail = author.authordetail
    print(authordetail.phone)

五、基于双下划线的跨表查询(SQL的连表查询)

  1. 确定以哪张表为查询基表来捋清楚正反向,正向按关联字段__查询字段,反向按表名小写__查询字段。
  2. 确定查询字段是否在基表里,不在的话用双下划线转到目标表。
  3. 跨表可以在filter,也可以在values里

栗子:查询moonzier作者的性别和手机号

# 正向
result = Author.objects.filter(name = 'moonzier').values('sex','authordetail__phone')

# 反向
result = AuthorDetail.objects.filter(author__name = 'moonzier').values('author__sex','phone')

related_name

  反向查询时,如果定义了related_name ,则用related_name替换表名小写。

六、聚合查询与分组查询

6.1 聚合(aggregate(*args, **kwargs))

from django.db.models import Avg,Count,Max,Min,Sum
# 计算所有图书的平均价格
Book.objects.all().aggregate(Avg('price'))

  aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数

from django.db.models import Avg,Count,Max,Min,Sum
Book.objects.aggregate(average_price=Avg('price'), Max('price'), Min('price'))

6.2 分组(annotate())

  annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。以谁为分组(group by)就以谁为基表。跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询

栗子

from django.db.models import Avg,Count,Max,Min,Sum
# 统计每本书的作者数
# 法一
ret= Book.object.all().annotate(num = Count('authors'))  # annotate的返回值是querySet
for result in ret:
    print(result.name,result.num)

# 法二
result= Book.object.all().annotate(num = Count('authors')).values('name','num')
print(result)

方法总结

  • values在前,表示 group by;values在后,表示取值。
  • filter在前,表示 where条件;filter在后,表示having(分组之后的条件筛选)。
from django.db.models import Avg,Count,Max,Min,Sum
# 统计以三开头的书籍的作者数
result = Book.object.all().filter(name__startwith='').annotate(num=Count('authors')).values('name','num')
# 套用方法总结写法
result = Book.object.all().values('pk').filter(name__startwith='').annotate(num=Count('authors')).values('name','num')
# 前面的values不写,默认以id,group by,可指定字段

七、F查询与Q查询

7.1 F查询

  以往我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

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

# 查询评论数大于收藏数的书籍
 from django.db.models import F
 Book.objects.filter(commentNum__gt=F('keepNum'))

  Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__gt=F('keepNum')*2)

# 将每一本书的价格提高30元:
Book.objects.all().update(price=F("price")+30) 

  F对象的字符串操作

# 所有书名字后面加上'限时特价'
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.all().update(name=Concat(F("name")+Value('限时特价')))

7.2 Q查询

  filter() 等方法中的关键字参数查询条件都是“AND” 。 如果你需要执行更复杂的查询(例如OR或者NOT),你可以使用Q对象

from django.db.models import Q
# 与:筛选价格等于88并且出版社为东京出版社的书
Book.objects.filter(Q(price=88) ,Q(publisher='东京出版社'))
# 或(|):筛选价格等于88或者出版社为东京出版社的书
Book.objects.filter(Q(price=88) | Q(publisher='东京出版社'))
# 非(~):筛选价格不等于88或者出版社为东京出版社的书
Book.objects.filter(~Q(price=88) | Q(publisher='东京出版社'))

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

Q对象查询字段是字符串

from django.db.models import F, Q
q = Q()
q.connector = 'or'  # 通过这个参数可以将Q对象默认的and关系变成or
q.children.append(('price',88.88))
q.children.append(('name','西游记'))
res = models.Product.objects.filter(q)  # Q对象查询默认也是and
print(res)

posted on 2019-07-21 22:10  慕子尔  阅读(180)  评论(0编辑  收藏  举报

导航