Models字段详解
AutoField(Field)
int类型自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
bigint类型自增列,必须填入参数 primary_key=True
注意:
在models中如果没有自定义自增列,则会自动创建一个列名为id的自增列
from django.db import models class User(models.Model): # 由于没有自定义自增列,所以会自动创建一个名为id的自增列 name = models.CharField(max_length=32)
如果自己定义了自增列,就不会创建了
from django.db import models class User(models.Model): nid = modelsAutoField(primary_key=True) name = models.CharField(max_length=32)
SmallIntegerField(IntegerField)
小整型 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
正小正数 0 ~ 32767
IntegerField(Field)
整数类型(有符号) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
正整数 0 ~ 2147483647
BigIntegerField(IntegerField)
长整型(有符号) -9223372036854775808 ~ 9223372036854775807
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,用于指定IPV6或IPV4, '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)
十进制小数
参数:
- max_digits:数据总长度
- decimal_places:小数位长度
BinaryField(Field)
二进制类型
各个字段对应在数据库中的类型
'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)',
字段参数介绍
这里介绍的字段参数是指所有字段均适用的参数,每个字段独有的参数已在上面单独介绍
null
指定数据库字段是否可以为空
db_column
指定在数据库表中这一列的列名,如果没有指定,就会默认使用字段名
default
字段的默认值,如果在插入一条记录时没有为该列赋值,那么就会使用该默认值
primary_key
指定该字段是否为主键
db_index
是否为该字段建立索引,这种索引只是单纯的加快查找速度,并无其他作用
unique
是否为该字段建立唯一索引,这种索引除了可以加快查询速度以外,还能限制该字段插入的值唯一
unique_for_date
是否为字段的日期部分建立唯一索引,有的时候我们查找只关注日期,对字段的其他部分并不感兴趣,那么就可以为日期单独建立索引,以此来加快查找速度
unique_for_month
是否为字段的月部分建立唯一索引,如果有的时候只关注月份,同样也可以只对字段月的部分建立索引
unique_for_year
是否为字段的年部分建立唯一索引,如果有时候只关注字段的年份,同样可以只对年的部分建立唯一索引
verbose_name
该参数是为字段在Admin中显示而设立的,相当于为该字段取了一个在admin中显示的名字
blank
该参数是为Admin设立的,是否允许在Admin中输入为空
editable
该参数是为Admin设立的,是否允许Admin中编辑该字段
help_text
该参数是为Admin设立的,在Admin中显示该字段的提示信息
choices
该参数是为Admin设立的,在Admin中下拉选框的内容
如:
1
|
gf = models.IntegerField(choices = [( 0 , '小姨妈' ), ( 1 , '大表姐' ), ], default = 1 ) |
error_messages
自定义错误信息(字典类型),从而定制想要显示的错误信息,字典的键是参数名,如:unique,invalid,invalid_choice等,字典的值是如果违反了相对应的规则而提示的错误信息,如:
1
2
3
4
|
error_messages = { 'unique' : '输入内容已存在' , 'invalid' : '输入格式错误' , } |
validators
自定义验证规则(列表类型),如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
from django.core.validators import RegexValidator from django.core.validators import EmailValidator,URLValidator,DecimalValidator, MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator name = models.CharField( max_length = 32 , validators = [ RegexValidator(r 'root_d+' , message = '错了' , code = "c1" ), RegexValidator(r 'root_11111d+' , message = "又错了" , code = 'c2' ), EmailValidator(message = '还是错了' , code = 'c3' ), ], error_messages = { 'c1' : '错误信息1' , 'c2' : '错误信息2' , 'c3' : '错误信息3' , } ) |
元信息
在models中创建的类除了可以写各种各样的字段以外,还可以在里面写一个类来对整个表做操作,这个类的名字是Meta,在里面同样是通过字段来操作
例:
1
2
3
4
5
|
class UserInfo(models.Model): nid = models.AutoField(primary_key = True ) username = models.CharField(max_length = 32 ) class Meta: db_table = "User" |
下面对该类中的字段做介绍
db_table
在数据库中生成的表的名字,默认是:app名称_类名
index_together
为两个或多个字段共同建立索引
1
2
3
|
index_together = [ ( "pub_date" , "deadline" ), ] |
unique_together
联合唯一索引,为两个或多个字段建立共同索引,并且限制两列联合唯一
1
|
unique_together = (( "driver" , "restaurant" ), ) |
verbose_name
在admin中显示的表名,在admin中会在设置的表名后面加上s
verbose_name_plural
如果将上面的verbose_name跟verbose_name_plural设置成同样的值,就不会再admin中显示s
多表关系以及参数的介绍
ForeignKey(ForeignObject)
ForeignKey是进行一对多表操作时的字段,设置在多的一方,下面对该字段的参数做详细介绍
to
要进行关联的表名,即想与哪张表进行关联
class Husband(models.Model): nid = models.AutOField(primary_key=True) name = CharField(max_length=32) class Wives(models.Model): name = CharField(max_length=32) husband = models.ForeignKey(to='Husband')
to_field
指定要与关联的表中的哪一个字段进行关联,默认是和主键关联
class Husband(models.Model): nid = models.AutOField(primary_key=True) name = CharField(max_length=32) class Wives(models.Model): name = CharField(max_length=32) husband = models.ForeignKey( to='Husband', to_field='nid', )
on_delete
当要删除表中的某条记录时,怎样处理关联表中与该条记录关联的记录
- models.CASCADE:将与之关联的数据一起删除
- models.DO_NOTHING:引发IntegrityErroe异常,不允许删除,想要删除先解除与该记录关联的记录
- models.PROTECT:引发ProtectedError异常,不允许删除,想要删除先解除与该记录关联的记录
- models.SET_NULL:将与之关联的记录的外键设置成空,前提是外键允许为空
- models.SET_DEFAULT:将与之关联的记录的外键设置成默认值,前提是外键设置了default参数
- models.SET:有两种情况
- 第一种,将与之关联的记录的外键设置成指定值,models.SET(‘指定值’)
- 第二种,将与之关联的记录的外键设置成指定可执行对象的返回值,即如下所示:
def func(): return 10 class MyModels(models.Model) fk = models.ForeignKey( to='User', to_field='id', on_delete=models.SET(func(), ) )
related_name
反向操作时,使用的字段名,没有设置时默认为【表名_set】
例如:
obj.book_set.all()
如果设置了related_name为"book",那么反向查询变成了:
obj.book.all()
related_query_name
反向操作时使用的名称,不过这个只是替换了表名,并没有替换掉set
例如:
obj.book_set.all()
如果设置了related_query_name为"bk",那么反向查询变成了:
obj.bk_set.all()
limit_choices_to
在Admin或者ModelForm中显示数据时,提供的条件
例如:
limit_choices_to={'id_gt': 5} 就只会显示id大于5的信息
limit_choices_to=Q(id__gt=8) | Q(id__lt=20) 就只会显示id大于8小于20的信息
db_constraint
是否在数据库中创建外键约束,本来当ForeignKey关联了某张表后,那么外键就只能是哪张表的id中出现的值了,但是如果设置了db_constraint为True后就可以将外键设置成关联表id以外的值了,关联表将对该表没有任何约束
parent_link
在Admin中是否显示关联数据
OneToOneField(ForeignKey)
两张表的一对一关系实际上是通过 一对多+unique 来实现的,在一对多的基础上让外键只能是唯一的,那样不就做到一对一了吗,下面对一对一的参数进行介绍
to
要进行关联的表名
to_field
选择与关联表的哪一列进行关联
on_delete
选择对关联数据的处理方式,与一对多一致
ManyToManyField(RelatedField)
两张表进行多对多关联时会使用该字段,至于该字段出现在哪张表无所谓,下面对该字段的参数做介绍
to
要进行关联的表名
related_name
反向操作时,使用的字段名,代替【表名_set】,具体用法与一对多中的一致
related_query_name
反向操作时用于代替【表名】,具体用法与一对多中一致
limit_choices_to
在Admin或者ModelForm中显示数据时,提供的条件,具体用法与一对多中一致
symmetrical
仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
如下,在做自关联时,symmetrical设置成True跟False会有不同的字段可选
做 models.BB.objects.filter(...) 操作
设置成True可选字段为code,id,m1
1
2
3
4
|
class BB(models.Model): code = models.CharField(max_length = 12 ) m1 = models.ManyToManyField( 'self' ,symmetrical = True ) |
设置成True可选字段为code,id,m1,bb
1
2
3
4
|
class BB(models.Model): code = models.CharField(max_length = 12 ) m1 = models.ManyToManyField( 'self' ,symmetrical = False ) |
db_constraint
是否在数据库中创建外键约束,与一对多的db_constraint一致
db_table
在创建第三张表时,为第三张表取的名字,如果没有设置则默认使用下划线将多对多的两张表的表名连起来作为第三张表的表名
through
自定义第三张表时,第三张表的表名
through_fields
自定义第三张表时,第三张表中可能有很多字段与多对多的两张表进行外键关联,使用through_fields可以指定那两个字段为联系两张表的外键
如: through_fields=('group', 'person')
多对多创建第三张表
在多对多关系中,除了进行多对多关联的两张表以外,还会有第三张表,那么第三张表怎样创建呢?下面介绍三种方式
自动创建
第一种方式可以使用ManyToManyField字段自动创建第三张表
手动创建
使用第一种方式自动创建确实很方便,但是如果想在第三张表中添加更多的字段应该怎么办呢?显然自动创建是不行的了,自动创建只能创建三行数据,一个是自己的id,其余两个是与两张表关联的外键,如果这时需要添加更多的字段就需要自己定义第三张表了,在第三张表中首先需要创建的是两个外键分别关联多对多的两张表,这样就将两张表联系起来了,但是这样还不够,因为如果只是这样,那么第三张表中的两个外键可以在添加了1跟1后,按理是不能再添加1跟1了的,但是如果我们只是创建了两个外键没有做其他的操作,那么在添加1跟1后确实还能再添加1跟1,那这样显然不行,因此还应该给这两个外键加上联合唯一才行,因此最终第三张表就是这样的:
1
2
3
4
5
6
|
class UserToTag(models.Model): u = models.ForeignKey(to = "User" ) t = models.ForeignKey(to = "Tag" ) class Meta: unique_together(( 'u' , 't' ), ) |
手动+自动
除了使用ManyToManyField创建以及自己手动创建以外还能通过ManyToManyField字段+手动来一起创建第三张表,这时便要使用到 through 跟 through_fields 来在ManyToManyField中去指定第三张表了,不过这时add,remove等方法都会失效,因此不建议使用第三种方法创建第三张表
QuerySet参数详解
方法 | 说明 | 演示 | 演示说明 |
all | 查询所有的记录 | Book.objects.all() | 可以查出书籍表中的每一列数据 |
first | 拿到第一条记录 | Book.objects.first() | 拿到书籍表中的第一条记录 |
last | 拿到最后一条记录 | Book.objects.last() | 拿到书籍表中的最后一条记录 |
get | 拿到符合条件的数据,只有一条数据,而且只有结果是一条数据时才没问题,
如果查询结果是多条数据或者是0的话就会报错 |
Book.objects.get(**kwargs) | 这里拿到的是一个book对象,查询的内容也可以是某个单一的字段,
例如:Book.objects.get(id=3) |
values | 获取查询对象中的某个或某几个字段(以字典的形式展示) | Book.objects.filter(name="python基础").values("price", "pub_date") | 拿到的是所有名字为python基础的书籍的价格和出版日期 |
values_list | 获取查询对象中的某个或某几个字段(以列表的形式展示) | Book.objects.filter(name="python基础").values_list("price", "pub_date") | 与上面相同,只是将数据都存到了列表中,每一列的数据单独以一个元组储存 |
distinct | 去掉某字段重复的项 | Book.objects.all().value("name").distinct() | 只有在对某字段去重时才有意义,因为对于整条记录而言,由于有主键id的存在,所以不可能有重复的项,所以对整条记录进行去重是没有意义的 |
count | 计数 | Book.objects.all().count() | 计算查到的QuerySet集合的长度 |
filter | 按照条件进行查找 | Book.objects.filter(name="python基础") | 与get()方法不同,这个可以查询多条记录 |
exclude | 查询出条件以外的数据 | Book.objects.exclude(name="python基础") | 查询出名字不为 python基础的书籍 |
order_by | 将查询的结果按照某列进行升序或降序排列 |
models.Tb1.objects.filter(name='seven').order_by('id') models.Tb1.objects.filter(name='seven').order_by('-id') |
使用id为升序,使用-id为降序 |
reverse | 倒序,对order_by()后的数据有效 |
models.UserInfo.objects.all().order_by('-nid').reverse() |
如果存在order_by(),reverse是倒序,如果是多个排序则一一倒序 |
defer | 不取某几列数据 |
models.UserInfo.objects.filter(...).defer('username','id') |
不取username跟id列 |
only | 只取某几列数据 |
models.UserInfo.objects.only('username','id') |
只取username跟id列,相当于SELECT username, id FROM UserInfo |
using | 指定要使用的数据库,参数是别名(在settings.py的DATABASES字典中配置的名字) | ![]() |
|
dates | 根据时间进行某一部分的去重查找并截取指定内容 |
models.DatePlus.objects.dates('ctime','day','DESC') |
参数说明:
field_name:要筛选的字段名 kind:只能是"year" "month" "day" order只能是"ASC" "DESC" 并获取转换后的时间 year:年-01-01 month:年-月-01 day:年-月-日 |
datetimes | 根据时间进行某一部分的去重查找并截取指定内容,将时间转换成指定时区的时间 |
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’) |
参数说明:
field_name:要筛选的字段名 kind:只能是"year" "month" "day" "hour" "minute" "second" order只能是"ASC" "DESC" tzinfo:时区对象 |
none | 空QuerySet对象 | ||
bulk_create | 批量插入 |
objs = [ models.DDD(name='r11'), models.DDD(name='r22') ] models.DDD.objects.bulk_create(objs, 10) |
后面的10表时每插入10条数据向数据库提交一次 |
get_or_create | 如果存在则获取,否则创建 |
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2}) |
如果存在username等于root1则取出,否则创建root1,并且其他字段是defaults里面的内容 |
update_or_create | 如果存在就更新,否则创建 |
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1}) |
如果存在username等于root1则将defaults里面的内容更新进去,否则创建root1,并且其他字段是defaults里面的内容 |
in_bulk | 根据主键ID进行查找 |
id_list = [11,21,31] models.DDD.objects.in_bulk(id_list) |
|
delete | 删除 | ||
update | 更新 | ||
exists | 是否有结果 | ||
create | 插入一条记录 |
自己写原生SQL
在Django中,已经给定的操作已经能够很6的操作数据库了,但是如果仍旧觉得不够,那么也可以自己写SQL语句去执行,自己写SQL去执行的方式有三种,下面一一介绍
使用connection
第一种方式可以用connection来做也可以用connections做,在用connections做的时候,可以指定去哪个数据库操作,而名字是在settings.py中已经配置好了的数据库,如下:
下面利用connection或connections来做
1
2
3
4
5
|
from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() # 获取游标 cursor.execute( """SELECT * FROM auth_user WHERE id = %s""" , [ 1 ]) # 传sql语句 row = cursor.fetchone() # 也可以使用fetchmany() |
使用raw()方法
第二种是使用QuerySet中的raw()方法来做
1
|
models.UserInfo.objects.raw( 'SELECT * FROM userinfo WHERE id > %s' , params = [ 1 , ]) |
使用extra()方法来做
第三种同样是使用QuerySet的extra()方法来做
1
|
models.Entry.objects.extra(select = { 'new_id' : "SELECT col FROM somtable WHERE othercol > %s" }, select_params = ( 1 ,)) |
上面的new_id是col的新名称,相当于 SELECT col as new_id FROM somtable WHERE othercol >%s
优化查询
有时候进行查询,在查询某张表的时候需要获取与之关联的另外一张表的信息,假设此时检索出了5条信息,但还要获取与这5条信息关联的信息,这时候就又会从数据库中查询,这样就增加了查询的次数,如下:
1
2
3
4
|
user = models.UserInfo.objects. filter (id__lt = 5 ) for u n user: print (u.name) print (u.girlfriend. all ()) # grilfriend是UserInfo与Grilfriend关联的外键 |
像上面这种情况,通过user去查找grilfriend,每次循环都会查找一次,会增加查询次数,也就是说上面一共查找了5次
那么有没有什么方法可以减少查询次数呢?在QuerySet中还真有这么两个方法,可以优化查询,分别是 select_related 跟 prefetch_related
select_related(一次连表查询获取所有的数据)
这个方法相当于在查询A表的时候也把B表相关的数据一起查了,做了一次连表操作,相当于 SELECT * FROM A LEFT JOIN B ON A.id = B.nid ,这样就只需要进行一次查询就可以获取所有数据
例:
1
|
models.UserInfo.objects.select_related( 'girlfriend' ) # girlfriend是跨表字段 |
这种方式虽然使用连表操作简化了查询次数,但是做连表操作的性能低,这又是一个问题,那么怎样解决呢?这涉及到了QuerySet的另外一个方法prefetch_related
prefetch_related(多次查询提高效率)
这个方法也是在查询A表的时候把B表相关的数据一起查了,但是与上面不同的是这个方法分了多次去查询,不再使用连表操作
例:
1
|
models.UserInfo.objects.prefetch_related( 'girlfriend' ) |
这种方法先是计算查询到的数据关联的另一张表的记录的ID,然后再去另外一张表查询出属于这些id的字段
即:
select * from UserInfo
计算获取到所有用户的用户类型ID [1, 2, 33],再去另外一张表查询出这些id对应的字段
select * from girlfriend where id in [1, 2, 33]
最新评论