Django框架Model

摘要

Django框架中Model知识点

  • db first
    • 自己再数据库先创建表结构,然后再连接数据库,在本地生成类
  • code first
    • 先写数据量对应类,然后执行命令通过类生成数据库
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
31
32
33
34
m = models.TB
m.objects.create()
m.objects.create(**{})
obj = models.TB()
obj.save()

m.objects.all()[3:10]
m.objects.all()
m.objects.update()
m.objects.delete()
m.objects.values()
m.objects.values_list()
m.objects.get()
m.objects.filter()
m.objects.filter(**{})
m.objects.filter(单下划线id)
m.objects.filter(双下划线跨表)
m.objects.filter().update()
m.objects.filter().first()
m.objects.filter().count()
m.objects.filter(id__gt=1)
m.objects.filter(id__lt=1)
m.objects.filter(id__gte=1)
m.objects.filter(id__lte=1)
m.objects.filter(id__range=[1,2])
m.objects.filter(id__in=[1,2])
m.objects.all().order_by()
m.objects.distinct()
# 多对多
obj.set()
obj.add()
obj.remove()
obj.all()
obj.fk.name

字段

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
AutoField(Field)
- int自增列,必须填入参数 primary_key=True

BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True

注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models

class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)

class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)

SmallIntegerField(IntegerField):
- 小整数 -3276832767

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 032767
IntegerField(Field)
- 整数列(有符号的) -21474836482147483647

PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 02147483647

BigIntegerField(IntegerField):
- 长整型(有符号的) -92233720368547758089223372036854775807

BooleanField(Field)
- 布尔值类型

NullBooleanField(Field):
- 可以为空的布尔值

CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度

TextField(Field)
- 文本类型

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 上传图片的宽度保存的数据库字段名(字符串)

DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD

TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]

DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

FloatField(Field)
- 浮点型

DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度

BinaryField(Field)
- 二进制类型

自定义无符号整数字段

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
31
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'

PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',

触发Model中的验证和错误提示有两种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,
如果都成功,才来检查Model的字段并显示指定错误信息
b. 使用ModelForm
c. 调用Model对象的clean_fields方法(钩子),如:
# models.py
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)

email = models.EmailField(error_messages={'invalid': '格式错了.'})

# views.py
def index(request):
obj = models.UserInfo(username='11234', email='uu')
try:
print(obj.clean_fields())
except Exception as e:
print(e)
return HttpResponse('ok')

# Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。

admin中修改错误提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# admin.py
from django.contrib import admin
from model_club import models
from django import forms


class UserInfoForm(forms.ModelForm):
age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'})

class Meta:
model = models.UserInfo
# fields = ('username',)
fields = "__all__"
exclude = ['title']
labels = { 'name':'Writer', }
help_texts = {'name':'some useful help text.',}
error_messages={ 'name':{'max_length':"this writer name is too long"} }
widgets={'name':Textarea(attrs={'cols':80,'rows':20})}

class UserInfoAdmin(admin.ModelAdmin):
form = UserInfoForm

admin.site.register(models.UserInfo, UserInfoAdmin)

字段参数

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
31
32
33
34
35
36
37
38
null                数据库中字段是否可以为空
db_column 数据库中字段的列名
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引

verbose_name Admin中显示的字段名称
blank Admin中是否允许用户输入为空
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)

error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'}

validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
)

元信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"

# 联合索引
index_together = [
("pub_date", "deadline"),
]

# 联合唯一索引
unique_together = (("driver", "restaurant"),)

# admin中显示的表名称
verbose_name

# verbose_name加s
verbose_name_plural

更多:https://docs.djangoproject.com/en/1.10/ref/models/options/

多表关系

  • 一对多:models.ForeignKey(其他表)
  • 多对多:models.ManyToManyField(其他表)
  • 一对一:models.OneToOneField(其他表)

应用场景:

  • 一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
    • 例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。
  • 多对多:在某表中创建一行数据是,有一个可以多选的下拉框
    • 例如:创建用户信息,需要为用户指定多个爱好
  • 一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了
    • 例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据

