ORM 图书管理系统
models.py(模型)
from django.db import models # Create your models here. # 书 出版社 作者 作者详情表 # 书 class Book(models.Model): title = models.CharField(max_length=32) # 书名 price = models.DecimalField(max_digits=5,decimal_places=2) # 五位数字,两位小数 999.99 publish_date = models.DateField(auto_now_add=True) # auto_now = True 每一次修改都自动更新时间; auto_now_add=True 第一次创建时自动填写时间 memo = models.CharField(max_length=128) def __str__(self): return self.title # 出版社 class Publisher(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=128) phone = models.IntegerField() def __str__(self): return self.name # 作者表 class Author(models.Model): name = models.CharField(max_length=16) # 作者和作者详情表表示一对一关联的 detail = models.OneToOneField(to="AuthorDetail") # 作者和书的关系是多对多 books = models.ManyToManyField(to="Book") def __str__(self): return self.name # 作者详情表 class AuthorDetail(models.Model): city = models.CharField(max_length=32) email = models.EmailField()
加数据

关于单表查询
>>> 找书名含有“看见”的书(contains-->包含) >>> models.Book.objects.filter(title__contains="看") --- <QuerySet [<Book: 看见>]> # 查找出版日期是2017年的书 >>> models.Book.objects.filter(publish_date__year="2017") --- <QuerySet [<Book: 好吗,好的>, <Book: 看见>]> # 查找出版日期大于2017年的书 >>> models.Book.objects.filter(publish_date__year__gt="2017") -- <QuerySet [<Book: 用我一辈子去忘记>, <Book: 镜子中的世界>]> # 查找出版日期是2017年的书名 >>> models.Book.objects.filter(publish_date__year="2017").values("title") --- <QuerySet [{'title': '好吗,好的'}, {'title': '看见'}]> # 查找价格大于10元的书名和价格 >>> models.Book.objects.filter(price__gt="10").values("title","price") --- <QuerySet [{'title': '看见', 'price': Decimal('10.90')},
{'title': '用我一辈子去忘记', 'price': Decimal('15.90')},
{'title': '天空之城', 'price': Decimal('20.90')}]>
# 查找memo为空的书
>>> models.Book.objects.filter(memo__isnull=True)
--- <QuerySet []>
# 查询在北京的出版社
>>> models.Publisher.objects.filter(addr="北京")
--- <QuerySet [<Publisher: 天天>]>
# 查找出版社名字以“天”开头的出版社
>>> models.Publisher.objects.filter(name__startswith="天")
--- <QuerySet [<Publisher: 天天>, <Publisher: 天空之境>]>
>>> a = models.Book.objects.filter(price__gt="10").values("title","price") >>> for i in a: ... print(i,type(i)) {'title': '看见', 'price': Decimal('10.90')} <class 'dict'> {'title': '用我一辈子去忘记', 'price': Decimal('15.90')} <class 'dict'> {'title': '天空之城', 'price': Decimal('20.90')} <class 'dict'> >>> for i in a: ... print(i["price"]) 10.90 15.90 20.90
多对多
查询规律(正向,用字段。 反向,用表名)
.authors.all() (Book------------(查所有相关的作者)--------------->Author) <--------(查作者写过所有相关的书籍)------------ .book_set.all()
# 查找书名为"看见"的书 >>> models.Book.objects.filter(title="看见") -- <QuerySet [<Book: 看见>]> # 此时找到的是一个对象集合 >>> models.Book.objects.get(title="看见") --- <Book: 看见> # 此时找到的是书的对象() >>> models.Book.objects.get(title="看见").publisher --- <Publisher: 稻城出版社> # 对应的是一个出版社的对象 >>> models.Book.objects.get(title="看见").publisher.name --- '稻城出版社' # 查找书名为“看见”的出版社的地址 >>> models.Book.objects.get(title="看见").publisher.addr --- '云南' (多对多反向查找)表名_set # 查找书名为“看见”的所有作者 # 从书找作者为反向查找 表名_set >>> models.Book.objects.get(title="看见").author_set.all() --- <QuerySet [<Author: 柴静>, <Author: 小黑>, <Author: 小景>]> # 查找书名为“看见”的所有作者的名字 # 不能直接.name,QuerySet没有这个方法 >>> models.Book.objects.get(title="看见").author_set.all().values("name") ---- <QuerySet [{'name': '柴静'}, {'name': '小黑'}, {'name': '小景'}]> # 查找书名是“看见”的作者的邮箱 # detail是作者与作者详情表一对一关联的 # 双下划线表示跨表 >>> models.Book.objects.get(title="看见").author_set.all().values("detail__email") -- <QuerySet [{'detail__email': '11@66'}, {'detail__email': '11@88'}, {'detail__email': '11@99'}]> # 查找作者为“柴静”的所有书 (正向找) 直接用字段名(books) # .books 相当于Django帮你去查询多对多关联的表 >>> models.Author.objects.get(name="柴静").books.all() --- <QuerySet [<Book: 看见>, <Book: 用我一辈子去忘记>]>
外键的反向查询 >>> models.Publisher.objects.get(name="天天出版社").book_set.all() --- <QuerySet [<Book: 用我一辈子去忘记>, <Book: 天空之城>]> >>> models.Publisher.objects.get(name="天天出版社").book_set.all().values_list("title","price") --- <QuerySet [('用我一辈子去忘记', Decimal('15.90')), ('天空之城', Decimal('20.90'))]> >>> models.Book.objects.filter(publisher__name="天天出版社").values_list("title","price") ---- <QuerySet [('用我一辈子去忘记', Decimal('15.90')), ('天空之城', Decimal('20.90'))]> # 由作者找到书 # l两种查法,查找柴静出版的书的书名及价格 >>> models.Author.objects.get(name="柴静").books.all().values_list("title","price") --- <QuerySet [('看见', Decimal('10.90')), ('用我一辈子去忘记', Decimal('15.90'))]> >>> models.Author.objects.filter(name="柴静").values_list("books__title","books__price") --- <QuerySet [('看见', Decimal('10.90')), ('用我一辈子去忘记', Decimal('15.90'))]> # 使用filter得到的是QuerySet .对象列表 values_list是query set的方法 get得到的是具体数据对象 对象才能直接.
# 查找书名为"围城"的作者,写过的书 >>> author_objs = models.Author.objects.filter(books__title="围城") >>> for i in author_objs ... print(i.books.all()) <QuerySet [<Book: 镜子中的世界>, <Book: 围城>]> <QuerySet [<Book: 用我一辈子去忘记>, <Book: 围城>]>
总结:在上述的论述中,所有的反向的查询的表名均为小写,且需要提醒的是在一对一的反向查询中表名不需要跟set,因为它只有一个对象,通过“.表名”得到的是一个models对象,可以直接查询需要的字段。
聚合函数
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。如下例:
#查询所有书籍的平均价格 # 实例1: a = models.Book.objects.all().aggregate(Avg("price"))
print(a)
# {'price__avg': 12.066667}
# 实例2: a = models.Book.objects.all().aggregate(avgprice=Avg("price"))
print(a)
# {'avgprice': 12.066667}
如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
# 查询所有书籍的平均价格、价格最大小值、价格最大值 a = models.Book.objects.all().aggregate(Avg("price"),Min("price"),Max("price"))
print(a)
-- {'price__avg': 12.066667, 'price__min': Decimal('6.90'), 'price__max': Decimal('20.90')}
以上实例中需要按照如下方式引入相应的模块方法: from django.db.models import Avg, Max, Min
分组函数(annotate)
annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。
实例1:查询每一个出版社出版过的书籍个数
from django.db.models import Avg,Max,Min,Count
a = models.Publisher.objects.all().annotate(num=Count("book__title"))
#可以理解为每一个出版社对象增加一个num字段,该字段值是通过聚合函数联表求得 for i in a:
print(i.num)
2
2
1
1
annotate的返回值是querySet,如果不想遍历对象,可以用上values_list,如下:
a = models.Publisher.objects.all().annotate(num=Count("book__title")).values_list("name","num") print(a) <QuerySet [('稻城出版社', 2), ('天天出版社', 2), ('天空之境出版社', 1), ('五道口出版社', 1)]>
查询每本书的作者个数
a = models.Book.objects.all().annotate(counts=Count("author__id")).values("title","counts") -- <QuerySet [{'title': '好吗,好的', 'counts': 1},
{'title': '看见', 'counts': 3},
{'title': '用我一辈子去忘记', 'counts': 2},
{'title': '镜子中的世界', 'counts': 3},
{'title': '围城', 'counts': 2},
{'title': '天空之城', 'counts': 2}]>
F查询与Q查询
F查询
上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
使用命令建app
E:\Django项目\Books>python3 manage.py startapp app02
注册:


