import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app02 import models
# 1.all() 获取所有的数据 QuerySet [对象,对象...]
ret = models.User.objects.all() # ]>
# 2.get() 获取一条数据 查询不到或者多个结果会报错 返回对象
# 另外,返回的结果是models.py中类的__str__方法的结果,我们也可以自定义__str__方法来自定义返回结果形式
ret = models.User.objects.get(pk=1) # User object
# 3.filter() 获取满足条件的所有数据 QuerySet [对象,对象...]
# 另外,返回结果是models.py中类的__repr__方法的结果,我们也可以自定义__repr__方法来自定义返回结果形式
ret = models.User.objects.filter(gender=True) # , ]>
# 4.exclude() 获取不满足条件的所有数据 QuerySet [对象,对象...]
ret = models.User.objects.exclude(gender=True)
###如上,定义models.py中User类的__str__ 和 __repr__方法###
# 5.order_by() 排序 默认升序,字段加'-'号即降序排 QuerySet [对象,对象]
ret = models.User.objects.order_by('uid') # , , ]>
ret = models.User.objects.order_by('-uid') # , , ]>
ret = models.User.objects.order_by('age','-uid') # 按照多个字段排序,优先顺序从左到右
# 6.reverse() 反转 对已经排序对QuerySet生效 QuerySet [对象,对象...]
ret = models.User.objects.all().order_by('uid').reverse() # , , ]>
# 7.values() 不加参数,则获取所有对字典名和值 QuerySet [字典,字典...]
# 指定参数,获取指定字段的名和值
# python中直接通过for循环来操作字典来取值即可
ret = models.User.objects.all().values() # ), 'uid': 1, 'name': 'tom', 'phone': '12345678912'}, {'age': 18, 'gender': False, 'birth': datetime.datetime(2020, 2, 24, 5, 24, 24, 995000, tzinfo=), 'uid': 2, 'name': 'Lily', 'phone': '13928263638'}, {'age': 20, 'gender': True, 'birth': datetime.datetime(2020, 2, 24, 5, 25, 0, 460000, tzinfo=), 'uid': 3, 'name': 'jack', 'phone': '13628366382'}]>
ret = models.User.objects.all().values('uid') #
# 8.values_list() 同values(),不同的是返回结果的形式 QuerySet [元组,元组...]
ret = models.User.objects.all().values_list() # ), '12345678912', True), (2, 'Lily', 18, datetime.datetime(2020, 2, 24, 5, 24, 24, 995000, tzinfo=), '13928263638', False), (3, 'jack', 20, datetime.datetime(2020, 2, 24, 5, 25, 0, 460000, tzinfo=), '13628366382', True)]>
# 9.distinct 去重,不是针对字段的,而是针对QuerySet里的元素的 QuerySet
# 因此可以获取指定字段后再进行去重
ret = models.User.objects.all().distinct() # 只要有一个字段不同,就不会去重
ret = models.User.objects.all().values('gender').distinct() #
# 10.first 取第一个元素 没有的话返回None
ret = models.User.objects.first() #
ret = models.User.objects.filter(name='laowang').first() # None
# 11.last 取最后一个元素 没有的话返回None
ret = models.User.objects.first()
ret = models.User.objects.filter(name='laowang').first()
# 12.count() 计数 count比len效率高(len()相当于调用__len__方法)
ret = models.User.objects.all().count() # 3
# 13.exists() 判断查询结果是否存在 存在-True 不存在-False
ret = models.User.objects.filter(name='tom').exists() # True
ret = models.User.objects.filter(name='laowang').exists() # False
按照返回的结果对方法进行分类:
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app02 import models
ret = models.User.objects.filter(pk=2)
# __gt 大于
ret = models.User.objects.filter(pk__gt=2) # ]>
# __gte 大于等于
ret = models.User.objects.filter(pk__gte=2) # , ]>
# __lt 小于
ret = models.User.objects.filter(pk__lt=2) # ]>
# __lte 小于等于
ret = models.User.objects.filter(pk__lte=2) # , ]>
# __in 成员判断
ret = models.User.objects.filter(pk__in=[1,3]) # , ]>
# __range 范围
ret = models.User.objects.filter(pk__range=[1,3]) # , , ]>
# __contain 包含 相当于sql中的like
ret = models.User.objects.filter(name__contains='om') # ]>
# __icontains 包含(忽略大小写)
ret = models.User.objects.filter(name__icontains='OM') # ]>
# __startswith 以...开头
ret = models.User.objects.filter(name__startswith='j') # ]>
# __istartswith 以...开头(忽略大小写)
ret = models.User.objects.filter(name__istartswith='j') # 忽略大小写
# __endswith 以...结尾
ret = models.User.objects.filter(name__endswith='m') # ]>
# __iendswith 以...结尾(忽略大小写)
ret = models.User.objects.filter(name__iendswith='m') # 忽略大小写
# __isnull 字段是否为空,True表示要为空的;False表示要非空的
ret = models.User.objects.filter(age__isnull=True) #
# __year 取时间中的满足year的数据;不支持month,但可以用contains来实现筛选
ret = models.User.objects.filter(birth__year=2020) # , , ]>
print(ret)
注:双下划线方法操作的字段名,都对应models里各个类的属性名,而不是数据表中的字段名(当然,两者名称也存在相同的情况)
OneToOneField(ForeignKey)
to, # 要进行关联的表名
to_field=None # 要关联的表中的字段名称
on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为
###### 对于一对一 ######
# 1. 一对一其实就是 一对多 + 唯一索引
# 2.当两个类之间有继承关系时,默认会创建一个一对一字段
# 如下会在A表中额外增加一个c_ptr_id列且唯一:
class C(models.Model):
nid = models.AutoField(primary_key=True)
part = models.CharField(max_length=12)
class A(C):
id = models.AutoField(primary_key=True)
code = models.CharField(max_length=1)
ForeignKey(ForeignObject) # ForeignObject(RelatedField)
to, # 要进行关联的表名
to_field=None, # 要关联的表中的字段名称
on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为
- models.CASCADE,删除关联数据,与之关联也删除
- models.DO_NOTHING,删除关联数据,引发错误IntegrityError
- models.PROTECT,删除关联数据,引发错误ProtectedError
- models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
- models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
- models.SET,删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
def func():
return 10
class MyModel(models.Model):
user = models.ForeignKey(
to="User",
to_field="id"
on_delete=models.SET(func),)
related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5}
from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
db_constraint=True # 是否在数据库中创建外键约束
parent_link=False # 在Admin中是否显示关联数据
如下models,通过ForeignKey
关键字,指定该外键关联的表
from django.db import models
# Create your models here.
class Publisher(models.Model):
name = models.CharField(max_length=32)
def __str__(self):
return self.name
__repr__ = __str__
class Book(models.Model):
title = models.CharField(max_length=32)
pub = models.ForeignKey('Publisher',on_delete=models.CASCADE)
def __str__(self):
return self.title
__repr__ = __str__
向表中填充数据
mysql> select * from app01_book;
+----+---------------------------+--------+
| id | title | pub_id |
+----+---------------------------+--------+
| 7 | python3从入门到放弃 | 15 |
| 8 | JAVA从入门到出家 | 15 |
| 12 | php从入门到跳楼 | 10 |
+----+---------------------------+--------+
3 rows in set (0.00 sec)
mysql> select * from app01_publisher;
+----+-----------------------------+
| id | name |
+----+-----------------------------+
| 9 | 美国人民出版社 |
| 10 | 新新欧美人民出版社 |
| 15 | 美少女出版社 |
| 16 | ChinaT出版社 |
+----+-----------------------------+
4 rows in set (0.00 sec)
mysql>
通过外键,可以实现基于对象或者字段的查询,同时也可以实现正向和反向两个方向的查询
正向查询:从多的一方
向 “一”的一方
去查询数据(“多”—>“一”);对象.关联字段.字段
反向查询:从”一“的一方
向 多的一方
去查询数据(“多”—>“一”);对象.表名_set
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app01 import models
## 基于对象的查询
# 正向查找 obj.ForeignKey.字段
book_obj = models.Book.objects.get(pk=7)
print(book_obj) # python3从入门到放弃
print(book_obj.pub) # 美少女出版社
# 反向查找 obj.表名_set
# 或者在ForeignKey字段的参数中指定 related_name='books',那么在反向查找时,就可以用books代替book_set
pub_obj = models.Publisher.objects.get(pk=15)
print(pub_obj) # 美少女出版社
print(pub_obj.book_set,type(pub_obj.book_set)) # 关系管理对象 app01.Book.None .RelatedManager'>
print(pub_obj.book_set.all()) #
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app01 import models
## 基于字段的查询
# 正向查询 关联字段__字段
ret = models.Book.objects.filter(pub__name='美少女出版社')
print(ret) #
# 反向查询 表名__字段
ret = models.Publisher.objects.filter(book__title='python3从入门到放弃')
print(ret) #
于上述定义的表中插入数据
视图函数
def book_list(request):
all_books = models.Book.objects.all()
for book in all_books:
print(book.pk,book.title,book.pub,type(book.pub))
return HttpResponse('ok')
打印结果
1 《活着》 Publisher object
2 《许三观卖血》 Publisher object
3 《生吞》 Publisher object
4 《达芬奇密码》 Publisher object
5 《天使与魔鬼》 Publisher object
6 《穆斯林的葬礼》 Publisher object
由上述结果可以得知book.pub
拿到的是该外键关联的Publisher表的对象,然后就可以通过obj.属性
d 的方法,拿到Publisher对象的属性值,也就是publisher表中的字段值
视图函数
def book_list(request):
all_books = models.Book.objects.all()
for book in all_books:
# print(book.pk,book.title,book.pub,type(book.pub))
print(book.pub.pk,book.pub.name)
return HttpResponse('ok')
打印结果
2 北京大学出版社
2 北京大学出版社
4 新新人民出版社
9 美国人民出版社
9 美国人民出版社
另外也可以通过book.pub_id
直接获取与之关联的外表的id(pub_id是数据库的中的字段,对应model中Book的pub)
视图函数
def book_list(request):
all_books = models.Book.objects.all()
for book in all_books:
# print(book.pk,book.title,book.pub,type(book.pub))
# print(book.pub.pk,book.pub.name)
print(book.pub_id)
return HttpResponse('ok')
打印结果
1
2
2
4
9
9
了解 __str__
函数
通过ORM对表数据进行编辑、修改操作
方法一
通过orm重新赋值字段数据,最后通过.save()
对所做的修改进行保存
book_obj.save()
def post(self,request,pk):
# 接收获取前端数据
book_obj = models.Book.objects.get(pk=pk)
title = request.POST.get('title')
pub_id = request.POST.get('pub_id')
# 修改字段值
book_obj.title = title
book_obj.pub_id = pub_id
# 保存所做修改
book_obj.save()
return redirect(reverse('app01:book'))
其中,.save()
的方法相当于对所有字段进行一次更新保存,因此,如果当一张表的字段比较多时,此方法的效率就比较低,那么我们了解第二种方法
方法二
通过ORM的filter
方法筛选出要修改的对象,然后通过update
方法对指定的字段进行更新、修改
models.Book.objects.filter(pk=pk).update(title=title,pub_id=pub_id)
def post(self,request,pk):
# 接收获取前端数据
title = request.POST.get('title')
pub_id = request.POST.get('pub_id')
# 对指定字段进行更新
models.Book.objects.filter(pk=pk).update(title=title,pub_id=pub_id)
return redirect(reverse('app01:book'))
其中,此种方式,仅会对update
里指定的两个字段进行更新修改,如果字段较多的情况下,此种方法的效率略高
另外,需要主要,通过filter
方法筛选出来的对象结果可能不止一个,比如多个点情况,就可以实现批量修改(即同时对多个满足筛选条件的对象进行字段更新);具体使用根据实际情况来用即可
ManyToManyField(RelatedField)
to, # 要进行关联的表名
related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5}
from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
# 做如下操作时,不同的symmetrical会有不同的可选字段
models.BB.objects.filter(...)
# 可选字段有:code, id, m1
class BB(models.Model):
code = models.CharField(max_length=12)
m1 = models.ManyToManyField('self',symmetrical=True)
# 可选字段有: bb, code, id, m1
class BB(models.Model):
code = models.CharField(max_length=12)
m1 = models.ManyToManyField('self',symmetrical=False)
through=None, # 自定义第三张表时,使用字段用于指定关系表
through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)
db_constraint=True, # 是否在数据库中创建外键约束
db_table=None, # 默认创建第三张表时,数据库中表的名称
如下Author表中定义多对多
关系,其中books = models.ManyToManyField(to='Book')
即定义一个多对多的属性到Book表,而Author类中的books不会生成字段,但是会生成第三张表,用来表示Author和Book两张表之间多对多的关系
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=32)
pub = models.ForeignKey('Publisher',on_delete=models.CASCADE)
def __str__(self):
return self.title
__repr__ = __str__
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToManyField(to='Book') # 定义一个ManyToMany属性,这个属性不会生成字段,但是会生成第三张表
def show_books(self):
book_lst = ["《{}》".format(book.title) for book in self.books.all()]
return ' '.join(book_lst)
def __str__(self):
return self.name
__repr__ = __str__
向表中插入数据如下
mysql> select * from app01_book;
+----+---------------------------+--------+
| id | title | pub_id |
+----+---------------------------+--------+
| 7 | python3从入门到放弃 | 15 |
| 8 | JAVA从入门到出家 | 15 |
| 12 | php从入门到跳楼 | 10 |
+----+---------------------------+--------+
3 rows in set (0.00 sec)
mysql> select * from app01_author;
+----+--------+
| id | name |
+----+--------+
| 1 | 老王 |
| 2 | 老刘 |
+----+--------+
2 rows in set (0.00 sec)
mysql> select * from app01_author_books;
+----+-----------+---------+
| id | author_id | book_id |
+----+-----------+---------+
| 1 | 1 | 7 |
| 3 | 1 | 8 |
| 2 | 2 | 7 |
| 4 | 2 | 8 |
+----+-----------+---------+
4 rows in set (0.00 sec)
mysql>
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。
它存在于下面两种情况:
简单来说就是当 点
后面的对象 可能存在多个 的时候就可以使用以下的方法
也就是说,点后面的对象存在多个时,返回的便是一个关系管理对象,以下便是关系管理对象的方法
语法:对象.关联字段.方法
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app01 import models
## 正向查询 对象.关联字段.方法
author_obj = models.Author.objects.get(name='老王')
# 1.all() 获取所有关联的对象
all_books = author_obj.books.all()
print(all_books) #
# 2.set() 设置多对多关系 重新设置
author_obj.books.set([]) # 设置一个空列表,那么和"老王"关联的book就没有了
# 3.add() 新增关系
author_obj.books.add(7,8) # 将id为7、8的书籍重新关联到"老王"身上(通过关联id实现)
author_obj.books.add(*models.Book.objects.filter(id__in=[7,8])) # 将id为7、8的书籍重新关联到"老王"身上(通过关联id为7、8的对象实现)
# 4.remove() 删除关系
author_obj.books.remove(7,8) # 将id为7、8的书籍重从"老王"身上删除(通过删除关联的id实现)
author_obj.books.remove(*models.Book.objects.filter(id__in=[7,8])) # 将id为7、8的书籍从"老王"身上删除(通过删除关联id为7、8的对象实现)
# 5.clear() 清空所有的关系
author_obj.books.clear() # 清空"老王"身上关联的所有的关系
# 说明:对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。
# 6.create() 新增关系
author_obj.books.create(title='C语言永不为奴',pub_id=10) # 相当于执行了两步操作:1.新建book;2.关联该book给'老王'
'''
mysql> select * from app01_book;
+----+---------------------------+--------+
| id | title | pub_id |
+----+---------------------------+--------+
| 7 | python3从入门到放弃 | 15 |
| 8 | JAVA从入门到出家 | 15 |
| 12 | php从入门到跳楼 | 10 |
| 13 | C语言永不为奴 | 10 |
+----+---------------------------+--------+
4 rows in set (0.01 sec)
mysql> select * from app01_author_books;
+----+-----------+---------+
| id | author_id | book_id |
+----+-----------+---------+
| 21 | 1 | 7 |
| 20 | 1 | 8 |
| 22 | 1 | 13 |
| 2 | 2 | 7 |
| 4 | 2 | 8 |
+----+-----------+---------+
5 rows in set (0.00 sec)
mysql>
'''
说明:
对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
语法:对象.关联表_set.方法
# 反向查 对象.关联表_set.方法
book_obj = models.Book.objects.get(title='python3从入门到放弃')
# 1. 1.all() 获取所有关联的对象
all_authors = book_obj.author_set.all()
print(all_authors) #
# 其他方法同正向操作的方法相同
说明:
正向、反向操作的方法都相同,只是语法有所不同
当在数据库中出现两张表中的数据存在多对多的关系时,常常是要引入第三张表来做两者关系的对应
比如,作者和作品的对应关系,一个作者可能写多本书,而一本书也可能又多个作者联合协作,这就是典型的多对多的关系
books = models.ManyToManyField(to='Book')
需要注意的是,models.ManyToManyField(to='Book')
这个属性不会生成字段,但是会生成第三张表,用来表示本表和Book表的对应关系
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=32)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=32)
pub = models.ForeignKey('Publisher',on_delete=models.CASCADE)
# 上面两个表是已存在的,Author对象是新建的
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToManyField(to='Book') # 定义一个ManyToMany属性,这个属性不会生成字段,但是会生成第三张表
执行迁移命令后看生成的新表
mysql> show tables;
+----------------------------+
| Tables_in_booker |
+----------------------------+
| app01_author |
| app01_author_books |
| app01_book |
| app01_publisher |
| ...... |
+----------------------------+
15 rows in set (0.00 sec)
mysql>
如结果,不仅生成了一个app01_author
表,同时还生成了一个用来表示author与book对应关系的app01_author_books
表
查看两个表的表结构如下:
mysql> desc app01_author;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(32) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> desc app01_author_books;
+-----------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| author_id | int(11) | NO | MUL | NULL | |
| book_id | int(11) | NO | MUL | NULL | |
+-----------+---------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql>
三张表插入对应数据
book表
mysql> select * from app01_book;
+----+---------------------------------+--------+
| id | title | pub_id |
+----+---------------------------------+--------+
| 7 | 《python3从入门到放弃》 | 15 |
| 8 | 《JAVA从入门到出家》 | 16 |
+----+---------------------------------+--------+
2 rows in set (0.00 sec)
author表
+----+--------+
| id | name |
+----+--------+
| 1 | 老王 |
| 2 | 老刘 |
+----+--------+
2 rows in set (0.00 sec)
author_books表
mysql> select * from app01_author_books;
+----+-----------+---------+
| id | author_id | book_id |
+----+-----------+---------+
| 1 | 1 | 7 |
| 3 | 1 | 8 |
| 2 | 2 | 7 |
| 4 | 2 | 8 |
+----+-----------+---------+
4 rows in set (0.00 sec)
views函数
def author_list(request):
all_authors = models.Author.objects.all()
for author in all_authors:
print(author.pk,author.name,'-',author.books,type(author.books))
访问对应页面,触发view函数后,打印结果
1 老王 - app01.Book.None .ManyRelatedManager'>
2 老刘 - app01.Book.None .ManyRelatedManager'>
关系管理对象:
app01.Book.None
.all()
获取所有关联的对象views函数
def author_list(request):
all_authors = models.Author.objects.all()
for author in all_authors:
# print(author.pk,author.name,'-',author.books,type(author.books))
print(author.books.all(),type(author.books.all()))
访问对应页面,触发view函数后,打印结果
, ]>
, ]>
如上,获取到了Book的QuerySet结果;之后就可以通过从QuerySet中取对应的数据提交给前端即可
路由文件配置
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'author/$',views.author_list,name="author"),
]
视图函数
def author_list(request):
all_authors = models.Author.objects.all()
return render(request,'author.html',{'all_authors':all_authors})
模版文件
<table class="table table-striped table-hover">
<thead>
<tr>
<th>序号th>
<th>IDth>
<th>作者姓名th>
<th>作品th>
<th>操作th>
<th>出版社th>
tr>
thead>
<tbody>
{% for author in all_authors %}
<tr>
<td>{{ forloop.counter }}td>
<td>{{ author.pk }}td>
<td>{{ author.name }}td>
<td>{% for book in author.books.all %}
《{{ book.title }}》
{% endfor %}
td>
tr>
{% endfor %}
tbody>
table>
说明:通过for循环获取了书的名字
models类中定义方法
或者直接在models.py的author类中定义show_books的方法,然后在前端直接调用该方法返回的结果即可
models.py
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToManyField(to='Book') # 定义一个ManyToMany属性,这个属性不会生成字段,但是会生成第三张表
def show_books(self):
book_lst = ["《{}》".format(book.title) for book in self.books.all()]
return ' '.join(book_lst)
模版文件
{% for author in all_authors %}
<tr>
<td>{{ author.show_books }}td>
tr>
{% endfor %}
展示结果
说明:
如果删除一个作者“老王”,不仅作者表中的“老王”被删掉,而且 关系对应表 中,和老王有关的映射记录也会被删掉
通过关系管理对象新增数据
# 若获取的是一个列表,则需要用getlist方法
book_id = request.POST.getlist('book_id')
# 插入数据,
# 同时创建了作者数据,获取了一个author_obj的作者对象
author_obj = models.Author.objects.create(name=author_name)
# set 设置多对多关系
# 通过author_obj对象调用关系管理对象的set方法,来新增数据,创建对应关系
author_obj.books.set(book_id)
def author_add(request):
if request.method == 'POST':
# 获取用户提交数据
author_name = request.POST.get('author_name')
book_id = request.POST.getlist('book_id') # 若获取的是一个列表,则需要用getlist方法 [8,9]
# 插入数据,
# 同时创建了作者数据,获取了一个author_obj的作者对象
author_obj = models.Author.objects.create(name=author_name)
# set 设置多对多关系
# 通过author_obj对象调用关系管理对象的set方法,来新增数据,创建对应关系
author_obj.books.set(book_id)
return redirect(reverse('app01:author'))
all_books = models.Book.objects.all()
return render(request,'author_add.html',{'all_books':all_books})
建表语句
from django.db import models
# Create your models here.
class Publisher(models.Model):
name = models.CharField(max_length=32)
def __str__(self):
return self.name
__repr__ = __str__
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5,decimal_places=2)
pub = models.ForeignKey('Publisher',on_delete=models.CASCADE)
def __str__(self):
return self.title
__repr__ = __str__
class Author(models.Model):
name = models.CharField(max_length=32)
books = models.ManyToManyField(to='Book') # 定义一个ManyToMany属性,这个属性不会生成字段,但是会生成第三张表
def show_books(self):
book_lst = ["《{}》".format(book.title) for book in self.books.all()]
return ' '.join(book_lst)
def __str__(self):
return self.name
__repr__ = __str__
表数据内容
mysql> melect * from app01_book;
+----+---------------------------+--------+--------+
| id | title | pub_id | price |
+----+---------------------------+--------+--------+
| 7 | python3从入门到放弃 | 15 | 100.00 |
| 8 | JAVA从入门到出家 | 15 | 150.00 |
| 12 | php从入门到跳楼 | 10 | 50.00 |
| 13 | C语言永不为奴 | 10 | 100.00 |
+----+---------------------------+--------+--------+
4 rows in set (0.00 sec)
mysql> select * from app01_publisher;
+----+-----------------------------+
| id | name |
+----+-----------------------------+
| 9 | 美国人民出版社 |
| 10 | 新新欧美人民出版社 |
| 15 | 美少女出版社 |
| 16 | ChinaT出版社 |
+----+-----------------------------+
4 rows in set (0.00 sec)
mysql> select * from app01_author;
+----+--------+
| id | name |
+----+--------+
| 1 | 老王 |
| 2 | 老刘 |
+----+--------+
2 rows in set (0.00 sec)
mysql> select * from app01_author_books;
+----+-----------+---------+
| id | author_id | book_id |
+----+-----------+---------+
| 21 | 1 | 7 |
| 20 | 1 | 8 |
| 22 | 1 | 13 |
| 2 | 2 | 7 |
| 4 | 2 | 8 |
+----+-----------+---------+
5 rows in set (0.00 sec)
mysql>
关键方法
models.Book.objects.aggregate(聚合函数('字段名'))
需要注意的是,aggregate()
是QuerySet
的一个终止子句,意思是说,它返回的结果不再是QuerySet
,而是一个包含一些键值对的字典
。
键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。
因此,对获取的结果再进行操作的时候,就不能用QuerySet
的方法了,而是通过字典
的方法来获取、操作
用到的内置函数
from django.db.models import Max,Min,Sum,Avg,Count
查询方法
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app01 import models
# 导入需要使用的内置函数
from django.db.models import Max,Min,Sum,Avg,Count
# 1.查询所有记录中最大价格
ret = models.Book.objects.aggregate(Max('price'))
print(ret) # {'price__max': Decimal('150.00')}
# 2.指定聚合值的键名;并可以获取多个聚合结果
ret = models.Book.objects.aggregate(max=Max('price'),min=Min('price'))
print(ret) # {'max': Decimal('150.00'), 'min': Decimal('50.00')}
# 3.对过滤后的对象进行聚查询
ret = models.Book.objects.filter(pk__gt=8).aggregate(max=Max('price'))
print(ret) # {'max': Decimal('100.00')}
关键方法
models.Book.objects.annotate(聚合函数('字段名'))
用到的内置函数
from django.db.models import Max,Min,Sum,Avg,Count
查询方法
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app01 import models
from django.db.models import Max,Min,Sum,Avg,Count
## 分组查询
# 1.统计每一本书的作者个数
ret = models.Book.objects.annotate(Count('author')).values()
print(ret)
#
# 'author_count'字段即每本书作者对个数
print(ret.values('title','author__count'))
#
# 2.统计每个出版社卖对最便宜的书的价格
ret = models.Publisher.objects.annotate(Min('book__price')).values()
print(ret)
#
print(ret.values('name','book__price__min'))
#
ret = models.Book.objects.values('pub').annotate(min=Min('price'))
print(ret)
#
print(ret.values('pub__name','min'))
#
# 3.统计不止一个作者的图书
ret = models.Book.objects.annotate(count=Count('author')).values() # 获取所有图书的作者个数
print(ret)
#
ret = models.Book.objects.annotate(count=Count('author')).values().filter(count__gt=1) # 对作者个数进行筛选,选出大于1个的
print(ret)
#
# 4.查询每个作者出的书的总价格
ret = models.Author.objects.annotate(Sum('books__price')).values()
print(ret)
#
ret = models.Book.objects.values('author__name').annotate(Sum('price'))
print(ret)
#
数据准备
建表语句
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5,decimal_places=2)
stock = models.IntegerField()
sale = models.IntegerField()
pub = models.ForeignKey('Publisher',on_delete=models.CASCADE)
def __str__(self):
return self.title
__repr__ = __str__
表数据内容
mysql> select * from app01_book;
+----+---------------------------+--------+--------+------+-------+
| id | title | pub_id | price | sale | stock |
+----+---------------------------+--------+--------+------+-------+
| 7 | python3从入门到放弃 | 15 | 100.00 | 30 | 70 |
| 8 | JAVA从入门到出家 | 15 | 150.00 | 40 | 60 |
| 12 | php从入门到跳楼 | 10 | 50.00 | 50 | 500 |
| 13 | C语言永不为奴 | 10 | 100.00 | 60 | 40 |
+----+---------------------------+--------+--------+------+-------+
4 rows in set (0.00 sec)
mysql>
使用方法
简单来说,F()方法/F查询,可以在查询中引用字段,比较同一个model实例中的两个不同字段的值,也就是比较一个表中两个字段的值;比如比较上述书籍表的库存量和销售量
除此之外,F方法还能对字段进行加减乘除四则运算
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app01 import models
from django.db.models import F,Q
# 1.查询销售量大于库存量的图书
ret = models.Book.objects.filter(sale__gt=F('stock'))
print(ret) #
# 2.将sale字段进行算术运算
ret = models.Book.objects.update(sale=F('sale')*2+5)
'''执行运算后的结果
mysql> select * from app01_book;
+----+---------------------------+--------+--------+------+-------+
| id | title | pub_id | price | sale | stock |
+----+---------------------------+--------+--------+------+-------+
| 7 | python3从入门到放弃 | 15 | 100.00 | 65 | 70 |
| 8 | JAVA从入门到出家 | 15 | 150.00 | 85 | 60 |
| 12 | php从入门到跳楼 | 10 | 50.00 | 105 | 500 |
| 13 | C语言永不为奴 | 10 | 100.00 | 125 | 40 |
+----+---------------------------+--------+--------+------+-------+
4 rows in set (0.00 sec)
'''
filter()
等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR
语句),你可以使用Q对象
。
简而言之,Q方法可以实现更复杂的筛选条件
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanager.settings")
import django
django.setup()
from app01 import models
from django.db.models import F,Q
# 且/and关系 取5
ret = models.Book.objects.filter(pk__gt=5,pk__lt=10)
print(ret) #
# 1.且/and关系 取5
ret = models.Book.objects.filter(Q(pk__gt=5) & Q(pk__lt=10))
print(ret) #
# 2.或/or关系 取pk>12或者pk<8的图书
ret = models.Book.objects.filter(Q(pk__gt=12) | Q(pk__lt=8))
print(ret) #
# 3.非/not关系, 取反
ret = models.Book.objects.filter(~Q(pk__gt=7))
print(ret) #
# 4.通过组合,Q方法实现更加复杂的筛选条件
# 取pk不大于8或者大于12,并且pub_id=10的图书
ret = models.Book.objects.filter(Q(~Q(pk__gte=8) | Q(pk__gt=12)) & Q(pub_id=10))
print(ret) #
Q语句的另一种用法
# Q方法的另一种写法
ret = models.Book.objects.filter(Q(('pk__gte',12)))
print(ret) #
# 第一种方法写的是关键字,比较固定;第二种方法写的是字符串,方便通过变量替换,比较灵活
import os
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()
import datetime
from app01 import models
try:
from django.db import transaction # 导入事务
with transaction.atomic():
# 一些列操作
new_publisher = models.Publisher.objects.create(name="火星出版社")
models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10) # 指定一个不存在的出版社id
except Exception as e:
print(str(e))
在Django项目的setting.py文件末尾粘贴如下内容,即可在执行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',
},
}
}
如下:
ret = models.Book.objects.filter(sale__gt=F('stock'))
print(ret) #
'''输出结果
(0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.000) SELECT VERSION(); args=None
(0.001) SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`stock`, `app01_book`.`sale`, `app01_book`.`pub_id` FROM `app01_book` WHERE `app01_book`.`sale` > (`app01_book`.`stock`) LIMIT 21; args=()
'''
import os
# 调用Django环境并启动Django
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()
# 上面三步是关键
from app01 import models # 导入对应app的models
books = models.Book.objects.all() # 进行ORM操作
print(books)