多表关系字段

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,删除关联数据,
      • 与之关联的值设置为指定值,设置:models.SET(值)
      • 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
1
2
3
4
5
6
7
8
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中显示关联数据时,提供的条件
1
2
3
4
5
6
7
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中是否显示关联数据

OneToOneField(ForeignKey)

  • to, # 要进行关联的表名
  • to_field=None, # 要关联的表中的字段名称
  • on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为
1
2
3
4
5
6
7
8
9
10
11
###### 对于一对一 ######
# 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)

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中显示关联数据时,提供的条件:
1
2
3
4
5
6
7
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用于指定内部是否创建反向操作的字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 做如下操作时,不同的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, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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, # 默认创建第三张表时,数据库中表的名称
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 一对多查询操作
class Group(models.Model):
name = models.CharField(max_length=24)

class Host(models.Model):
host = models.CharField(max_length=32, db_index=True)
g = models.ForeignKey(Group, to_field='id')
## Host数据的QuerySet对象包含一个g对象
## g对象就是相关联的Group的QuerySet对象
v1 = Host.objects.filter(id__gt=1)

## 使用values时可以用__双下划线获取对应关联对象的数据
## 获取数据为字典
v2 = Host.objecys.filter(id__gt=1).values('id','host','g__name')
## 获取数据为元组
v3 = Host.objecys.filter(id__gt=1).values_list('id','host','g__name')

# 自定义多表关系,多对多
# 可以自定义第三张表
class Host(models.Model):
host = models.CharField(max_length=32, db_index=True)

class App(models.Model):
name = models.CharField(max_length=24)

class AppToHost(models.Model):
host = models.ForeignKey(Host, to_field='id')
app = models.ForeignKey(App, to_field='id')
status = models.IntegerField(default=1)

# 自动创建多表关系
# 无法直接对第三章表操作
class Host(models.Model):
host = models.CharField(max_length=32, db_index=True)

class App(models.Model):
name = models.CharField(max_length=24)
host = models.ManyToManyField(Host)

# 多表操作
a = App.objects.filter(id=1)
a.name

## 增加
a.host.add(1)
a.host.add(2,3,4)
a.host.add(*[5,6,7,8])

## 删除
a.host.remove(1)
a.host.remove(2,3,4)
a.host.remove(*[5,6,7,8])
a.host.clear()

## 删除数据库中相关的数据(id=1)然后重新添加新的数据,更新数据的时候使用
a.host.set([3,5,7])

## 查,所有相关的host的query_set对象
a.host.all()

ORM操作

基本操作

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
31
32
# 增
## 增加一条数据,可以接受字典类型数据 **kwargs
models.Tb1.objects.create(c1='xx', c2='oo')

## 添加数据,需要save
obj = models.Tb1(c1='xx', c2='oo')
obj.save()

# 查
## 获取单条数据,不存在就报错(不推荐)
models.User.objects.get(id=1)
## 返回对象的query_set
models.User.objects.all()
## 返回字典的query_set
models.User.objects.all().values('username','passwd')
## 返回元组的query_set
models.User.objects.all().values_list('username','passwd')
## 获取指定条件的数据
models.User.objects.filter(name='seven')
models.User.objects.exclude(name='seven')

# 删
## 删除指定条件的数据
models.Tb1.objects.filter(name='seven').delete()

# 改
## 将指定条件的数据更新,均支持 **kwargs
models.Tb1.objects.filter(name='seven').update(gender='0')
## 修改单条数据,需要save提交
obj = models.Tb1.objects.get(id=1)
obj.c1 = '111'
obj.save()

进阶操作

利用双下划线将字段和对应的操作连接起来

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 获取个数
models.Tb1.objects.filter(name='seven').count()

# 大于,小于
models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值
models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值
models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值

# in
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in

# isnull
Entry.objects.filter(pub_date__isnull=True)

