本篇介绍Django的ORM。
通过这篇文章,你能了解到:
- ORM是什么以及它的优缺点
- ORM语句的使用
ORM
是什么
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
Django的orm操作本质上会根据对接的数据库引擎,翻译成对应的sql语句;所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite….,如果数据库迁移,只需要更换Django的数据库引擎即可。
优势
使用 ORM 最大的优点就是快速开发,让我们将更多的精力放在业务上而不是数据库上,下面是 ORM 的几个优点
- 隐藏了数据访问细节,使通用数据库交互变得简单易行。同时 ORM 避免了不规范、冗余、风格不统一的 SQL 语句,可以避免很多人为的 bug,方便编码风格的统一和后期维护。
- ORM提供了对数据库的映射,不用直接编写SQL代码,只需操作对象就能对数据库操作数据。
- 方便数据库的迁移。当需要迁移到新的数据库时,不需要修改对象模型,只需要修改数据库的配置。
劣势
ORM 的最令人诟病的地方就是性能问题,不过现在已经提高了很多,下面是 ORM 的几个缺点
- 性能问题
- 自动化进行数据库关系的映射需要消耗系统资源
- 程序员编码
- 在处理多表联查、where 条件复杂的查询时,ORM 可能会生成的效率低下的 SQL
- 通过 Lazy load 和 Cache 很大程度上改善了性能问题
- SQL 调优,SQL 语句是由 ORM 框架自动生成,虽然减少了 SQL 语句错误的发生,但是也给 SQL 调优带来了困难。
- 越是功能强大的 ORM 越消耗内存,因为一个 ORM Object 会带有很多成员变量和成员函数。
- 对象和关系之间并不是完美映射
一般来说 ORM 足以满足我们的需求,如果对性能要求特别高或者查询十分复杂,可以考虑使用原生 SQL 和 ORM 共用的方式
Django的ORM
Django ORM用到三个类:
Manager
、QuerySet
、Model
。Manager定义表级方法(表级方法就是影响一条或多条记录的方法),我们可以以
models.Manager
为父类,定义自己的manager,增加表级方法;QuerySet:Manager类的一些方法会返回QuerySet实例,QuerySet是一个可遍历结构,包含一个或多个元素,每个元素都是一个Model 实例,它里面的方法也是表级方法,前面说了,Django给我们提供了增加表级方法的途径,那就是自定义manager类,而不是自定义QuerySet类,一般的我们没有自定义QuerySet类的必要;
Manager类的绝大部分方法是基于Queryset的。一个QuerySet包含一个或多个model instance。QuerySet类似于Python中的list,list的一些方法QuerySet也有,比如切片,遍历。
django.db.models模块中的Model类,我们定义表的model时,就是继承它,它的功能很强大,通过自定义model的instance可以获取外键实体等,它的方法都是记录级方法(都是实例方法,貌似无类方法),不要在里面定义类方法,比如计算记录的总数,查看所有记录,这些应该放在自定义的manager类中。
测试方式
以前,我们不能直接运行一个py文件,因为会有环境约束,那我们还想在这个环境下,直接一段代码该怎么办?
console控制台
在django项目中,点击Python Console,就可以使用Django环境。
- 优点:无需额外创建py文件
- 缺点:结果无法保存
1 | Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32 |
自定义py文件
在django项目下自定义一个py文件,写入如下内容,就可以在django环境下使用。
1 | import os |
admin管理后台
可以使用的原因:
- url中有,而且在apps中也注册了
对表进行增删查改
创建超级用户
1
python manage.py createsuperuser
- 然后,输入用户名密码
注册
1
2
3
4
5
6# app下的admin.py
from django.contrib import admin
from app01 import models
admin.site.register(models.Person)
# 注册了多个model,当编辑这个model是需要其它model(外键,多对多),但其它model内容为空就可以从当前页面打开创建其它model。登录
- 127.0.0.1:8000/admin
对于ManyToManyField字段 ,想避过admin校验(必填),可以加
blank = True
定制额外的功能
1 | from django.contrib import admin |
打印sql语句
在settings.py中,添加如下配置,即可在使用orm查询数据库的时候,输出对应的sql语句。
1 | LOGGING = { |
字段
常用字段
数值
AutoField(Field)
int自增列,必须填入参数 primary_key=True
一个model不能有两个AutoField
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
note:当model中如果没有自增列,则自动会创建一个列名为id的列
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
- 不要用它来存手机号(char)
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
布尔值
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
字符
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
时间
DateTimeField(DateField)
- 日期+时间格式
YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
- auto_now_add=True 只保存新增的那个时间
- auto_now 保存新增和修改的时间
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD,相当于Python中的datetime.date的实例。
- auto_now:每次修改时修改为当前日期时间。
- auto_now_add:新创建对象时自动添加当前日期时间。
note:auto_now
、auto_now_add
、default
是互斥的
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
验证
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, ‘both’,”ipv4”,”ipv6”
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol=”both”
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = “” 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = “” 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
推荐阅读: field-types
自定义字段
例子:自定义char类型
1 | class MyCharField(models.Field): |
字段参数
常用参数
null
- 该字段可以为空
blank
- 设置字段后,使用admin管理的表中该字段可以不填
- 校验时可以为空(使用admin管理的表中可以不填)
default
- 数据库中字段的默认值
primary_key
- 数据库中字段是否为主键
db_index
- 数据库中字段是否可以建立索引
unique
- 数据库中字段是否可以建立唯一索引
unique_for_date
- 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month
- 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year
- 数据库中字段【年】部分是否可以建立唯一索引
model form 系列
verbose_name
- Admin中显示的字段名称
blank
- Admin中是否允许用户输入为空
editable
- Admin中是否可以编辑
help_text
- Admin中该字段的提示信息
choices
Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
例子:
1 | # 元组中,左边是真实存放的值,右边是显示的值 |
对于choices字段:
对象.get_字段名_display()
来显示对应的值,而不是数据库中的值
error_messages
- 自定义错误信息(字典类型),从而定制想要显示的错误信息;
- 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
- 如:{‘null’: “不能为空.”, ‘invalid’: ‘格式错误’}
validators
- 自定义错误验证(列表类型),从而定制想要的验证规则
1 | from django.core.validators import RegexValidator |
推荐阅读 field-options
Model Meta参数
推荐阅读 model meta options
改变Admin中显示的内容:
改表名,显示名称
联合索引
联合唯一索引
1 | class Person(models.Model): |
当我们在settings.py 设置
LANGUAGE_CODE= ‘zh-Hans’
Admin 内的内容就翻译成中文了
Manager
- 在创建完 Model 对象之后,Django 会自动为其关联一个 Manager 对象,该对象是 Model 进行数据库操作的接口。默认的 Manager 对象名称为 objects。
- 表级
- Manager 对象也可以自定义。
更改Manager对象的名称
1 | # models.py |
1 | # 自定义的py文件中 |
自定义Manager对象
1 | # models.py |
1 | # 自定义的py文件中 |
QuerySet
- 从数据库中查询出来的结果一般是一个集合,这个集合称为 QuerySet。
- QuerySet 有两种来源:通过 Manager 的方法获取、通过 QuerySet 自身的方法获得。
- 字段级
- Manager 的查询方法和 QuerySet 的方法大部分同名、同意(Manager的就是基于 QuerySet 的实现的,所有两者会有相同的部分),例如 相同的有:filter, exclude等,但两者也有不同的方法,例如 Manager 的 create、get_or_create,QuerySet 的 delete 等。
限制QuerySet
在MySQL查询时,我们往往可以限制显示的数量, LIMIT
,那再ORM中是如何表达呢? 切片!!!
对于QuerySet,我们可以时候python中的索引,切片,来限制输出,一个场景就是用于分页。
1 | # 显示前3个元素 |
- 看到这里可能会有一个疑惑,这有些low吧。查了所有,然后只显示前3个,其实并不是这样的,因为有Lazy load机制。
Lazy load
QuerySet 是惰性加载的,创建查询集不会访问数据库,只有查询集需要求值时,才会真正运行这个查询。
在下面的例子中只有执行 print ret
才会真正的去查询数据库。
1 | ret = models.Person.objects.all() |
关联对象也是惰性加载,只有用到了关联对象的值才会访问数据库
1 | ret = models.Person.objects.get(pk=1) |
真正访问数据库的几种情况:
- 迭代:在首次迭代查询集时会执行数据库查询
- 切片(限制查询集):对查询集执行切片操作时或指定 step 参数
- 序列化/缓存
- repr:对查询集调用 repr 函数
- len:对查询集调用 len 函数
- list: 对查询集调用 list() 方法强制求值
- bool:测试一个查询集的布尔值,例如使用bool(), or, and 或者 if 语句都将导致查询集的求值
缓存
每个 QuerySet 都包含一个缓存来最小化对数据库的访问
1 | # 访问两次数据库 |
在一个新的 QuerySet 中,缓存为空。当首次对 QuerySet 的所有实例进行求值时(代码中是print),会将查询结果保存到 QuerySet 的缓冲中。
当再访问该 QuerySet 时,会直接从缓冲中取数据。
必知必会13条(查询)
返回QuerySet
8 个
- all
- filter
- exclude
- values
- values_list
- order_by
- reverse
- distinct
1 | #1. all 获取所有数据,返回QuerySet(对象列表) |
返回对象
3个
- get
- first
- last
1 | # get 获取一个满足条件的数据,返回对象 要求:存在且唯一,其它情况报错 |
返回bool
- exists
1 | # exists 判断数据是否存在,存在这条记录,返回True,否则返回False |
返回数字
- count
1 | # count 计数,比len的效率高一些,因为len会真正的去数据库中进行查询 |
单表的双下划线
双下划线是python的一个特色,在ORM中通常用于显式分隔过滤关键字 (filter key name) 的各个部分。在底层,字符串用这些下划线分割开,然后这些标记分开处理。name__contains
被替换成 attribute: name, filter: contains
。
范围
大于,大于等于,小于,小于等于
__gt
__gte
__lt
__lte
1 | ret = models.Person.objects.filter(pk__gt=1) # greater than |
具体区间
(闭区间)
__range
1 | ret = models.Person.objects.filter(pk__range=[1, 3]) # 表示范围 |
成员判断
__in
1 | ret = models.Person.objects.filter(pk__in=[1, 3, 7, 9]) # 取出这几个 |
模糊查询
包含
__contains
__icontains
1 | ret = models.Person.objects.filter(name__contains='a') # like 包含/模糊查询 |
以什么开头
__startswith
__istartswith
1 | ret = models.Person.objects.filter(name__startswith='x') # 以什么开头 |
以什么结尾
__endswith
__iendswith
1 | ret = models.Person.objects.filter(name__endswith='x') # 以什么结尾 |
年份
__year
1 | ret = models.Person.objects.filter(birth__year=2019) # 查询年份是2019 |
__month
1 | ret = models.Person.objects.filter(birth__month=9) # 对于date类型是可以查到的 |
- 使用模糊查询来查月份(会有警告,不过还是可以查到)
1 | ret = models.Person.objects.filter(birth__contains=9) |
为空
__isnull
1 | ret = models.Person.objects.filter(age__isnull=True) |
外键的查询
模型定义
1 | class Publisher(models.Model): |
note:对于Book想要删除pub 只需要 Book.pub = None 就欧克了
自关联
- 表内自关联是指表内数据相关联的对象和表是相同字段,这样我们就直接用表内关联将外键关联设置成自身表的字段。同样表内关联也分一对多字段和多对多字段。
伪外键
db_constraint = False 数据库中不加这个外键约束,相当于伪约束。
基于对象的查询
基于对象的查询: 正向查询,反向查询
不指定related_name
- 正向查询: 通过Book对象来查Publisher
1 | book_obj = models.Book.objects.all().first() |
- 反向查询:通过Publisher对象来查询Book
- 关系管理对象名称:类名小写 加
_set
- 关系管理对象名称:类名小写 加
1 | pub_obj = models.Publisher.objects.get(pk=1) |
指定related_name
在外键字段中 添加 related_name='book'
- 正向查询:通过Book对象来查Publisher
- 同前面,略。
- 反向查询:通过Publisher对象来查询Book
- 不同之处在于,关系管理对象的名字
- 关系管理对象名称:
book
1 | ret = pub_obj.book.all() |
基于字段的查询
不指定related_name
给出版社的名字,查书籍(通过外键)
pub__name表示从 book表跨到publisher
__ 表示跨表,在sql语句中,就是要进行连表操作。
1 | book = models.Book.objects.filter(pub__name='变强出版社') |
给书名查出版社
Publisher对象 通过 book (类名小写) 来跨表
类名小写__字段
1 | ret = models.Publisher.objects.filter(book__title='Mysql删库到跑路') |
指定related_name
外键定义中添加 related_name=’books’
- 给出版社的名字,查书籍(通过外键)
- 同前面,不涉及related_name。
- 给书名查出版社
- 通过定义的 books 来进行跨表
- 关系管理对象名称:
books
1 | ret = models.Publisher.objects.filter(books__title='Mysql删库到跑路') |
指定related_query_name
外键定义中添加 related_query_name=’xxx’
- 给出版社的名字,查书籍(通过外键)
- 同前面,不涉及related_name。
- 给书名查出版社
- 通过定义的 xxx 来进行跨表
- 关系管理对象名称:xxx
- 且只能用在基于字段查询,不能用在基于对象查询
1 | ret = models.Publisher.objects.filter(xxx__title='Mysql删库到跑路') |
小结
设置了外键后,我们可以通过外键来正向查询所关联的对象,名称为外键名(pub =models.ForeignKey(to='Publisher')
),使用外键名与双下划线(pub__
)就可以进行跨表查询,通过book对象,来查询出版社的信息。
那么对于出版社来说,通过出版社对象查询书籍的过程被称为反向操作(没有设置外键/少的一方 —>设置外键/多的一方),具体如下:
- 在没有指定related_name的时候,我们通过设置外键的那个类的类名小写与双下划线结合(
book__
)来进行跨表查询; - 设置了 related_name 后,我们不再用类名小写,而是用 related_name 的值;
- 在基于字段查询的过程中,如果设置了related_query_name,则优先使用related_query_name的值。
- 所以 related_name 和 related_query_name 都是反向查询时,关系管理对象可用的名字
关系管理对象的方法
通过关系管理对象来进行增删查改
针对出版社与书籍这个一对多关系
all
- 获取所关联的所有对象
1 | # 正向 |
set
设置一对多关系
只能用对象或对象列表,不能用id
set(QuerySet)
当为外键设置 null=True 时,才可以减少(减少意味着,有些就为null了);没有设置null=True 则不会改变。
1 | # 正向 : 一本书对应一个出版社 |
add
添加一对多关系
add(对象, 对象)
add( *QuerySet) 列表打散
理解上也是:我的少的一方可以指定多的一方(一个出版社可以对应很多书)
1 | # 正向 |
remove/clear
remove(*QuerySet)
当你干掉这两个字段后,为null,如果字段不允许为空就会报错
外键字段设置可以为空 null=True 后,才能用remove/clear
1 | # 反向: |
create
- 新增一个所关联的对象,并且建立一对多的关系(少的一方创建多的一方)
- 小差别
1 | # 通过关系管理对象,创建一本书。 |
update
- 使用
update()
方法可以批量为QuerySet中所有的对象进行更新操作。 - 增加外键:
1 | # 反向 |
- 删除外键:
1 | # 反向 |
多对多关系
模型定义
继续在models.py 中定义新类 Author, Author 与 Book 是多对多关系。
1 | class Publisher(models.Model): |
基于对象的查询
不指定related_name
- 正向查询:
1 | author = models.Author.objects.all().first() |
- 反向查询:
- 关系管理对象名称:类名小写 加
_set
(author_set
)
- 关系管理对象名称:类名小写 加
1 | book_obj = models.Book.objects.all().first() |
指定related_name
- 正向:
- 相似,略。(不涉及related_name)
- 反向查询:
- 在ManyToManyField设置 related_name 为authors
- 关系管理对象名称: authors
1 | book_obj = models.Book.objects.all().first() |
基于字段的查询
不指定related_name
通过书籍去查作者
正向查询(设置ManyToManyField)
Author —> Book
1 | author = models.Author.objects.filter(books__title='Mysql删库到跑路') |
- 通过作者去查书籍
- 查询这个作者写的书(反向查询)
- Book —> Author
1 | books = models.Book.objects.filter(author__name='光头强') |
指定related_name
在 ManyToManyField 定义中,添加 related_name = 'authors'
通过书籍去查作者
- 同前面, related_name 不影响正向
通过作者去查书籍
- 关系管理对象名称: authors
1 | books = models.Book.objects.filter(authors__name='光头强') |
小结
基于对象的查询与基于字段的查询:
- 基于字段的查询,名称是写在 filter 内部,当关键字参数;而基于对象的查询,是通过
对象.关联对象小写__set
这个关系管理对象来进行操作。
关系管理对象的方法
通过关系管理对象来进行增删查改
针对书籍与作者这个多对多关系
也分为正反向,反向也可以使用 related_name , 明确用对的关系管理对象名称就行。
all
- 获取所关联的所有对象
set
设置多对多关系(先删除该对象原有的,在添加新的)
[id, id, id]
[对象, 对象, 对象] 或 QuerySet,最终还是转成id
为什么可以做到呢? 因为这个表里共三个字段,id不用管,book_id 是你当前对象携带的,所以只需要指定 author_id 就行。
1 | book_obj = models.Book.objects.all().first() |
add
添加多对多关系(不在用列表了)
add(id, id)
add(对象, 对象)
add( *QuerySet) 列表打散
1 | book_obj = models.Book.objects.all().first() |
remove
- 删除多对多关系 (有则删除,无则不变,不会报错)
- remove(id, id)
- remove(对象, 对象)
- remove( *QuerySet) 列表打散
1 | book_obj = models.Book.objects.all().first() |
clear
清空多对多关系
对象.关系管理对象.clear()
1 | book_obj = models.Book.objects.all().first() |
create
- 新增一个所关联的对象,并且建立多对多的关系(通过作者去造一本书,并自动添加关系)
1 | # 正向 通过作者对象,来创建一本书,并自动添加关系 |
小结
对于外键中关于关系管理对象的方法 和 多对多关系中关系管理对象的方法 它们的一个差别在于后者可以直接使用 id 来进行增删查改,而前者只能通过对象。
聚合查询和分组查询
聚合查询
从 django.db.models 导入聚合函数,这些聚合函数是类
aggregate 是终止子句,执行后得到的是一个字典
例子:
1 | from django.db.models import Max,Min,Count,Avg,Sum |
分组查询
- 分组查询需要和聚合一起使用,不然没多大意义
- annotate 是注释的意思(额外添加信息),将结果封装对象的属性,翻译成sql语句,里面是有group by的。
- annotate 默认以该对象的id,进行分组,如果想更改分组条件,可以先使用values进行筛选,然后使用annotate,添加注释。 见例中方法二。 当然使用values筛选建议筛选这个类(表)
例子:
1 | # 统计每一本书的作者个数 |
F查询与Q查询
F查询:字段之间的比较;动态获取字段的值。
Q查询是为了弥补 OR
模型定义
为了丰富查询,增加列
1 | class Book(models.Model): |
F查询
字段之间的比较
以前在filter内写多个条件,用逗号隔开,这是与的意思。
例如:
我们查询销量大于50且库存大于30的
1 | ret = models.Book.objects.filter(sale__gt=50, memo__gt=30) # sale >50 且 memo > 30 |
如果我们想查销量大于库存的行有哪些(同一行的不同字段间的比较),通过以前的方式是做不了的,所以引入F查询
1 | from django.db.models import F |
动态获取字段的值
假如我们想刷一下销量,按照以前的做法,可以这样做:
1 | ret = models.Book.objects.all() |
- logging打印的sql语句:
1 | (0.001) UPDATE `app01_book` SET `title` = '祥龙宝典', `price` = '5.00', `sale` = 400, `memo` = 50, `pub_id` = 1 WHERE `app01_book`.`id` = 7; args=('祥龙宝典', '5.00', 400, 50, 1, 7) |
使用F查询就很简单
1 | ret = models.Book.objects.update(sale=F('sale') * 2 + 10) |
- logging打印的sql语句
1 | (0.001) UPDATE `app01_book` SET `sale` = ((`app01_book`.`sale` * 2) + 10); args=(2, 10) |
update与save比较:
save会把所有内容取出重新赋值,而update只会把需要修改的内容进行修改,效率上,这两个差异很大。
Q查询
1 | # 取 2< pk < 5 |
引入Q查询,来解决 OR
1 | from django.db.models import Q |
几种清空:
Q(条件)
Q(条件)&Q(条件) 与
Q(条件)|Q(条件) 或
~Q(条件) 非
Q对象的新写法
当我们使用搜索功能时,希望对搜索条件有一个更好的封装,而不是直接写一堆Q()
- 小试牛刀
1 | q = Q() |
- 整理简化
定义:
1 | def local_search(self, field_list, search): |
使用:
1 | search = request.GET.get('local_search', '') |
bulk_create
一次性插入多条
1 | # 批量插入数据 |
事务
一系列操作,当作一个操作。要么都成功,要么都失败。
格式:
1 | with transation.atomic(): |
例1:
a同学给b同学转账100元
1 | rom django.db import transaction |
with transaction.atomic()
开启事务.select_for_update()
开启行级锁
执行原生SQL
例子:
1 | from django.db import connection, connections |
小结
写orm语句的时候,可以正向,可以反向,根据要求中出现的表名出发,两种写出来进行比较。
正向查询后继续反向回查会多连一次表。例子如下:
1 | # 查找书名是“小灰机”的书的出版社出版的其他书籍的名字和价格 |
先values在filter 与 先filter在values是不一样的
先filter在values,这样列少一些(少一些连表),信息会缺。
先filter在values,先增加列信息,然后再进行筛选,这也不会缺失信息。(这个作者它的所有书籍都在一行,而不是一本书是一条行)
1 | # 查找书名是“小灰机”的书的作者们的姓名以及出版的所有书籍名称和价钱 |
- null 和 空 的判断方式是不一样的,但对于查一个字段是否为空,需要将两者结合
1 | # 查找memo字段是空的书 |