写测试脚本/单表操作(双下滑线查询)/多表操作/ORM跨表查询(基于双下划线的查询,F查询,Q查询)******

如何写一个测试脚本?

创建一个test.py文件

from django.test import TestCase
​
# Create your tests here.
import os
​
​
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings文件路径")
    import django
    django.setup()
    """在下面就可以写针对某一个py文件的测试代码"""
    from app01 import models

搞定了!!!

模型层

13个方法

all()
filter()
get()
reverse()
order_by()
exclude()
values()
values_list()
count()
distinct()
exists()
first()
last()

演示表

from django.db import models
​
# Create your models here.
class Book(models.Model):
    title = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)
​
    # 库存数
    kucun = models.IntegerField(null=True)
    # 卖出数
    maichu = models.IntegerField(null=True)
​
    publish = models.ForeignKey(to='Publish')  # 默认是跟publish的主键字段做的一对多外键关联
    authors = models.ManyToManyField(to='Author')
    # 虚拟字段      1.自动创建第三张表    2.帮助orm跨表查询
def __str__(self):
        return self.title
​
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    # email = models.EmailField()  # 就是varchar(254)
def __str__(self):
        return self.name
​
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail')
​
    def __str__(self):
        return self.name
class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=64)
    """
    models.py中的模型类__str__方法 必须返回一个字符串形式数据!!!
    
    """
    def __str__(self):
        return self.addr

单表操作

新增数据

# 第一种:有返回值,并且就是当前被创建的数据对象
modles.Book.objects.create(name='',price='',publish='',author='',create_time='2019-5-1')
# 第二种:先实例化产生对象,然后调用save方法保存
book_obj = models.Book(name='',price='',publish='',author='',create_time='2019-5-1')
book_obj.save()
# 2.验证时间格式字段即可以传字符串也可以传时间对象
import datetime
ctime = datetime.datetime.now()
book = models.Book.objects.create(name='',price='',author='',create_time=ctime)

删除数据

# 1.删除书名为xxx的这本书  queryset方法
res = models.Book.objects.filter(name='').delete()
# 2.删除书名为xxx的这本书  queryset方法
res = models.Book.objects.filter(name='').first()
res.delete()

修改数据

# 1.queryset修改
models.Book.objects.filter(name='').update(price='')
# 2.对象修改
book = models.Book.objects.filter(name='').first()
book.price = 66.66
book.save()  # 对象只有保存方法 这样也能实现修改需求

查询数据

<1> all():                  查询所有结果
<2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象
<3> get(**kwargs):          返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误
<4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象
<5> order_by(*field):       对查询结果排序('-id')/('price')
<6> reverse():              对查询结果反向排序   >>>前面要先有排序才能反向
<7> count():                返回数据库中匹配查询(QuerySet)的对象数量
<8> first():                返回第一条记录
<9> last():                返回最后一条记录
<10> exists():              如果QuerySet包含数据,就返回True,否则返回False
<11> values(*field):        返回一个ValueQuerySet(一个特殊的QuerySet),运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
<12> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<13> distinct():            从返回结果中剔除重复纪录
# 必须完全一样才可以去重(意味着带了id就没有意义了)
# res = models.Book.objects.all().values('name').distinct()  先查一个重复的值再去重

双下滑线查询

查看orm内部sql语句的方法有哪些?

  • 1.如果是queryset对象,那么可以点query直接查看该queryset的内部sql语句
  • 2.在django项目的配置文件中,配置一下参数即可实现所有的orm在查询的时候自动打印对应的sql语句
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

查询价格大于200的书籍

res = models.Book.objects.filter(price__gt=200)

查询价格小于200的书籍

res = models.Book.objects.filter(price__lt=200)

查询价格大于等于200.22的书籍

res = models.Book.objects.filter(price__gte=200.22)

查询价格小于等于200.22的书籍

res = models.Book.objects.filter(price__lte=200.22)

查询价格要么是200,要么是300,要么是666.66

res = models.Book.objects.filter(price__in=[200,300,666.66])

查询价格在200到800之间的

res = models.Book.objects.filter(price__range=(200,800))  # 两边都包含

查询书籍名字中包含p的

res = models.Book.objects.filter(title__contains='p')  # 仅仅只能拿小写p
res = models.Book.objects.filter(title__icontains='p')  # 忽略大小写

查询书籍是以三开头的,或者以P结尾的

res = models.Book.objects.filter(title__startswith='')
res1 = models.Book.objects.filter(title__endswith='p')

查询出版日期是2017的年

res = models.Book.objects.filter(create_time__year='2017')

多表操作

图书管理系统表创建

  • 一对多:ForeignKey

  • 一对一:OnoToOneField,可以用ForeignKey代替ForeignKey(unique=True)

    • 上面两个关键字所创建出来的字段会自动加上_id后缀

  • 多对多:ManyToManyFiled

    • 该字段并不会真正的在表中展示出来 它仅仅是一个虚拟字段

      • 1.告诉orm自动创建第三种表

      • 2.帮助orm跨表查询

1.一对多的书籍记录增删改查

# 针对外键关联的字段 两种添加方式
# 第一种通过publish_id
# 第二种通过publish传出版社对象
# 删除书籍直接查询删除即可,删除出版社会级联删除
# 编辑数据也是两种对应的方式(对象点的方式(这里能点publish和publish_id)最后点save(),queryset方式update())

2.多对多的书籍与作者的增删改查

"""前提:先获取书籍对象,再通过书籍对象点authors来进行书籍作者的增删改查"""
# 1.给书籍新增作者add
  # 1.add可以传作者id,也可以直接传作者对象,并且支持传多个位置参数(不要混着用)
  