# contains
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.exclude(name__icontains="ven")

# range
models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and

# 其他类似
# startswith,istartswith, endswith, iendswith,

# order by
models.Tb1.objects.filter(name='seven').order_by('id') # asc
models.Tb1.objects.filter(name='seven').order_by('-id') # desc

# group by
# from django.db.models import Count, Min, Max, Sum
models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

# limit 、offset
models.Tb1.objects.all()[10:20]

# regex正则匹配,iregex 不区分大小写
Entry.objects.get(title__regex=r'^(An?|The) +')
Entry.objects.get(title__iregex=r'^(an?|the) +')

# date
# Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
# Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

# year
#
# Entry.objects.filter(pub_date__year=2005)
# Entry.objects.filter(pub_date__year__gte=2005)

# month
#
# Entry.objects.filter(pub_date__month=12)
# Entry.objects.filter(pub_date__month__gte=6)

# day
#
# Entry.objects.filter(pub_date__day=3)
# Entry.objects.filter(pub_date__day__gte=3)

# week_day
#
# Entry.objects.filter(pub_date__week_day=2)
# Entry.objects.filter(pub_date__week_day__gte=2)

# hour
#
# Event.objects.filter(timestamp__hour=23)
# Event.objects.filter(time__hour=5)
# Event.objects.filter(timestamp__hour__gte=12)

# minute
#
# Event.objects.filter(timestamp__minute=29)
# Event.objects.filter(time__minute=46)
# Event.objects.filter(timestamp__minute__gte=29)

# second
#
# Event.objects.filter(timestamp__second=31)
# Event.objects.filter(time__second=2)
# Event.objects.filter(timestamp__second__gte=31)

高级操作

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# extra
#
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
# Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
# Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

# F
#
# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1)


# Q
#
# 方式一:
# Q(nid__gt=10)
# Q(nid=8) | Q(nid__gt=10)
# Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')

# 方式二:
# con = Q()
# q1 = Q()
# q1.connector = 'OR'
# q1.children.append(('id', 1))
# q1.children.append(('id', 10))
# q1.children.append(('id', 9))
# q2 = Q()
# q2.connector = 'OR'
# q2.children.append(('c1', 1))
# q2.children.append(('c1', 10))
# q2.children.append(('c1', 9))
# con.add(q1, 'AND')
# con.add(q2, 'AND')
#
# models.Tb1.objects.filter(con)


# 执行原生SQL
#
# from django.db import connection, connections
# cursor = connection.cursor() # cursor = connections['default'].cursor()
# cursor.execute("""SELECT * from auth_user where id = %s""", [1])
# row = cursor.fetchone()

连表操作

利用双下划线和_set将表之间的操作连接起来

表结构实例

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
31
32
33
34
35
36
37
38
39
40
class UserProfile(models.Model):
user_info = models.OneToOneField('UserInfo')
username = models.CharField(max_length=64)
password = models.CharField(max_length=64)

def __unicode__(self):
return self.username


class UserInfo(models.Model):
user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),
)
user_type = models.IntegerField(choices=user_type_choice)
name = models.CharField(max_length=32)
email = models.CharField(max_length=32)
address = models.CharField(max_length=128)

def __unicode__(self):
return self.name


class UserGroup(models.Model):

caption = models.CharField(max_length=64)

user_info = models.ManyToManyField('UserInfo')

def __unicode__(self):
return self.caption


class Host(models.Model):
hostname = models.CharField(max_length=64)
ip = models.GenericIPAddressField()
user_group = models.ForeignKey('UserGroup')

def __unicode__(self):
return self.hostname
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 一对一操作

user_info_obj = models.UserInfo.objects.filter(id=1).first()
print user_info_obj.user_type
print user_info_obj.get_user_type_display()
print user_info_obj.userprofile.password

## 使用双下划线获取关联表数据
user_info_obj = models.UserInfo.objects.filter(id=1).values('email', 'userprofile__username').first()
print user_info_obj.keys()
print user_info_obj.values()

