Django ORM之F与Q查询

1、F查询

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

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

models.py

from django.db import models

# Create your models here.
class Product(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    maichu = models.IntegerField()
    kucun = models.IntegerField()

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

用的sqlite数据库

image

F可以依次取出表中某个字段对应的值,当作筛选条件,实现动态比较的效果;
from django.test import TestCase

# Create your tests here.
import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day59.settings")
    import django
    django.setup()
    from app01 import models
    from django.db.models import F, Q

# F查询
# 查询卖出数大于库存数的商品
res = models.Product.objects.filter(maichu__gt=F('kucun'))
print(res)

# 将所有的商品的价格提高100块
models.Product.objects.update(price=F('price')+100)

# 将所有商品的名字后面都加一个爆款,Concat表示进行字符串的拼接操作,Value里是要新增的拼接值
from django.db.models.functions import Concat
from django.db.models import Value
models.Product.objects.update(name=Concat(('name'), Value('爆款')))
#Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。基于此可以对表中的数值类型进行数学运算➕➖✖➗

2、Q查询

# Q查询
# 或的关系
res = models.Product.objects.filter(Q(price=188.88), Q(name='帽子爆款')) # and
res = models.Product.objects.filter(Q(price=188.88)|Q(name='帽子爆款'))  # or

# 价格是188.88或者name不是帽子爆款的; 这里not用 ~表示
res = models.Product.objects.filter(Q(price=188.88) | ~ Q(name='帽子爆款'))  # not
print(res)

# 混合使用
# 名字不是帽子爆款,并且价格是188.88的
# 需要注意的是Q对象必须放在普通的过滤条件前面
res = models.Product.objects.filter(~Q(name='帽子爆款'), price=188.88)
print(res)


# 当条件是字符串时
q = Q()
q.connector = 'or'   # 通过这个参数可以将Q对象默认的and关系变成or
q.children.append(('price', 188.88))   # children和append是两个方法,就是将条件加进去(price=188.88)
q.children.append(('name', '裤子爆款'))
res = models.Product.objects.filter(q)   # 原先Q对象查询默认是and,但是现在改为or了,也就是在Product表中price=188.88或者name=裤子爆款的所有数据;
print(res)

3、ORM事务

# 事务
from django.db import transaction
with transaction.atomic():
    # 在with代码块写实务操作
    models.Product.objects.filter(id=1).update(kucun=F('kucun')-1)
    models.Product.objects.filter(id=1).update(kucun=F('maichu') + 1)

# 出了with就可以写其他代码逻辑
print("123")

4、自定义ORM字段

#models.py

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

    """
    限定生成数据库表的字段类型为char,长度为max_length指定的值
    """
    def db_type(self, connection):
        return 'char(%s)' %self.max_length


#接着就可以使用了
class Product(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    maichu = models.IntegerField()
    kucun = models.IntegerField()
    info = MyCharField(max_length=32, null=True)

5、only与defer

only

# only帮我们拿到一个对象
res = models.Product.objects.only('name')
# 结果:<QuerySet [<Product: Product object>, <Product: Product object>, <Product: Product object>]>
for i in res:
    # 如果这个对象在only中,走一次数据库就可以全部拿到
    print(i.name)
    # 如果这个对象不在only中,也可以查,但是要反复走数据库
    print(i.price)

defer

# 简单来说defer的作用是与only是相反的

# defer帮我们拿到一个对象,
res = models.Product.objects.defer('name')
# 结果:<QuerySet [<Product: Product object>, <Product: Product object>, <Product: Product object>]>
for i in res:
    # 如果这个对象不在only中,走一次数据库就可以全部拿到
    print(i.name)
    # 如果这个对象在only中,也可以查,但是要反复走数据库
    print(i.price)

6、choices

# choices可以用在有固定某些选择的地方,用数字来对应实际的选项,在数据库中存储的是数字,查询的时候可以再把数字转为实际的值;

# 比如我们在表中存储人的性别时,一般也就分为:’男‘、‘女’、‘其他’这几类,这时就可以用到choices功能了;

#models.py
class Product(models.Model):
    name = models.CharField(max_length=32)

    # 括号中是一个元组,写的就是数字与实际值的对应关系
    choices = ((1, ''), (2, ''), (3, '其他'))
    gender = models.IntegerField(choices=choices, default=2)  # default=2:默认值是女


# 然后:
python3 manage.py makemigrations  
python3 manage.py migrate

数据库中的存储情况,我这里还是用前面的Product表举的例子:

image

查询:

# tests.py

# choices
res = models.Product.objects.filter(id=1).first()
print(res.gender)    # 获取编号;结果:2
print(res.get_gender_display())    # 获取编号对应的实际值;结果:女;get_xxx_display()是固定写法

其他适用场景:

# choices使用场景还还是很多的
# 如上面的性别,其他的还有学历(本科,专科...)、成绩级别(A+,A,B,C...)、上班打卡(已签到,迟到,早退...)
# 等等......
原文地址:https://www.cnblogs.com/weiyiming007/p/12370774.html