django之模型层
1. ORM
-
MVC或者MTV框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动
-
ORM是“对象-关系-映射”的简称。
Mysql
#sql中的表 #创建表: CREATE TABLE employee( id INT PRIMARY KEY auto_increment , name VARCHAR (20), gender BIT default 1, birthday DATE , department VARCHAR (20), salary DECIMAL (8,2) unsigned, ); #sql中的表纪录 #添加一条表纪录: INSERT employee (name,gender,birthday,salary,department) VALUES ("alex",1,"1985-12-12",8000,"保洁部"); #查询一条表纪录: SELECT * FROM employee WHERE age=24; #更新一条表纪录: UPDATE employee SET birthday="1989-10-24" WHERE id=1; #删除一条表纪录: DELETE FROM employee WHERE name="alex"
ORM
#python的类 --->对应表 class Employee(models.Model): id=models.AutoField(primary_key=True) name=models.CharField(max_length=32) gender=models.BooleanField() birthday=models.DateField() department=models.CharField(max_length=32) salary=models.DecimalField(max_digits=8,decimal_places=2) #python的类对象 --->对应记录 #添加一条表纪录: emp=Employee(name="alex",gender=True,birthday="1985-12-12",epartment="保洁部") emp.save() #查询一条表纪录: Employee.objects.filter(age=24) #更新一条表纪录: Employee.objects.filter(id=1).update(birthday="1989-10-24") #删除一条表纪录: Employee.objects.filter(name="alex").delete()
from django.db import models # Create your models here. class Book(models.Model): id=models.AutoField(primary_key=True) title=models.CharField(max_length=32) state=models.BooleanField() pub_date=models.DateField() price=models.DecimalField(max_digits=8,decimal_places=2) publish=models.CharField(max_length=32)
2. settings配置
若想将模型转为mysql数据库中的表,需要在settings中配置:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'bms', # 要连接的数据库,连接前需要创建好 'USER':'root', # 连接数据库的用户名 'PASSWORD':'', # 连接数据库的密码 'HOST':'127.0.0.1', # 连接主机,默认本级 'PORT':3306 # 端口 默认3306 } }
import pymysql pymysql.install_as_MySQLdb()
最后通过两条数据库迁移命令即可在指定的数据库中创建表 :
python manage.py makemigrations
python manage.py migrate
`django.core.exceptions.ImproperlyConfigured: mysqlclient ``1.3``.``3` `or` `newer ``is` `required; you have ``0.7``.``11.None`
MySQLclient目前只支持到python3.4,因此如果使用的更高版本的python,需要修改如下:
通过查找路径C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql 这个路径里的文件把
if version < (1, 3, 3): raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.version)
注释掉就行了
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
def index(request): # 添加表记录 # 方式1: # book_obj = Book(title='python基础教程',price=100,pub_date='2012-12-12',publish='人民出版社') # book_obj.save() # 方式2:create的返回值就是当前生成的对象记录 book_obj = Book.objects.create(title='django企业开发',price=100,pub_date='2018-12-12',publish='人民出版社') print(book_obj.title) print(book_obj.price) return HttpResponse('ok')
查询表记录
查询API
def index(request): # 查询表记录 ''' 1. 方法的返回值 2. 方法的调用者 ''' # (1) all 方法:返回值:queryset对象,queryset是django自定义的一种数据类型。 # book_list = Book.objects.all() # [obj1,obj2......] # for obj in book_list: # print(obj.title, obj.price) # print(book_list[1].title) # (2) first,last:调用者:queryset对象 返回值:model对象 # book = Book.objects.all().first() # book = Book.objects.all()[0] # 和上面一样 # (3) filter() 返回值:queryset对象 # book_list = Book.objects.filter(price=100) # [obj1,obj2......] # # print(book_list) # <QuerySet [<Book: python基础教程>, <Book: django企业开发>]> # # book_obj = Book.objects.filter(price=100).first() # ret = Book.objects.first(title='go',price=100) # (4) get() 有且只有一个查询结果时,才有意义,查询出多条结果会报错。 返回值:model对象 # book_obj = Book.objects.get(title='go') # print(book_obj.price) # book_obj = Book.objects.get(price=100) # 报错,因为结果超过一个。price=300也报错,因为查询不到。 # (5) exclude() 和filter一样,只是exclude是排除条件过滤 # ret = Book.objects.exclude(title='go') # print(ret) # (6) order_by 调用者是queryset对象,返回值也是queryset对象 # ret = Book.objects.all().order_by('-id') # 按id降序 # ret = Book.objects.all().order_by('price','-id') # 按价格升序,价格一样的话按id降序 # print(ret) # (7) reverse() 对查询结果反向排序 # ret = Book.objects.all().order_by('price','-id').reverse() # print(ret) # (8) count() 调用者是queryset对象,返回值是int类型 # ret = Book.objects.all().count() # print(ret) # (9) exists() # ret = Book.objects.all() # if ret: # 如果记录很多的话就效率太低了 # print('ok') # ret = Book.objects.all().exists() # 原理 LIMIT 1 # # if ret: # print('ok') # ======================== 重要的三个方法 ======================== # (10) values 调用者:queryse 返回值:queryset # ret = Book.objects.all() # for item in ret: # print(item.price) # ret = Book.objects.all().values("price") # ret = Book.objects.values("price") # 这样也可以,本质上还是用的all,为了更好的理解,用上面的方法。 # print(ret) # <QuerySet [{'price': Decimal('100.00')}, {'price': Decimal('100.00')}, {'price': Decimal('200.00')}]> ''' 原理: temp = [] for obj in Book.objects.all(): temp.append({ "price":obj.price }) return temp ''' # print(ret[0].get('price')) # 100 # ret = Book.objects.all().values("price", 'title') # print(ret) ''' values: <QuerySet [{'price': Decimal('100.00'), 'title': 'python基础教程'}, {'price': Decimal('100.00'), 'title': 'django企业开发'}, {'price': Decimal('200.00'), 'title': 'go'}]> ''' # (11) values_list values里面放的是字典,values_list里面放的是元组 # ret = Book.objects.all().values_list("price",'title') # print(ret) ''' values_list: # <QuerySet [(Decimal('100.00'), 'python基础教程'), (Decimal('100.00'), 'django企业开发'), (Decimal('200.00'), 'go')]> ''' # (12) distinct 去重 # ret = Book.objects.all().distinct() # 没有意义 ret = Book.objects.all().values('price').distinct() print(ret) return HttpResponse('ok')
基于双下划线的模糊查询
Book.objects.filter(price__in=[100,200,300]) Book.objects.filter(price__gt=100) Book.objects.filter(price__lt=100) Book.objects.filter(price__range=[100,200]) Book.objects.filter(title__contains="python") # 精确大小写 Book.objects.filter(title__icontains="python") # 忽略大小写 Book.objects.filter(title__startswith="py") Book.objects.filter(pub_date__year=2012)
def index(request): # 模糊查询 # ret = Book.objects.filter(price__gt=10,price__lt=200) # print(ret) # ret = Book.objects.filter(title__startswith='py') # print(ret) # ret = Book.objects.filter(title__contains='g') # ret = Book.objects.filter(title__icontains='g') # 不区分大小写 # print(ret) # # ret = Book.objects.filter(price__in=[200,300]) # 区间 # print(ret) # ret = Book.objects.filter(price__range=[100, 200]).values('title', 'price') # print(ret) ret = Book.objects.filter(pub_date__year=2018,pub_date__month=6) print(ret) return HttpResponse('ok')
删除表记录
def index(request): # 删除 # 删除方法1: 调用者:queryset对象 # ret = Book.objects.filter(price=100).delete() # print(ret) # (2, {'app01.Book': 2}) 一般不用这个返回值 # 删除方法2:调用者:model对象 # Book.objects.filter(price=100).first().delete() # 修改 # 调用者一定要是queryset Book.objects.filter(title='go').update(title='go语言') return HttpResponse('ok')
3. ORM之多表操作
一对多
多:出版社
一:书籍
总结:一旦确定表关系是一对多:在多的表中创建关联字段
多对多
多:作者
多:书籍
总结:一旦确定表关系是多对多,要创建第三张关系表
一对一
一:作者
一:作者详细信息
一对第一本质上可以放到一张表里,有时候为了解耦,有时候为了查询的时候更简洁,把他们拆分成了两张表。
一对一的关联字段必须加上唯一(Unique)这个约束,而且放到两张表里的任何一张都可以。
from django.db import models # Create your models here. class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) # 如果写了就用写的,如果不写就自动添加 birthday = models.DateField() telephone = models.BigIntegerField() addr = models.CharField(max_length=64) class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() # 一对一 authordetail = models.OneToOneField(to="AuthorDetail", to_field='nid', on_delete=models.CASCADE) # 加引号会从全局去找 ''' 翻译成: authordetail_id INT UNIQUE, FOREIGN KEY(author_detail_id) REFERENCES authordetail(id) ''' def __str__(self): return self.name class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.name class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) publishDate = models.DateField() price = models.DecimalField(max_digits=5, decimal_places=2) # 一对多 # 关联主键可以不加to_field # Django会自动把publish转换成publish_id # null = true 允许publish_id可以为空 publish = models.ForeignKey(to="Publish", to_field='nid', on_delete=models.CASCADE) ''' 翻译成: publish_id INT, FOREIGN KEY(publish_id) REFERENCES publish(id) ''' # 创建多对多第一种方法 authors = models.ManyToManyField(to='Author') ''' 翻译成: CREATE TABLE book_author( id INT PRIMARY KEY auto_increment, book_id INT, author_id INT, FOREIGN KEY (book_id) REFERENCES book(id), FOREIGN KEY (author_id) REFERENCES author(id) ) ''' def __str__(self): return self.title # 创建多对多第二种方法 # class Book2Author(models.Model): # nid = models.AutoField(primary_key=True) # book = models.ForeignKey(to='Book', ) # 关联主键可以不加to_field # author = models.ForeignKey(to='Author', )
一对多添加记录
def add(request): Publish.objects.create( name='人民出版社', email='123@qq.com', city='北京' ) # =========================== 绑定一对多的关系 =========================== # 为book表绑定出版社 # 方式一 # book_obj = Book.objects.create( # title='红楼梦', # price=100, # publishDate='2012-02-01', # publish_id=1 # ) # print(book_obj.publish) # 也能显示,与这本书籍关联的出版社对象 # print(book_obj.publish_id) # print(book_obj.publish.email) # 为book表绑定关系:publish # 方式二 # pub_obj = Publish.objects.filter(nid=1).first() # book_obj = Book.objects.create( # title='水浒传', # price=102, # publishDate='2013-01-02', # publish=pub_obj # django会给翻译成publish_id = 1 # ) # print(book_obj.publish) # 与这本书籍关联的出版社对象 # 人民出版社 #它是对象名字是__str__方法显示出来的 # print(book_obj.publish_id) # 也能显示 # 1 # print(book_obj.publish.email) # 查询西游记的出版社对应的邮箱 book_obj = Book.objects.filter(title='西游记').first() print(book_obj.publish.email) # 赋值的时候给了book_obj一个publish对象 return HttpResponse('ok')
def add(request): # =========================== 绑定多对多的关系 =========================== # book_obj = Book.objects.create( # title='三国演义', # price=102, # publishDate='2013-01-02', # publish_id=1 # django会给翻译成publish_id = 1 # ) # # # alex = Author.objects.get(name='alex') # egon = Author.objects.get(nid=2) # # # 绑定多对多关系的API接口 (三种方法) # book_obj.authors.add(alex, egon) # book_obj.authors.add(1, 2) # 放author的主键, 和上面的效果一样 # book_obj.authors.add(*[1,2]) # 和上面效果一样 ''' 找到book和author之间的关联表,然后在关联表里生成: book_id author_id 4 1 4 2 ''' # 解除多对多关系 book_obj = Book.objects.filter(nid=4).first() # # book_obj.authors.remove(1,2) # # book_obj.authors.remove(*[1,2]) # # # 删除全部 # # book_obj.authors.clear() # 不用加任何参数,找到book_id为4的然后全部清空掉 # all 重要 print(book_obj.authors.all()) # [obj1,obj2...]queryset:与这本书关联的所有作者对象集合 # 查询主键为4的书籍的所有作者的名字 ret = book_obj.authors.all().values('name') # 查询每一个作者的名字 print(ret) return HttpResponse('ok')
4. 跨表查询
基于对象的跨表查询(翻译成子查询)
一对多查询
def query(request): # ---------------------- 1. 基于对象的跨表查询(子查询) ---------------------- # 一对多查询 ''' select name from Publish where nid = ( select publish_id from Book where title = '三国演义'); ''' # 一对多的正向查询:查询三国演义这本书出版社的名字 # book_obj = Book.objects.filter(title='三国演义').first() # print(book_obj.publish) # 与这本书关联的出版社对象 # print(book_obj.publish.name) # 一对多的反向查询:查询人民出版社出版过的书籍名称 publish_obj = Publish.objects.filter(name='人民出版社').first() print(publish_obj.book_set.all()) # queryset return HttpResponse('OK')
def query(request): # 多对多查询的正向查询:查询三国演义这本书的所有作者的名字 # book_obj = Book.objects.filter(title='三国演义').first() # author_list = book_obj.authors.all() # queryset # # for author in author_list: # print(author.name) # 多对多查询的反向查询:查询alex出版过的所有书籍名称 alex = Author.objects.filter(name='alex').first() book_list = alex.book_set.all() for book in book_list: print(book.title) return HttpResponse('OK')
一对一查询
def query(request): # 一对一查询的正向查询:查询alex的手机号 # alex = Author.objects.filter(name='alex').first() # print(alex.authordetail.telephone) # 一对一查询的反向查询:查询手机号为110的作者的名字和年龄 ad = AuthorDetail.objects.filter(telephone=110).first() print(ad.author) print(ad.author.age) return HttpResponse('OK')
总结
''' A-B 关联属性在A表中 正向查询:通过A去查B。 反向查询:通过B去查A。 # 一对多查询 正向查询:按字段 反向查询:表名小写_set.all() book_obj.publish Book(关联属性:publish) --------------> Publish <-------------- publish_obj.book_set.all() 多对多查询 正向查询:按字段 反向查询:表名小写_set.all() author.authordetail Author(关联属性:authordetail) --------------> AuthorDetail对象 <-------------- authordetail.author 一对一查询 正向查询:按字段 反向查询:表名小写 '''
5. 基于双下划线的跨表查询(翻译成join查询)
tip:values相当于sql里的select ,filter相当于sql里的where
def query(request): # 正向查询按字段,反向查询按表名小写,用来告诉ORM引擎join哪张表 # 查询三国演义这本书出版社的名字 ''' select app01_publish.name from app01_book inner join app01_publish on app01_book.publish_id = app01_publish.nid where app01_book.title = '三国演义' ''' # 下面两种方式效率一样,建议用方式一,因为方式一比较好理解。 # 方式一,正向查询(按字段) # ret = Book.objects.filter(title='三国演义').values("publish__name") # # join publish,然后要name。注意:不能写表名,要写字段的名字。 # print(ret) # <QuerySet [{'publish__name': '人民出版社'}]> # 方式二,反向查询(表名小写) ret = Publish.objects.filter(book__title='三国演义').values('name') # 和上一个sql语句比只是左右颠倒了,其实是完全一样的,对于ORM只是思路不同。 # select app01_publish.name from app01_publish inner join app01_book ..... print(ret) # <QuerySet [{'name': '人民出版社'}]> return HttpResponse('OK')
# 正向查询按字段,反向查询按表名小写,用来告诉ORM引擎join哪张表 # 查询三国演义这本书的所有作者的名字 ''' select app01_author.name from app01_book inner join app01_book_authors on app01_book.nid = app01_book_authors.book_id inner join app01_author on app01_book_authors.author_id = app01_author.nid where app01_book.title = "三国演义" ''' # 下面两种方式效率一样,建议用方式一,因为方式一比较好理解。 # 方式一,正向查询(按字段) # 需求:通过Book表join与其关联的Author表,属于正向查询:安字段authors通知ORM引擎join book_authors与author # ret = Book.objects.filter(title='三国演义').values("authors__name") # authors = models.ManyToManyField(to='Author') # print(ret) # <QuerySet [{'authors__name': 'alex'}, {'authors__name': 'egon'}]> # 方式二,反向查询(表名小写) # 需求:通过Author表join与其关联的Book表,属于反向查询:按表名小写book通知ORM引擎join book_authors与book ret = Author.objects.filter(book__title='三国演义').values('name') # 和正向查询的sql语句本质一样,只是左右表的位置换了一下 print(ret) # <QuerySet [{'name': 'alex'}, {'name': 'egon'}]> return HttpResponse('OK')
def query(request): # 一对一查询 # 查询alex的手机号 # 方式一,正向查询(按字段) # 需求:通过Author表join与其关联的AuthorDetail表,属于正向查询:按字段authordetail通知ORM引擎join Authordetail表 # ret = Author.objects.filter(name='alex').values('authordetail__telephone') # print(ret) # <QuerySet [{'authordetail__telephone': 110}]> # 方式二,反向查询(表名小写) # 需求:通过Authordetail表join与其关联的Author表,属于反向查询:按表名小写author通知ORM引擎join Author表 ret = AuthorDetail.objects.filter(author__name='alex').values('telephone') print(ret) # <QuerySet [{'telephone': 110}]> return HttpResponse('OK')
def query(request): # 进阶练习 # 查询手机号以110开头的作者出版过的所有书籍名称以及书籍出版社名称 # # 方式一(正向查询): # # 需求:通过Book表join AuthorDetail表, Book与AuthorDetail无关联,所以需要连续跨表 # ret = Book.objects.filter(authors__authordetail__telephone__startswith='110').values('title', 'publish__name') # # 这个查询语句用到了5张表,Book,Book_Authors,Author,Authordetail,Publish # print(ret) # <QuerySet [{'title': '三国演义', 'publish__name': '人民出版社'}]> # 方式二(反向查询): ret = Author.objects.filter(authordetail__telephone__startswith=110).values('book__title', 'book__publish__name') print(ret) # <QuerySet [{'book__title': '三国演义', 'book__publish__name': '人民出版社'}]> return HttpResponse('查询成功')
def query(request): # ----------------------------->聚合查询 aggregate:返回值是一个字典,不再是queryset # 查询所有书籍的平均价格 ''' select avg(price) from app01_book ''' from django.db.models import Avg, Max, Min, Count ret = Book.objects.all().aggregate(avg_price=Avg('price'),max_price=Max('price')) print(ret) # {'avg_price': 101.5, 'max_price': Decimal('102')} return HttpResponse('查询成功')
# model.py class Emp(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() salary = models.DecimalField(max_digits=8, decimal_places=2) dep = models.CharField(max_length=32) province = models.CharField(max_length=32) # views.py def query(request): # ----------------------------->分组查询 annotate,返回值仍然是queryset # ===== 单表分组查询:===== # 示例1 # 查询每一个部门的名称以及员工的平均薪水 from django.db.models import Avg, Max, Min, Count ''' select dep,avg(salary) from emp group by dep ''' # ret = Emp.objects.values('dep').annotate(avg_salar=Avg('salary')) # print(ret) # <QuerySet [{'dep': '保安部', 'avg_salar': 5000.0}, {'dep': '教学部', 'avg_salar': 6000.0}]> # # # 单表分组查询的ORM语法:单表模型.objects.values('group by的字段').annotate(聚合函数("统计字段")) # values里的是select的字段 # 查询每一个省份的名称以及对应的员工数 # ret = Emp.objects.values('province').annotate(emp_count=Count('id')) # print(ret) # <QuerySet [{'province': '山东省', 'emp_count': 2}, {'province': '河北省', 'emp_count': 1}]> # 补充知识点: ret = Emp.objects.all() print(ret) # select * from emp ret = Emp.objects.values('name') # 和ret = Emp.objects.all().values('name')语法相同 print(ret) # select name from emp # 在单表下,这样按照group by分组是没有任何意义的。 return HttpResponse('查询成功')
多表分组查询
def query(request): # 查询每一个出版社出版的书籍个数 --> 不需要跨表 # Book.objects.values('publish_id').annotate(Count('id')) # 查询每一个出版社的名称以及出版的书籍个数 --> 需要跨表 # 方式1(只显示id): ''' SELECT "app01_publish"."nid", COUNT("app01_book"."nid") AS "count_book" FROM "app01_publish" LEFT OUTER JOIN "app01_book" ON ("app01_publish"."nid" = "app01_book"."publish_id") GROUP BY "app01_publish"."nid" LIMIT 21; ''' # ret = Publish.objects.values("nid").annotate(count_book=Count('book__nid')) # print(ret) # <QuerySet [{'nid': 1, 'count_book': 3}, {'nid': 2, 'count_book': 1}]> # 方式2: ''' SELECT "app01_publish"."name", COUNT("app01_book"."nid") AS "count_book" FROM "app01_publish" LEFT OUTER JOIN "app01_book" ON ("app01_publish"."nid" = "app01_book"."publish_id") GROUP BY "app01_publish"."name" LIMIT 21; ''' # ret = Publish.objects.values("name").annotate(count_book=Count('book__nid')) # print(ret) # <QuerySet [{'name': '人民出版社', 'count_book': 3}, {'name': '河北出版社', 'count_book': 1}]> # 方式3:建议用这种方式,以表的主键作为分组条件: ''' SELECT "app01_publish"."name", COUNT("app01_book"."nid") AS "count_book" FROM "app01_publish" LEFT OUTER JOIN "app01_book" ON ("app01_publish"."nid" = "app01_book"."publish_id") GROUP BY "app01_publish"."nid", "app01_publish"."name" LIMIT 21; ''' ret = Publish.objects.values("nid").annotate(count_book=Count('book__nid')).values('name', 'count_book') print(ret) # <QuerySet [{'name': '人民出版社', 'count_book': 3}, {'name': '河北出版社', 'count_book': 1}]> # 第三种方法取的是nid,但是却能取出name,原因是ret是一个queryset对象,它储存了Publish的所有字段外加count_book # 按照Publish下的每一个字段进行 group by ret = Publish.objects.all().annotate(count_book=Count('book__nid')) print(ret) # 两个publish对象 <QuerySet [<Publish: 人民出版社>, <Publish: 河北出版社>]>
示例2:查询每一个作者的名字以及出版过的书籍的最高价格
def query(request): from django.db.models import Avg, Max, Min, Count ''' select app01_author.name,max(app01_book.price) as highest_price from app01_book inner join app01_book_authors on app01_book.nid = app01_book_authors.book_id inner join app01_author on app01_book_authors.author_id = app01_author.nid group by app01_author.nid ''' ret = Author.objects.values('pk').annotate(max_price = Max('book__price')).values('name','max_price') # ret = Publish.objects.all().annotate(count_book=Count('book__nid')) # 和上面效果一样 print(ret) # <QuerySet [{'name': 'alex', 'max_price': Decimal('102')}, {'name': 'egon', 'max_price': Decimal('102')}]> # 跨表的分组查询的模型 # 每一个的表模型.objects.values('pk').annotate(聚合函数(关联表__统计字段)) return HttpResponse('查询成功')
def query(request): ret = Book.objects.values('pk').annotate(author_count=Count('authors__name')).values('title','author_count') print(ret) # <QuerySet [{'title': '红楼梦', 'author_count': 0}, {'title': '西游记', 'author_count': 0}, {'title': '水浒传', 'author_count': 0}, {'title': '三国演义', 'author_count': 2}]> return HttpResponse('查询成功')
''' 每一个的表模型.objects.values('pk').annotate(聚合函数(关联表__统计字段)).values('表模型的所有字段以及统计字段') 每一个的表模型.objects.annotate(聚合函数(关联表__统计字段)).values('表模型的所有字段以及统计字段') 每一个的表模型.objects.all().annotate(聚合函数(关联表__统计字段)).values('表模型的所有字段以及统计字段') '''
def query(request): # 练习 # 统计每一本以三开头的书籍的作者个数: # ret = Book.objects.filter(title__startswith='三').values('pk').annotate(author_count=Count('authors__name')).values( # 'title', 'author_count') # print(ret) # <QuerySet [{'title': '三国演义', 'author_count': 2}]> # 统计不止一个作者的图书: ret = Book.objects.values('pk').annotate(author_count=Count('authors__nid')).filter(author_count__gt=1).values( 'title', 'author_count') print(ret) # <QuerySet [{'title': '三国演义', 'author_count': 2}]> 原生sql用了having ''' SELECT "app01_book"."title", COUNT("app01_book_authors"."author_id") AS "author_count" FROM "app01_book" LEFT OUTER JOIN "app01_book_authors" ON ("app01_book"."nid" = "app01_book_authors"."book_id") GROUP BY "app01_book"."nid", "app01_book"."title" HAVING COUNT("app01_book_authors"."author_id") > 1 LIMIT 21; ''' return HttpResponse('查询成功')
7. F查询和Q查询
补充
# modles.py # 在book表新加两个字段 read_number = models.IntegerField() comment_number = models.IntegerField() # 在makemigrations的时候提示: ''' 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py ''' # 这是因为已有的数据没有设置这俩值,给他们换一个default值就好了 read_number = models.IntegerField(default=0) comment_number = models.IntegerField(default=0)
def query(request): # F查询可以对两个字段的值做比较用 from django.db.models import F, Q # 查询评论数大于阅读数的书籍 # ret = Book.objects.filter(comment_number__gt=F('read_number')) # print(ret) # <QuerySet [<Book: 西游记>, <Book: 三国演义>]> # F查询支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作 # 给每一个书籍的价格加10 Book.objects.all().update(price=F('price') + 10) return HttpResponse('查询成功')
def query(request): # filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。 # 你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) # 查询名字等于红楼梦而且价格等于101的书籍 # ret = Book.objects.filter(title='红楼梦', price=110) # ret = Book.objects.filter(Q(title='红楼梦') & Q(price=110)) # 查询名字等于红楼梦或价格等于101的书籍 # ret = Book.objects.filter(Q(title='红楼梦') | Q(price=110)) # 查询名字不等于红楼梦或者价格等于101的书籍,但是评论数要大于100。 ret = Book.objects.filter(~Q(title='红楼梦') | Q(price=110),comment_number__gt=100) # 必须Q在前面,自己的键值对在后面 print(ret) # <QuerySet [<Book: 红楼梦>, <Book: 西游记>, <Book: 水浒传>, <Book: 三国演义>]> return HttpResponse('查询成功')