# 当你的表里面有2个字段做比较,就用F查询 查找商品的收藏数大于购买数的商品 >>> models.Goods.objects.all().filter(keep_num__gt=F("buy_num")) --- <QuerySet [<Goods: 娃哈哈>]> # 收藏数比购买数大2倍 >>> models.Goods.objects.all().filter(keep_num__gt=F("buy_num")*2) --- <QuerySet [<Goods: 小天使>]>
# 将全部购买数增加100 >>> models.Goods.objects.all().update(buy_num=F("buy_num")+100) 5

引申:
如果需要修改char字段?
如:把所有商品名前面加上“你好”
from django.db.models import Value as V from django.db.models.functions import Concat models.Goods.objects.all().update(name=Concat(V("你好"),F("name")))

>>> from django.db.models.functions import Concat >>> from django.db.models import Value >>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("666"), Value(")")))
Q查询
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。
示例一:
查询作者名是柴静或为lishi
app01_models.Author.objects.filter(Q(name="柴静")|Q(name="jassin")) <QuerySet [<Author: 柴静>, <Author: jassin>]>
你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
示例:查询作者名字是柴静并且不是2018年出版的书的书名。
from django.db.models import Q from app01 import models as app01_models models.Book.objects.filter(Q(author__name="柴静")&Q(publish_day__year="2018"))
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。
例如:查询出版年份是2017或2018,书名中带物语的所有书。
>>> models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="物语") <QuerySet [<Book: 番茄物语>, <Book: 香蕉物语>, <Book: 橘子物语>]>

浙公网安备 33010602011771号