django ORM
定义
| ./应用/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"))
|