# 2.给书籍删除作者remove
    # 1.remove同样可以传id,对象,并且支持传多个位置参数(不要混着用)
# 3.直接清空书籍对象所有的作者数据clear()不用传任何参数
# 4.修改书籍对象所关联的作者信息set,注意点set括号内必须传可迭代对象,里面可以传id,对象
    
"""总结:一对多增删改,多对多add,remove,clear,set"""

ORM跨表查询

基于对象的跨表查询

正向查询与反向查询

  • 正向查询按字段,反向查询按表名小写

正向与方向的概念解释

# 一对一
# 正向:author---关联字段在author表里--->authordetail     按字段
# 反向:authordetail---关联字段在author表里--->author     按表名小写
​
​
# 一对多
# 正向:book---关联字段在book表里--->publish      按字段
# 反向:publish---关联字段在book表里--->book      按表名小写_set.all() 因为一个出版社对应着多个图书
# 多对多
# 正向:book---关联字段在book表里--->author       按字段
# 反向:author---关联字段在book表里--->book       按表名小写_set.all() 因为一个作者对应着多个图书

连续跨表查询

  • 查询图书是三国演义的作者的手机号,先查书,再正向查到作者,在正向查手机号

总结:

  • 基于对象的查询都是子查询,这里可以用django配置文件自动打印sql语句的配置做演示

基于双下划线的查询

一对一用连表查询

  • 一对一双下划线查询
    • 正向:按字段,跨表可以在filter,也可以在values中
    • 反向:按表名小写,跨表可以在filter,也可以在values中

示例:查询jason作者的手机号

# 正向
ret=Author.objects.filter(name='jason').values('authordetail__phone')
​
# 反向
# 以authordetail作为基表 反向查询,按表名小写  跨表的话,用表名小写
ret=AuthorDetail.objects.filter(author__name='jason').values('phone')

示例;查询jason这个作者的性别和手机号

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

示例:查询手机号是130的作者性别

# 正向
ret=Author.objects.filter(authordetail__phone='13888888').values('sex')
​
# 反向
ret=AuthorDetail.objects.filter(phone='13888888').values('author__sex')

总结:

  • 其实你在查询的时候先把orm查询语句写出来,再看用到的条件是否在当前表内,在就直接获取,不在就按照正向按字段反向按表名来查即可

示例1:查询出版社为北方出版社的所有图书的名字和价格

res1 = Publish.objects.filter(name='').values('book__name','book__price')
res2 = Book.objects.filter(publish__name='').values('name','price')

示例2:查询北方出版社出版的价格大于19的书

res1 = Publish.objects.filter(name='',book__price__gt=19).values('book__name','book__price)

聚合查询

关键词是aggregate

# 别忘了导入下面的模块
from django.db.models import Sum,Max,Min,Avg,Count

示例:

models.Book.objects.all().aggregate(Avg("price"))

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

models.Book.objects.aggregate(average_price=Avg('price'))

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

models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))

分组查询

关键词是annotate

ORM查询示例:

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

示例1:统计每一本书的作者个数

book_list = models.Book.objects.all().annotate(author_num=Count("author"))
for obj in book_list:
    print(obj.author_num)

示例2:统计出每个出版社买的最便宜的书的价格

publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
for obj in publisher_list:
    print(obj.min_price)

示例3:统计不止一个作者的图书

models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)

示例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序

models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")

示例5:查询各个作者出的书的总价格

models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")

总结:

  • value里面的参数对应的是sql语句中的select要查找显示的字段,
  • filter里面的参数相当于where或者having里面的筛选条件
  • annotate本身表示group by的作用,前面找寻分组依据,内部放置显示可能用到的聚合运算式,后面跟filter来增加限制条件,最后的value来表示分组后想要查找的字段值
  • 只要是queryset对象,就可以无限制的调用queryset对象的方法!!!最最常用的就是对一个已经filter过滤完的数据 再进行更细化的筛选

F查询

  • F查询的本质就是从数据库中获取某个字段的值
  • 之前查询等号后面的条件都是我们认为输入的 ,现在变成了需要从数据库中获取数据放在等号后面

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

from django.db.models import F
res = models.Book.objects.filter(kucun__gt=F('maichu'))

示例:将书籍库存数全部增加1000

models.Book.objects.update(kucun=F('kucun')+1000)

示例:把所有书名后面加上'新款'

from django.db.models.functions import Concat
from django.db.models import Value
​
ret3 = models.Book.objects.update(title=Concat(F('title'), Value('新款')))
​
# 不能这么写,数据会变成0
models.Book.objects.update(title = F('title')+'新款')

Q查询

示例:查询书籍名称是三国演义或者价格是444.44

from django.db.models import Q
​
# filter只支持and关系,这种情况下下面代码的返回结果只会是空[]
res = models.Book.objects.filter(title='三国演义',price=444.44)
​
# 如果用逗号 那么还是and关系,返回值依然为空[]
res1 = models.Book.objects.filter(Q(title='三国演义'),Q(price=444))
​
# 用"|"就变为"或"关系了,返回结果正常
res2 = models.Book.objects.filter(Q(title='三国演义')|Q(price=444))
​
# 在Q前面加"~"就是not的意思
res3 = models.Book.objects.filter(~Q(title='三国演义')|Q(price=444))

Q查询的高级用法

q = Q()
q.connector = 'or'  # 修改查询条件的关系,默认是and
q.children.append(('title__contains','三国演义'))  # 往列表中添加筛选条件
q.children.append(('price__gt',444))  # 往列表中添加筛选条件
res = models.Book.objects.filter(q)  # filter支持你直接传q对象,但是默认还是and关系
原文地址:https://www.cnblogs.com/zhukaijian/p/11552494.html