Django框架之第五篇(模型层)

Posted wangcuican

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django框架之第五篇(模型层)相关的知识,希望对你有一定的参考价值。

一、单表查询之神奇的双下划线

查询价格大于200的书籍

models.Book.objects.filter(price__gt=200)  #price>200   大于__gt

查询价格小于200的书籍

models.Book.objects.filter(price__lt=200)  小于__lt

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

models.Book.objects.filter(price__gte=200)  大于等于__gte

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

models.Book.objects.filter(price__lte=200)  小于等于__lte

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

models.Book.objects.filter(price__in=[200,300,600])  在什么之间选择一个__in

查询价格在200到800之间

models.Book.objects.filter(price__range=(200,800))   在什么范围之间__range

查询书籍名字中包含p的

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

查询书籍是以三开头,以p结尾

models.Book.objects.filter(title__startswith=)  开头
models.Book.objects.filter(title__endswith=‘p‘) 结尾

查询出版日期是2017年的

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

二、Django ORM常用字段和参数

创建模型类

常用字段

AutoField

int自增列,必须填入参数 primary_key=True。当model中如果没有写自增列,则自动会创建一个列名为id的列。(可以不写,会自动创建主键为id的字段)

IntegerField

一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)

CharField

字符类型,必须提供max_length参数, max_length表示字符长度。

DateField

日期字段,日期格式  YYYY-MM-DD,相当于Python中的datetime.date()实例。

配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。修改数据记录不会更新该字段
配置上auto_now=True,每次更新数据记录的时候会更新该字段

DateTimeField

日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例

技术图片

 

字段参数

null   用于表示某个字段可以为空。 null = True  设置字段允许为空

unique  如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index  如果db_index=True 则代表着为此字段设置索引。

default  为该字段设置默认值。

关系字段

Foreignkey  一对多

比如书籍和出版社的关系

外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 ‘一对多‘中‘多‘的一方。

ForeignKey可以和其他表做关联关系同时也可以和自身做关联关系。

字段参数

to   设置要关联的表

to_field   设置要关联的表的字段

on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。

models.CASCADE  删除关联数据,与之关联也删除,级联删除

db_constraint   是否在数据库中创建外键约束,默认为True。

技术图片
models.DO_NOTHING
删除关联数据,引发错误IntegrityError


models.PROTECT
删除关联数据,引发错误ProtectedError


models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)


models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)


models.SET

删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
其余字段参数
class MyModel(models.Model):
    user = models.ForeignKey(
        to="User",  #关联User表
        to_field="id"#关联User表id字段
        on_delete=models.SET(func)
    )

技术图片

 

OneToOneField   一对一

比如作者表和作者详情表

通常一对一字段用来扩展已有字段。(通俗的说就是一个人的所有信息不是放在一张表里面的,简单的信息一张表,隐私的信息另一张表,之间通过一对一外键关联),随便建在哪张表上都可以,但是建议建在查询频率高的那一组

to   设置要关联的表。

to_field    设置要关联的字段。

on_delete    当删除关联表中的数据时,当前表与其关联的行的行为。(参考上面的例子)

技术图片

 

ManyToManyField   多对多

比如书籍表和作者表

多对多生成一张新的表,下面设置的字段不会在book表中出现,它仅仅是一个虚拟字段

技术图片

 新生成的表

技术图片

 Django终端打印SQL语句

如果你想知道你对数据库进行操作时,Django内部到底是怎么执行它的sql语句时可以加下面的配置来查看

在Django项目的settings.py文件中,在空白处复制粘贴如下代码:

LOGGING = 
    version: 1,
    disable_existing_loggers: False,
    handlers: 
        console:
            level:DEBUG,
            class:logging.StreamHandler,
        ,
    ,
    loggers: 
        django.db.backends: 
            handlers: [console],
            propagate: True,
            level:DEBUG,
        ,
    

配置好之后,再执行任何对数据库进行操作的语句时,会自动将Django执行的sql语句打印到pycharm终端上

补充:

除了配置外,还可以通过一点.query即可查看查询语句,具体操作如下:

技术图片

 

 多表操作

一对多字段的增删改(book表和publish表是一对多关系,publish_id字段)

增  create

publish_id 传数字   (publish_id是数据库显示的字段名)

models.Book.objects.create(title=三国演义,price=200,publish_id=1)  #publish_id是mysql数据库中显示的字段名

publish直接传出版社对象    publish是models.py设计表字段名

#查询到的publish表主键为2的对象
publish_obj = models.Publish.objects.filter(pk=2).first()

models.Book.objects.create(title=红楼梦,price=250,publish=publish_obj)  #publish是models.py中的字段名

改    update

传数字的

models.Book.objects.filter(pk=2).update(publish_id=3)  #修改publish_id=3

传对象的

#查询publish表中要修改的对象主键值为2
publish_obj = models.Publish.objects.filter(pk=2).first()
#修改
models.Book.objects.filter(pk=1).update(publish=publish_obj)