#####
# 一对多 类似一对一
# 搜索条件使用 __ 连接
# 获取值时使用 . 连接
user_info_obj = models.UserInfo.objects.get(name=u'武沛齐')
user_info_objs = models.UserInfo.objects.all()

group_obj = models.UserGroup.objects.get(caption='CEO')
group_objs = models.UserGroup.objects.all()

# 添加数据
#group_obj.user_info.add(user_info_obj)
#group_obj.user_info.add(*user_info_objs)

# 删除数据
#group_obj.user_info.remove(user_info_obj)
#group_obj.user_info.remove(*user_info_objs)

# 添加数据
#user_info_obj.usergroup_set.add(group_obj)
#user_info_obj.usergroup_set.add(*group_objs)

# 删除数据
#user_info_obj.usergroup_set.remove(group_obj)
#user_info_obj.usergroup_set.remove(*group_objs)

# 获取数据
#print group_obj.user_info.all()
#print group_obj.user_info.all().filter(id=1)

# 获取数据
#print user_info_obj.usergroup_set.all()
#print user_info_obj.usergroup_set.all().filter(caption='CEO')
#print user_info_obj.usergroup_set.all().filter(caption='DBA')

其他操作

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################

def all(self)
# 获取所有的数据对象

def filter(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q

def select_related(self, *fields)
# 性能相关:表之间进行join连表操作,一次性获取关联的数据。
# 一般的跨表操作会执行多次数据库操作,而如果想一次性获取跨表操作,就要用这个方法
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_related('外键字段__外键字段')

def prefetch_related(self, *lookups)
# 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
# 获取所有用户表
# 获取用户类型表where id in (用户表中的查到的所有用户ID)
# 使用的时候分多次数据库操作
models.UserInfo.objects.prefetch_related('外键字段')



from django.db.models import Count, Case, When, IntegerField
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=1),
output_field=CharField(),
))
)

students = Student.objects.all().annotate(num_excused_absences=models.Sum(
models.Case(
models.When(absence__type='Excused', then=1),
default=0,
output_field=models.IntegerField()
)))

def annotate(self, *args, **kwargs)
# 用于实现聚合group by查询

from django.db.models import Count, Avg, Max, Min, Sum

v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
# SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
# SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
# SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
# 用于distinct去重
models.UserInfo.objects.values('nid').distinct()
# select distinct nid from userinfo

注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
# 用于排序
models.UserInfo.objects.all().order_by('-id','age')

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# 构造额外的查询条件或者映射,如:子查询

Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

def reverse(self):
# 倒序
models.UserInfo.objects.all().order_by('-nid').reverse()
# 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


def defer(self, *fields):
models.UserInfo.objects.defer('username','id')

models.UserInfo.objects.filter(...).defer('username','id')
#映射中排除某列数据

def only(self, *fields):
#仅取某个表中的数据
models.UserInfo.objects.only('username','id')

models.UserInfo.objects.filter(...).only('username','id')

