如何写一个测试脚本?
创建一个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关系