django ORM

这里先记录下常用的操作,后续整理。

数据库更新

django 有一个 django_migrations 用于记录每一次更新。结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> desc django_migrations;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| app | varchar(255) | NO | | NULL | |
| name | varchar(255) | NO | | NULL | |
| applied | datetime(6) | NO | | NULL | |
+---------+--------------+------+-----+---------+----------------+

> select * from django_migrations;
+----+------+--------------+----------------------------+
| id | app | name | applied |
+----+------+--------------+----------------------------+
| 1 | site | 0001_initial | 2019-05-16 10:56:28.313479 |
+----+------+--------------+----------------------------+

app 是表的名字, name 前面的序号是变更次数;

Migrations 步骤如下:

  1. 修改数据库模型;
  2. pyhton manage.py makemigrations my_model, 如果没有改动,使用 —empty 参数;
  3. python manage.py migrate.

需要注意的是migrate并不是原子操作,也不会加锁,例如在k8s多个pod中执行,django_migrations表中可能会出现多条记录。

我们也可以在migartions 中执行脚本,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from django.db import migrations

def update_site(apps, schema_editor):
Site = apps.get_model('site', 'Site')

do something...

class Migration(migrations.Migration):

dependencies = [
('site', '0001_auto_20190705_1027'),
]

operations = [
migrations.RunPython(update_site)
]

有时候自动 makemigrations 的脚本并不能一次解决我们的问题,例如增加一列 uuid,如果直接插入,所有 uuid都是一样的,可以先设置为空,再把空的填充,然后设置字段不可为空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from django.db import migrations, models
import uuid


def create_uuid(apps, schema_editor):
Device = apps.get_model('device_app', 'Device')
for device in Device.objects.all():
device.uuid = uuid.uuid4()
device.save()


class Migration(migrations.Migration):

dependencies = [
('device_app', 'XXXX'),
]

operations = [
migrations.AddField(
model_name='device',
name='uuid',
field=models.UUIDField(blank=True, null=True),
),
migrations.RunPython(create_uuid),
migrations.AlterField(
model_name='device',
name='uuid',
field=models.UUIDField(unique=True)
)
]

数据获取

1
2
3
4
5
# 获取所有对象
Model.objects.all()

# 根据条件过滤
Model.objects.filter(name="lina")

特殊操作

  • 大于: __gt
  • 大于等于: __gte

  • 小于: __lt

  • 小于等于: __lte

  • 包含: __contains

  • 开头是: __startswith

  • 结尾是: __endswith

  • 其中之一: __in

  • 范围: __range

1
2
# in
Model.object.filter(name__in=["line", "Alice"])

如果需要 <>, 则使用 Q

1
2
3
from django.db.models import Q

Entry.objects.filter(~Q(date=2020))

创建

1
2
3
4
5
6
7
# 更新或者创建
# 前面的是 filter, defaults 是更新
Industry.objects.update_or_create(
industry_id=industry_id,
name=industry_name[INDUSTRY_NAME_CHN],
defaults={'name_en': industry_name[INDUSTRY_NAME_EN]},
)

raw

Django 可以直接执行 sql 脚本,需要注意的是 django 的 raw 查询必须要有primary key字段,因为返回的是Model 的 QuerySet列表。新增的属性不要也是放到 Model的属性中的,所以名字不要包含特殊字符,这点确实有点蛋疼。

1
Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')

常用

Group by 使用

1
2
3
4
5
6
7
8
9
10
11
from django.db.models import Count

MyModel.objects.filter(row_id__in=[15,17])\
.distinct()\
.values('user_id')\
.annotate(user_count=Count('user_id'))\
.filter(user_count__gt=1)\
.order_by('user_id')

# 结果如下
# <QuerySet [{'user_count': 2, 'user_id': 1L}, {'user_count': 2, 'user_id': 5L}]>

统计数量

如果只是为了获取数量,使用 count 就行,不要使用 len(QuerySet), 这会获取所有的数据,消耗大量内存。

1
2
3
QuerySet.count()

# 等价于 SELECT COUNT(*) some_table

逻辑操作

1
2
3
from django.db.models import Q

MyModel.objects.filter(Q(username__contains='lina') | Q(age=12))

Model 参数说明

  • null: 默认为False,对应的字段如果没有设置则为空字符串(””), 如果设置了,对应 None;

自动插入/更新时间

1
2
3
4
5
# auto_now 创建、更新的时候自动获取当前时间
# 调用 update() 方法时,不生效,需要手动指定
# 调用 save() 方法时生效

# auto_now_add 创建的时候自动获取当前时间