def using(self, alias):
指定使用的数据库,参数为别名(setting中的设置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

def raw(self, raw_query, params=None, translations=None, using=None):
# 执行原生SQL
models.UserInfo.objects.raw('select * from userinfo')

# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表')

# 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

# 将获取的到列名转换为指定列名
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

# 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default")

################### 原生SQL ###################
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone() # fetchall()/fetchmany(..)


def values(self, *fields):
# 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
# 获取每行数据为元祖

def dates(self, field_name, kind, order='ASC'):
# 根据时间进行某一部分进行去重查找并截取指定内容
# kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
# order只能是:"ASC" "DESC"
# 并获取转换后的时间
- year : 年-01-01
- month: 年-月-01
- day : 年-月-日

models.DatePlus.objects.dates('ctime','day','DESC')

def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
# 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
# kind只能是 "year", "month", "day", "hour", "minute", "second"
# order只能是:"ASC" "DESC"
# tzinfo时区对象
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

"""
pip3 install pytz
import pytz
pytz.all_timezones
pytz.timezone(‘Asia/Shanghai’)
"""

def none(self):
# 空QuerySet对象

####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
# 聚合函数,获取字典类型聚合结果
from django.db.models import Count, Avg, Max, Min, Sum
result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
===> {'k': 3, 'n': 4}

def count(self):
# 获取个数

def get(self, *args, **kwargs):
# 获取单个对象

def create(self, **kwargs):
# 创建对象

def bulk_create(self, objs, batch_size=None):
# 批量插入
# batch_size表示一次插入的个数
objs = [
models.DDD(name='r11'),
models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
# 如果存在,则获取,否则,创建
# defaults 指定创建时,其他字段的值
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

def update_or_create(self, defaults=None, **kwargs):
# 如果存在,则更新,否则,创建
# defaults 指定创建时或更新时的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

def first(self):
# 获取第一个

def last(self):
# 获取最后一个

def in_bulk(self, id_list=None):
# 根据主键ID进行查找
id_list = [11,21,31]
models.DDD.objects.in_bulk(id_list)

def delete(self):
# 删除

def update(self, **kwargs):
# 更新

def exists(self):
# 是否有结果

其他

Django原生SQL获取cursor字典

1
2
3
4
5
6
7
8
9
import pymysql
from django.db import connection, connections

connection.connect()
conn = connection.connection
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("""SELECT * from app01_userinfo""")
row = cursor.fetchone()
connection.close()

数字自增、字符串更新

1
2
3
4
5
6
7
8
9
10
# 数字自增
from django.db.models import F
models.UserInfo.objects.update(num=F('num') + 1)

# 字符串更新
from django.db.models.functions import Concat
from django.db.models import Value

models.UserInfo.objects.update(name=Concat('name', 'pwd'))
models.UserInfo.objects.update(name=Concat('name', Value('666')))

ORM函数相关

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# ########### 基础函数 ###########

# 1. Concat,用于做类型转换
# v = models.UserInfo.objects.annotate(c=Cast('pwd', FloatField()))

# 2. Coalesce,从前向后,查询第一个不为空的值
# v = models.UserInfo.objects.annotate(c=Coalesce('name', 'pwd'))
# v = models.UserInfo.objects.annotate(c=Coalesce(Value('666'),'name', 'pwd'))

# 3. Concat,拼接
# models.UserInfo.objects.update(name=Concat('name', 'pwd'))
# models.UserInfo.objects.update(name=Concat('name', Value('666')))
# models.UserInfo.objects.update(name=Concat('name', Value('666'),Value('999')))

# 4.ConcatPair,拼接(仅两个参数)
# v = models.UserInfo.objects.annotate(c=ConcatPair('name', 'pwd'))
# v = models.UserInfo.objects.annotate(c=ConcatPair('name', Value('666')))

# 5.Greatest,获取比较大的值;least 获取比较小的值;
# v = models.UserInfo.objects.annotate(c=Greatest('id', 'pwd',output_field=FloatField()))

# 6.Length,获取长度
# v = models.UserInfo.objects.annotate(c=Length('name'))

# 7. Lower,Upper,变大小写
# v = models.UserInfo.objects.annotate(c=Lower('name'))
# v = models.UserInfo.objects.annotate(c=Upper('name'))

# 8. Now,获取当前时间
# v = models.UserInfo.objects.annotate(c=Now())

# 9. substr,子序列
# v = models.UserInfo.objects.annotate(c=Substr('name',1,2))

# ########### 时间类函数 ###########
# 1. 时间截取,不保留其他:Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,ExtractSecond, ExtractWeekDay, ExtractYear,
# v = models.UserInfo.objects.annotate(c=functions.ExtractYear('ctime'))
# v = models.UserInfo.objects.annotate(c=functions.ExtractMonth('ctime'))
# v = models.UserInfo.objects.annotate(c=functions.ExtractDay('ctime'))
#
# v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year'))
# v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'month'))
# v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year_month'))
"""
MICROSECOND
SECOND
MINUTE
HOUR
DAY
WEEK
MONTH
QUARTER
YEAR
SECOND_MICROSECOND
MINUTE_MICROSECOND
MINUTE_SECOND
HOUR_MICROSECOND
HOUR_SECOND
HOUR_MINUTE
DAY_MICROSECOND
DAY_SECOND
DAY_MINUTE
DAY_HOUR
YEAR_MONTH
"""

# 2. 时间截图,保留其他:Trunc, TruncDate, TruncDay,TruncHour, TruncMinute, TruncMonth, TruncSecond, TruncYear
# v = models.UserInfo.objects.annotate(c=functions.TruncHour('ctime'))
# v = models.UserInfo.objects.annotate(c=functions.TruncDate('ctime'))
# v = models.UserInfo.objects.annotate(c=functions.Trunc('ctime','year'))

ORM自定义函数

1
2
3
4
5
6
7
8
9
10
from django.db.models.functions.base import Func
class CustomeFunc(Func):
function = 'DATE_FORMAT'
template = '%(function)s(%(expressions)s,%(format)s)'

def __init__(self, expression, **extra):
expressions = [expression]
super(CustomeFunc, self).__init__(*expressions, **extra)

v = models.UserInfo.objects.annotate(c=CustomeFunc('ctime',format="'%%Y-%%m'"))

配置

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class Env(models.Model):
id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4)
name = models.CharField(max_length=20)
keys = models.CharField(max_length=20, unique=True)
context = models.CharField(max_length=100)
status = models.BooleanField(default=True)
create_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.name

class Meta:
ordering = ['create_at', ]

@classmethod
def currentID(cls, env):
p = cls.objects.filter(keys=env)
return p[0].id if p else ''


@classmethod
def getEnv(cls):
p = cls.objects.values()
return list(p)

###############

from django.db import models
from django.urls import reverse
import uuid
from base.models import Env
from django.utils.translation import ugettext_lazy as _


class App(models.Model):
id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4)
name = models.CharField(max_length=30, verbose_name=_("Name"))
# blank=True 在admin后台编辑的时候可以为空
description = models.CharField(max_length=100, blank=True, verbose_name=_("Description"))
config_path = models.CharField(max_length=50, verbose_name=_("Config_path"))
md5 = models.CharField(max_length=50, blank=True)
status = models.BooleanField(default=True, verbose_name=_("Status"))
create_at = models.DateTimeField(auto_now=True, verbose_name=_("Create_at"))
env_id = models.CharField(max_length=36, default='')

