ORM操作之进阶

ORM

1. 必知必会13条

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

按照返回的结果对方法进行分类:

  • 返回QuerySet
    • all
    • fi lter
    • exclude
    • Order_by
    • reverse
    • values :返回一个可迭代的字典序列
    • Values_list :返回一个可迭代的元组序列
    • distinct
  • 返回对象
    • get
    • first
    • last
  • 返回数字
    • count
  • 返回布尔值
    • exists

2. 单表查询的双下划线方法

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里各个类的属性名,而不是数据表中的字段名(当然,两者名称也存在相同的情况)

1)多表关系之OneToOne

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)

3. 外键操作(ForeignKey)

3.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中是否显示关联数据

3.2 外键的定义方法

如下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> 

3.3 数据的查询

通过外键,可以实现基于对象或者字段的查询,同时也可以实现正向和反向两个方向的查询

1)基于对象的查询

正向查询:从多的一方“一”的一方去查询数据(“多”—>“一”);对象.关联字段.字段

反向查询:从”一“的一方多的一方去查询数据(“多”—>“一”);对象.表名_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())   # 
2)基于字段的查询
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)  # 

3.4 项目示例

于上述定义的表中插入数据

ORM操作之进阶_第1张图片

ORM操作之进阶_第2张图片

视图函数

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对表数据进行编辑、修改操作

方法一

通过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方法筛选出来的对象结果可能不止一个,比如多个点情况,就可以实现批量修改(即同时对多个满足筛选条件的对象进行字段更新);具体使用根据实际情况来用即可

4. 多对多(ManyToManyField)

4.1 多对多参数介绍

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,              # 默认创建第三张表时,数据库中表的名称

4.2 多对多的定义方法

​ 如下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> 

4.3 关系管理器

"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。

它存在于下面两种情况:

  1. 外键关系的反向查询
  2. 多对多关联关系

简单来说就是当 后面的对象 可能存在多个 的时候就可以使用以下的方法

也就是说,点后面的对象存在多个时,返回的便是一个关系管理对象,以下便是关系管理对象的方法

4.4 方法

1)正向操作
语法:对象.关联字段.方法
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()方法。

2)反向操作
语法:对象.关联表_set.方法
# 反向查   对象.关联表_set.方法
book_obj = models.Book.objects.get(title='python3从入门到放弃')

# 1. 1.all() 获取所有关联的对象
all_authors = book_obj.author_set.all()
print(all_authors)  # 

# 其他方法同正向操作的方法相同

说明

​ 正向、反向操作的方法都相同,只是语法有所不同

4.5 多对多示例

​ 当在数据库中出现两张表中的数据存在多对多的关系时,常常是要引入第三张表来做两者关系的对应

​ 比如,作者和作品的对应关系,一个作者可能写多本书,而一本书也可能又多个作者联合协作,这就是典型的多对多的关系

1) 创建多对多关系表
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)
2)获取关系管理对象

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

.ManyRelatedManager'>

3)通过.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中取对应的数据提交给前端即可

4.6 项目实战

路由文件配置

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 %}

展示结果

ORM操作之进阶_第3张图片

说明

如果删除一个作者“老王”,不仅作者表中的“老王”被删掉,而且 关系对应表 中,和老王有关的映射记录也会被删掉

通过关系管理对象新增数据

# 若获取的是一个列表,则需要用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})

5. 聚合查询和分组查询

5.1 数据准备

建表语句

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> 

5.2 聚合查询

关键方法

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')}

5.3 分组查询

关键方法

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)
# 

6. F查询和Q查询

6.1 F查询

数据准备

建表语句

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)
'''

6.2 Q查询

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)  # 

# 第一种方法写的是关键字,比较固定;第二种方法写的是字符串,方便通过变量替换,比较灵活

7. 事务

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))

8. Django终端打印SQL语句

在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=()

'''

9. 在python脚本中调用Django环境

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)

你可能感兴趣的:(Django,python)