django ORM
约 1089 个字 153 行代码 预计阅读时间 6 分钟
定义
./应用/models.py from django.db import models
class User ( models . Model ):
id = models . AutoField ( primary_key = True )
create_time = models . DateTimeField ( auto_now_add = True )
update_time = models . DateTimeField ( auto_now = True )
create_user = models . CharField ( max_length = 20 )
update_user = models . CharField ( max_length = 20 )
is_deleted = models . BooleanField ( default = False )
username = models . CharField ( max_length = 20 )
password = models . CharField ( max_length = 64 )
email = models . EmailField ()
phone_area_code = models . CharField ( max_length = 10 )
phone = models . CharField ( max_length = 20 )
is_validated = models . BooleanField ( default = False )
常用字段类型
数字
文本
CharField:对应 varchar。可设置 max_length 参数
TextField:大量文本。对应 longtext
EmailField:类似 CharField,最大长度 254。会验证格式
FileField:存储文件路径
ImageField:存储图片路径
UUIDField:存储 UUID 格式的字符串(32 字)。会验证格式
URLField:类似 CharField,存储 URL 格式的字符串(默认 max_length 为 200)。会验证格式
日期时间
DateTimeField:存储日期时间
DateField:存储日期
TimeField:存储时间
这些类可以设置以下属性:
auto_now:设为 True 时,新增、修改时设置为当前时间
auto_now_add:设为 True 时,新增时设置为当前时间
常用字段参数
null:数据库层面上是否可空,默认为 False。官方不建议在文本类型中设置为 True(防止同时有 NULL 和空字符串的情况)
blank:表单验证层面上是否可空,默认为 False
db_column:数据库里面的列名(如未定义则用参数名)
default:默认值。可用函数,但不能用 lambda 表达式;不能用列表、字典、集合等可变数据类型
primary_key:是否主键
unique:是否唯一
class User ( models . Model ):
id = models . AutoField ( primary_key = True )
create_time = models . DateTimeField ( auto_now_add = True )
...
class Meta :
db_table = 'test'
常用参数:
db_table: 自定义表名
ordering:自定义查询时的默认排序方式,如 ['username', '-create_time']
外键
一般外键(一对多关系)
class User ( models . Model ):
username = models . CharField ( max_length = 20 )
email = models . EmailField ()
class Article ( models . Model ):
title = models . CharField ( max_length = 20 )
author = models . ForeignKey ( "User" , on_delete = models . CASCADE )
on_delete 参数定义了:当外键指向的记录删除时,本记录如何处理(应用层面上的,不是数据库层面上的)。常用值:
models.CASCADE:关联的本条记录也一并删除
models.SET_NULL:本记录的对应列设为 NULL(需要设置该列的 null 和 blank 为 True)
models.PROTECT:只要该记录存在,就不能删除外键指向的记录
models.SET_DEFAULT:本记录的对应列设为默认值(需设置默认值)
models.SET(函数或方法对象):本记录的对应列设为函数 / 方法的返回值
models.DO_NOTHING:只看数据库层面约束
上例在数据库中,会添加 xxx_article.author_id 列,对应 xxx_user.id。
被指向的实体可以通过 xxxx_set (如上例的 User.author_set)查询其下的项目:
# 假如指定了 User 实例 user
articles = user . article_set . all ()
这个名字可以在定义外键时通过 related_name 参数自定义:
author = models . ForeignKey ( "User" , on_delete = models . CASCADE , related_name = "posted_articles" )
如果被指向的实体类在其他 app 中:
# 如 app 名称为 user 中有 User 实体类
author = models . ForeignKey ( "user.User" , on_delete = models . CASCADE )
如果要指向自己:
author = models . ForeignKey ( "self" , on_delete = models . CASCADE )
# 也可写该实体类的名字
一对一关系
class User ( models . Model ):
username = models . CharField ( max_length = 20 )
email = models . EmailField ()
class UserExtension ( models . Model ):
birthday = models . DateTimeField ()
user = models . OneToOneField ( "User" , on_delete = models . CASCADE )
多对对关系
class User ( models . Model ):
username = models . CharField ( max_length = 20 )
email = models . EmailField ()
tags = models . ManyToManyField ( "Tag" , related_name = "users" )
class Tag ( models . Model )
name = models . CharField ( max_length = 20 )
实质是建了一个中间表(上例为 xxx_user_tags,其中有 id、article_id、tag_id)。
应用到数据库
# 1. 生成迁移文件,但不会应用到数据库(但是会试图连接数据库,如有问题仍然会生成)
python manage.py makemigrations
# 2. 应用到数据库
python manage.py migrate
新增
user = User ( create_time =... , ... )
user . save ()
查询
查询所有
users = User . objects . all ()
for user in users :
print ( user . id , user . username , ... )
这里的 users 是 QuerySet 实例,可通过 .query 属性查看具体执行的 SQL 语句。
带条件查询
# 返回多个实例
users = User . objects . filter ( username = "ABC" )
# 返回单个实例,找不到则抛出 类.DoesNotExist 错误
user = User . objects . get ( username = "ABC" )
# 返回多个实例,但排除掉符合条件的数据
users = User . objects . exclude ( username = "ABC" )
可以通过 列名__操作符 的参数指定条件:
exact:等于
article = Article . objects . get ( id__exact = 14 ) # id = 14
article = Article . objects . get ( id__exact = None ) # id is null
iexact:类似于(LIKE)
articles = Article . objects . filter ( title__iexact = 'hello' ) # title like 'hello'
articles = Article . objects . filter ( title__iexact = 'hello%' ) # title like 'hello%'
包含
contains:大小写敏感
icontains:大小写不敏感
articles = Article . objects . filter ( title__contains = 'hello' ) # title like binary '%hello%'
articles = Article . objects . filter ( title__icontains = 'hello' ) # title like '%hello%'
in:值是否在给定容器
可以传列表、元组等可迭代对象,包括 QuerySet:
articles = Article . objects . filter ( id__in = [ 1 , 2 , 3 ]) # id in (1, 2, 3)
inner_qs = Article . objects . filter ( title__contains = 'hello' )
categories = Category . objects . filter ( article__in = inner_qs )
# select ... from category
# where article_id in (
# select id from article
# where title like binary '%hello%'
# )
比较
articles = Article . objects . filter ( id__gt = 3 ) # id > 3
以给定值开始
startswith:大小写敏感
istartswith:大小写不敏感
articles = Article . objects . filter ( title__startswith = 'hello' ) # id like binary 'hello%'
以给定值结束
endswith:大小写敏感
iendswith:大小写不敏感
articles = Article . objects . filter ( title__endswith = 'hello' ) # id like binary '%hello'
range:值是否在给定区间
传入元组:
from django.utils.timezone import make_aware
from datetime import datetime
start_date = make_aware ( datetime ( year = 2024 , month = 1 , day = 1 ))
end_date = make_aware ( datetime ( year = 2025 , month = 1 , day = 1 ))
articles = Article . objects . filter ( pub_date__range = ( start_date , end_date ))
# pub_date between '2024-01-01' and '2025-01-01'
日期时间
可用 date、year、month、day、week_day(周日为 1,周一 - 周六为 2-7)、time,并支持链式调用。
articles = Article . objects . filter ( pub_date__year = 2020 )
articles = Article . objects . filter ( pub_date__year__gt = 2020 )
articles = Article . objects . filter ( pub_date__date = date ( 2020 , 1 , 1 ))
articles = Article . objects . filter ( pub_date__time = datetime . time ( 22 , 1 , 1 ))
isnull:是否为空
articles = Article . objects . filter ( pub_date__isnull = False ) # pub_date is not null
正则表达式
regex:大小写敏感
iregex:大小写不敏感
articles = Article . objects . filter ( title__regex = r '^hello' ) # title regexp binary '^hello'
关联表查询
class Category ( models . Model ):
name = models . CharField ( ... )
class Article ( models . Model ):
title = models . CharField ( ... )
category = models . ForeignKey ( 'Category' , on_delete = models . CASCADE )
# 获取文章标题含 hello 的所有分类
categories = Category . object . filter ( article__title__contains ( 'hello' ))
排序
# 按照 name 升序
users = User . objects . order_by ( 'name' )
# 按照 name 降序
users = User . objects . order_by ( '-name' )
聚合函数
平均
from django.db.models import Avg
result = Book . objects . aggregate ( Avg ( 'price' ))
# {'price__avg': 23.0}
可修改默认名称:
result = Book . objects . aggregate ( my_avg = Avg ( 'price' ))
# {'my_avg': 23.0}
计数
from django.db.models import Count
result = Book . objects . aggregate ( book_num = Count ( 'price' ))
# {'book_num': 12}
有 distinct 参数,设为 True 则去重:
result = Book . objects . aggregate ( book_num = Count ( 'price' , distinct = True ))
最值
from django.db.models import Max , Min
result = Book . objects . aggregate ( max_price = Max ( 'price' ), min_price = Min ( 'price' ))
# {'max_price': 100.2, 'min_price', 35.4}
求和
from django.db.models import Sum
result = Book . objects . annotate ( total = Sum ( "bookorder__price" )) . values ( "name" , "total" )
其中,annotate() 方法表示在查询结果中附加对应的字段。values() 方法表示只提取给定的字段。返回 QuerySet 实例。
annotate() 方法类似于 group by。
Q 语句:或关系
from django.db.models import Q
books = Book . objects . filter ( Q ( price__lte = 10 ) | Q ( rating__lte = 9 )) # price <= 10 or rating <= 9
修改
# user 是查询得到的一个实例
user . username = '222'
user . save ()
删除
# user 是查询得到的一个实例
user . delete ()
user . save ()
F 表达式
F 表达式在生成 SQL 语句时,动态获取传给其的值,而无需事先查询数据。用于批量操作:
例 1:批量更改数据
原来的写法 employees = Employee . object . all ()
for employee in employees :
employee . salary += 1000
employee . save ()
使用 F 表达式 from django.db.models import F
Employee . object . update ( salary = F ( "salary" ) + 1000 )
例 2:筛选出两字段值相等的数据
原来的写法 out = []
employees = Employee . object . all ()
for employee in employees :
if employee . name == employee . email :
out . append ( employee )
使用 F 表达式 from django.db.models import F
out = Employee . object . filter ( name = F ( "email" ))