def __str__(self):
# print显示的信息
return self.name

class Meta:
# 排序字段
ordering = ['create_at', ]
# 设置表的名称
verbose_name = '用户类型'
verbose_name_plural = '用户类型'

def get_absolute_url(self):
return reverse('configs:create', kwargs={'id': self.pk})

@classmethod
def search_value(cls, q, env):
env_id = Env.currentID(env)
limit = q.get('limit', 0)
offset = q.get('offset', 0)
order = q.get('order', '')
search = q.get('search', '')
p = cls.objects.filter(env_id=env_id, name__startswith=search).values()
return p.count(), p[offset:offset+limit]

from django.contrib import admin
from configs import models

# admin后台数据管理界面定制
class AppAdmin(admin.ModelAdmin):
# 页面默认显示的字段
list_display = ('name', 'description', 'config_path', 'md5', 'status')
# 为数据表添加搜索功能
search_fields = ('name',)
# 添加快速过滤
list_filter = ('name', 'status')
# 排序字段
ordering = ('name',)
# 可编辑的字段名
fields = ('name', 'description', 'config_path', 'md5', 'status')
# many to many 字段多选
# filter_horizontal = ('url',)
date_hierarchy = 'create_at'

admin.site.register(models.App, AppAdmin)

事务操作

1
2
3
4
5
from django.db import transaction

with transaction.atomic():
obj = model.User.filter(id=1).update(name='xx')
obj = model.User.filter(id=1).update(name='xx')