删  delete

models.Publish.objects.filter(pk=2).delete()  # 默认都是级联更新 级联删除

多对多字段的增删改(book表和author表是多对多关系,新生成一张表)

增   add()

要给主键为1的书籍添加两个作者

传数字
#
先查询主键为1的书籍 book_obj = models.Book.objects.filter(pk=1).first() print(book_obj.book_author) 这个book_author是models.py中设置的字段名#对象点击多对多虚拟字段,会直接跨到多对多的第三张表 book_obj.book_author.add(1) 传数字 给书籍增加主键为1的作者 book_obj.book_author.add(2,3)
传对象 author_obj
= models.Author.objects.filter(pk=1).first() author_obj1 = models.Author.objects.filter(pk=2).first() author_obj2 = models.Author.objects.filter(pk=3).first() book_obj.book_author.add(author_obj) 传对象 book_obj.book_author.add(author_obj1,author_obj2) add() 是给书籍添加作者 括号内既可以传数字也可以传对象,但是不要混着用 并且支持一次性传多个 逗号隔开即可

多对多表(mysql)

技术图片

 

 改  set()    #set里面传的是一个可迭代对象(******)

将主键为1的书籍对象作者修改为2,3

传数字
#
也是先查询出主键为1的书籍 book_obj = models.Book.objects.filter(pk=1).first() book_obj.book_author.set([2,]) book_obj.book_author.set([2,3])

传对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.book_author.set([author_obj,])
book_obj.book_author.set([author_obj, author_obj1, author_obj2])
set()括号内 需要传一个可迭代对象 
可迭代对象中 可以是多个数字组合
也可以是多个对象组合
但是不要混着用!!!

上一步主键为1的书籍绑定了三个作者,现在改为两个,就是把多余的删除掉

技术图片

 删    remove()

删除主键值为1的书籍作者2

传数字
#也是先查询出主键为1的书籍
book_obj = models.Book.objects.filter(pk=1).first()

book_obj.book_author.remove(2)
book_obj.book_author.remove(2,3)

传对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.book_author.remove(author_obj)
book_obj.book_author.remove(author_obj1,author_obj2)

remove()括号内既可以传数字 也可以传对象 
并且支持传对个 逗号隔开即可

删除作者2

技术图片

 清空  clear()

 将某本书跟作者的关系全部清空

book_obj = models.Book.objects.filter(pk=1).first()
book_obj.book_author.clear()  # 清空当前书籍与作者的所有关系

多对多增删改总结

add()
set()
remove()
上面三个都支持传数字 或者对象 并且可以传多个 但是set需要传可迭代对象
    
clear()
clear括号内不需要传任何参数

跨表查询(******)  就是前面缺的查询

正向与反向的概念

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

基于对象的跨表查询(子查询:将一张表的查询结果当做另一个查询语句的条件)

1.查询书籍id是1的出版社名称   (一对多查询)正向查询

先查询出id为1的书籍对象
book_obj = models.Book.objects.filter(pk=1).first()

print(book_obj.publish.name) #正向查询,因为publish在book表中,直接对象.字段名就能获取到
print(book_obj.publish.addr)

2.查询书籍id是2的作者姓名   (多对多查询) 书籍和作者是多对多关系

先查询出书籍id为2的对象
book_obj = models.Book.objects.filter(pk=2).first()

print(book_obj.book_author)  #正向查询 #app01.Author.None
print(book_obj.book_author.all())   #<QuerySet [<Author: jason>, <Author: egon>]>
res =book_obj.book_author.all()   
    for r in res:
        print(r.name)    #jason   egon

3.查询作者是jason的家庭住址  (一对一查询)正向查询

#先查作者名的jason的对象
author_obj = models.Author.objects.filter(name=jason).first()

print(author_obj.author_detail.addr) #正向查询  在Author表中有author_detil字段

4.查询出版社是东方出版社出版的书籍  (一对多查询)反向查询

#先查询出出版社是东方出版社的对象
publish_obj = models.Publish.objects.filter(name=东方出版社).first()

print(publish_obj.book_set)   #app01.Book.None   #反向查询是小写表名,当你反向查询结果是多个的时候,就需要加_set
print(publish_obj.book_set.all())  #<QuerySet [<Book: 三国演义>, <Book: 西游记>]>   #需要加.all()才能获取到结果

res = publish_obj.book_set.all()
    for r in res:
        print(r.title)   #三国演义   西游记

5.查询作者是jason的写过的所有的书籍   (多对多查询)反向查询

#先查询作者是jason的对象
author_obj = models.Author.objects.filter(name=jason).first()

print(author_obj.book_set)  # app01.Book.None
print(author_obj.book_set.all())  #<QuerySet [<Book: 三国演义>, <Book: 红楼梦>]>

6.查询电话号码是130的作者姓名  (一对一查询)反向查询

