Django多表查询

字段关系

外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。

ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。

关键字 意义
to 设置要关联的表
to_field 设置要关联的表的字段,默认不写关联的是主键
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。删除关联数据,与之关联也删除
db_constraint 是否在数据库中创建外键约束,默认为True。
models.DO_NOTHING
删除关联数据,引发错误IntegrityError    
    
models.PROTECT
删除关联数据,引发错误ProtectedError

models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)

models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)

models.SET

删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

OneToOneField

一对一字段。

通常一对一字段用来扩展已有字段。(通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联)

关键字 意义
to 设置要关联的表
to_field 设置要关联的表的字段,默认不写关联的是主键
on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。删除关联数据,与之关联也删除

ManyToManyField

"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。

它存在于下面两种情况:

  1. 外键关系的反向查询
  2. 多对多关联关系

简单来说就是在多对多表关系并且这一张多对多的关系表是有Django自动帮你建的情况下,下面的方法才可使用。

方法

create()

创建一个关联对象,并自动写入数据库,且在第三张双方的关联表中自动新建上双方对应关系。

models.Author.objects.first().book_set.create(title="偷塔秘籍")
上面这一句干了哪些事儿:
1.由作者表中的一个对象跨到书籍比表
2.新增名为偷塔秘籍的书籍并保存
3.到作者与书籍的第三张表中新增两者之间的多对多关系并保存

add()

把指定的model对象添加到第三张关联表中。

添加对象

>>> author_objs = models.Author.objects.filter(id__lt=3)
>>> models.Book.objects.first().authors.add(*author_objs)

添加id

>>> models.Book.objects.first().authors.add(*[1, 2])

set()

更新某个对象在第三张表中的关联对象。不同于上面的add是添加,set相当于重置

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.set([2, 3])

remove()

从关联对象集中移除执行的model对象(移除对象在第三张表中与某个关联对象的关系)

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.remove(3)

clear()

从关联对象集中移除一切对象。(移除所有与对象相关的关系信息)

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.clear()

注意:

对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。

aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。

键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

用到的内置函数:

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

示例:

>>> from django.db.models import Avg, Sum, Max, Min, Count
>>> models.Book.objects.all().aggregate(Avg("price"))
{'price__avg': 13.233333}

如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

>>> models.Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 13.233333}

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

>>> models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}

11|2****分组

我们在这里先复习一下SQL语句的分组。

假设现在有一张公司职员表:

img

我们使用原生SQL语句,按照部分分组求平均工资:

select dept,AVG(salary) from employee group by dept;

ORM查询:

from django.db.models import Avg
Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")
这里需要注意的是annotate分组依据就是他前面的值,
如果前面没有特点的字段,则默认按照ID分组,
这里有dept字段,所以按照dept字段分组

连表查询的分组:

img

SQL查询:

select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;

ORM查询:

from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")

F查询

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

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

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

字符串的拼接

   from django.db.models.functions import Concat
   from django.db.models import Value
   models.Product.objects.update(name=Concat(F('name'),Value('爆款')))

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

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

修改操作也可以使用F函数,比如将每一本书的价格提高30元:

Book.objects.all().update(price=F("price")+30) 

Q查询

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

from django.db.models import Q
Q(title__startswith='Py')

Q 对象可以使用&| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

等同于下面的SQL WHERE 子句:

WHERE name ="yuan" OR name ="egon"

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

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

bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python")

Q 查询改变默认的查询方式为或

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

bulk_create批量插入数据

要求:一次性插入多条数据

data = ["".join([str(random.randint(65, 99)) for i in range(4)]) for j in range(100)]
obj_list = [models.A(name=i) for i in data]
models.A.objects.bulk_create(obj_list)

事务支持

事务的定义:将多个sql语句操作变成原子性操作,要么同时成功,有一个失败则里面回滚到原来的状态,保证数据的完整性和一致性(NoSQL数据库对于事务则是部分支持)

# 如果你是买家,你买了一件商品,那么在后台数据库中要做什么操作?
# 1. 创建一条订单数据
# 2. 去产品表 将卖出数+1, 库存数-1
from django.db.models import F
from django.db import transaction
# 开启事务处理
try:
    with transaction.atomic():
        # 创建一条订单数据
        models.Order.objects.create(num="110110111", product_id=1, count=1)
        # 能执行成功
        models.Product.objects.filter(id=1).update(kucun=F("kucun")-1, maichu=F("maichu")+1)
except Exception as e:
    print(e)

Defer和Only

defer('id','name'):取出对象,字段除了id和name都有
only('id','name'):取的对象,只有id和name
如果点,依然能点出其它列,但是不要点了,因为取没有的列,会再次查询数据库

    ret=models.Author.objects.only('nid')
    for i in ret:
        # 查询不在的字段,会再次查询数据库,造成数据库压力大
        print(i.nam

支持原生的SQL语句

from django.db import connection, connections

cursor = connection.cursor() # connection=default数据
cursor = connections['db2'].cursor()

cursor.execute("""SELECT * from auth_user where id = %s""", [1])

row = cursor.fetchone()
row = cursor.fetchall()
ret=models.Author.objects.raw('select * from app01_author where nid>1')
    print(ret)
    for i in ret:
        print(i)
    print(ret.query)
    # 会把book的字段放到author对象中
    ret=models.Author.objects.raw('select * from app01_book where nid>1')
    print(ret)
    for i in ret:
        print(i.price)
        print(type(i))

自定义Char类型

from django.db import models

# Create your models here.
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

Choice 字段

models.py

choices = ((1,‘男’),(2,'女'))
gender = modles.IntegerField(choices = choices,default = 2 )
res = models.Product.objects.filter(id=1).first()
print(res.gender) //到到的是数字
print(res.get_gender_display())  # 获取编号对应的中文注释
# models.Product.objects.create(...gender=1) // 会对应的存到数据库里面
原文地址:https://www.cnblogs.com/ruhai/p/11020241.html