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, MaxMin

分组函数(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: 橘子物语>]>

 

 



 

posted @ 2018-01-25 00:08  小杜要加油  阅读(209)  评论(0)    收藏  举报