#先查询电话号码是130的对象
author_detail_obj = models.AuthorDetail.objects.filter(phone=130).first()

print(author_detail_obj.author.name)  #反向查询
print(author_detail_obj.author.addr)

需要注意:在反向查询的结果是多个的时候,需要加_set(******)

7.查询书籍id为1的作者的电话号码  (多对多查询)正向查询

book_obj = models.Book.objects.filter(pk=1).first()
author_list = book_obj.book_author.all()  #多个结果时候用all()
for author_obj in author_list:
    print(author_obj.author_detail.phone)  直接.就能出结果

基于双下划线的跨表查询

-正向:按字段,跨表可以在filter,也可以在values中
-反向:按表名小写,跨表可以在filter,也可以在values中

1.查询jason作者的手机号  (一对一查询)正向查询

models.Author.objects.filter(name=jason).values(author_detail__phone)   #author_detail是Author的字段(所以是正向查询)

2.查询jason作者的手机号    反向查询

models.AuthorDetail.objects.filter(author__name=jason).values(phone)  #反向查询  author是小写表名

3.查询jason这个作者的年龄和手机号  (一对一查询)正向查询

models.Author.objects.filter(name=jason).values(‘age‘,‘author_detail__phone‘)

4.查询jason这个作者的年龄和手机号  反向查询

models.AuthorDetail.objects.filter(author__name=jason).values(phone,author__age)

5.查询手机号是130的作者年龄    (一对一查询)正向查询

models.Author.objects.filter(author_detail__phone=130).values(age)

6.查询手机号是130的作者年龄    反向查询

models.AuthorDetail.objects.filter(phone=130).values(author__age)

7.查询书籍id是1的作者的电话号码

models.Book.objects.filter(pk=1).values(book_author__author_detail__phone)
models.Book.objects.filter(pk=1).values(‘外键字段1__外键字段2__外键字段3__普通字段‘)
只要表里面有外键字段 你就可以无限制跨多张表

 

1.查询出版社为北京出版社的所有图书的名字和价格  (一对多查询)正向

models.Book.objects(publish__name=北京出版社).values(title,price)

2.查询出版社为北京出版社的所有图书的名字和价格  (一对多查询)反向

models.Publish.objects.filter(name=北京出版社).values(book__name,book__price)

3.查询北京出版社出版的价格大于19的书

models.Book.objects.filter(price__gt=19,publish__name=北京出版社).values(title)

聚合查询(aggregate)

from django.db.models import Max,Min,Count,Avg,Sum   #查询总和,平均,最大,最小

res = models.Book.objects.aggregate(Sum(price))
res1 = models.Book.objects.aggregate(Avg(price))
res2 = models.Book.objects.aggregate(Count(price))
res3 = models.Book.objects.aggregate(Max(price))
res4 = models.Book.objects.aggregate(Min(price))
res5 = models.Book.objects.aggregate(Max(price),Min(price),Count(pk),Avg(price),Sum(price))  #也可以放在一个里面写

分组查询 (annotate)

 1.统计每一本书的作者个数

from django.db.models import Max, Min, Count, Avg, Sum
res
= models.Book.objects.annotate(author_num = Count(authors)).values(author_num,title)
 #book表根据authors分组求和,author_num是取的别名,values是控制台打印的值

2.统计每个出版社卖的最便宜的书的价格

res = models.Publish.objects.annotate(mmp = Min(book__price)).values(name,mmp)

3.统计不止一个作者的图书

res = models.Book.objects.annotate(author_num=Count(authors)).filter(author_num__gt=1)

4.查询每个作者出的书的总价格

res = models.Author.objects.annotate(sp=Sum(book__price)).values(name,sp)

F查询

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

查询库存量大于卖出量的书籍

from django.db.models import F
res
= models.Book.objects.filter(kucun__gt=F(maichu)) #kucun和maichu都是Book表的字段

将书籍库存数全部增加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(新款)))
models.Book.objects.update(title = F(title)+新款)  # 不能这么写

Q查询  (filter里面条件都是与,Q支持与或非)

 查询书籍名称是三国演义或者价格是444

from django.db.models import Q
res = models.Book.objects.filter(title=三国演义,price=444.44)  # filter只支持and关系
res1 = models.Book.objects.filter(Q(title=三国演义),Q(price=444))  # 如果用逗号 那么还是and关系
res2 = models.Book.objects.filter(Q(title=三国演义)|Q(price=444))   #或者关系
res3 = models.Book.objects.filter(~Q(title=三国演义)|Q(price=444))  #查询除了title是三国演义,或者价格是444的书籍

 

Q的高级用法

from django.db.models import 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关系
print(res)

 

以上是关于Django框架之第五篇(模型层)的主要内容,如果未能解决你的问题,请参考以下文章

Django框架

Django 框架入门

Django框架

Django 框架

Django框架

Django框架