Django框架之五-->模型
模型类的_meta属性
用类直接取属性和使用_meat.get_field()取得的对象是不一样的,就像modelform都是经过了封装
Book._meta.get_field("state") <django.db.models.fields.IntegerField: state> Book.state <django.db.models.query_utils.DeferredAttribute object at 0x0000020C7F2B1A20>
字段对象可以取得数据,而DeferredAttribute对象不可以
模型类对象也可以调用_meta
obj._meta.related_objects # 是所有关联这张表的字段对象集合 for i in obj._meta.related_objects : print(i) # 关联obj的字段对象(ManyToOneRel或ManyToManyRel,并不是ForeignKey或ManyToMany) # 虽然类型不同,但是有些属性是相同的 print(i.field_name) # 关联obj的字段对象的to__field的值 print(i.related_name) # 用于反向查询的名字 print(i.limit_choise_to) # 筛选条件 print(i.to) # 关联的表 print()
model._meta.get_field(字符串字段名) # 根据字符串获得模型类对象的字段对象 外键字段: 字段对象.rel.to.objects.all()就等于model.object.字段名.all()
model._meta.model_name # 模型类名 model._meta.app_label # app名
models.IntegerField()可以设置choices,数据库中存储的是键值对中的第0个元素,字段对象.choices就得到了设置的元组,模型类实例想要得到用于展示的内容要使用modelobj.get_字段名_display()
a = Book.objects.first() a <Book: python> a.state 1 a.get_state_display() '已出版'
Django通过关联字段查询执行的sql是子查询
models.Class.objects.get(cname='全栈5期').student_set.all()
通过双下方法进行跨表是join连表查询,应该注意效率问题
models.Student.objects.filter(cid__cname="全栈5期")
Object Relational Mapping(ORM)
ORM介绍
ORM概念
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。
ORM在业务逻辑层和数据库层之间充当了桥梁的作用。
ORM由来
让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。
几乎所有的软件开发过程中都会涉及到对象和关系数据库。在用户层面和业务逻辑层面,我们是面向对象的。当对象的信息发生变化的时候,我们就需要把对象的信息保存在关系数据库中。
按照之前的方式来进行开发就会出现程序员会在自己的业务逻辑代码中夹杂很多SQL语句用来增加、读取、修改、删除相关数据,而这些代码通常都是重复的。
ORM的优势
ORM解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。
ORM提供了对数据库的映射,不用直接编写SQL代码,只需像操作对象一样从数据库操作数据。
让软件开发人员专注于业务逻辑的处理,提高了开发效率。
ORM的劣势
ORM的缺点是会在一定程度上牺牲程序的执行效率。
ORM用多了SQL语句就不会写了,关系数据库相关技能退化...
ORM总结
ORM只是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。
但我们不能指望某个工具能一劳永逸地解决所有问题,一些特殊问题还是需要特殊处理的。
但是在整个软件开发过程中需要特殊处理的情况应该都是很少的,否则所谓的工具也就失去了它存在的意义。
Django中的ORM
建库
Django中并不存在创建数据库的功能,所以需要手动创建数据库
修改配置
首先要配置settings中的数据库配置
# 默认配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # 默认的设置非常简单,下面说明各个设置: # -- ENGINE 告诉 Django 使用哪个数据库引擎 # -- NAME 告诉 Django 数据库的名称 # -- sqlite3是一种文件数据库,所以name这里谢了一个路径 # 连接mysql数据库 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':"s8", # 库名 "USER":"root", # 用户名 "PASSWORD":"123456", # 密码 "HOST":"127.0.0.1", # ip "PORT":3306 # 端口 } }
如果使用 Django 的数据库层(模型),必须创建 Django 应用。模型必须保存在应用中。因此,编写模型之前,要新建一个应用。
创建APP后也要修改配置文件
INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', # 添加的配置 ]
模型就存放在APP中的models.py文件中,另外Django默认使用的模块是MySQLdb,python3中并不支持MySQLdb模块,所以需要在项目主文件中的__init__文件中添加下面两句
import pymysql pymysql.install_as_MySQLdb()
创建模型
在app/models.py里面写上一个类,必须继承models.Model这个类 (注意启动Django项目)
from django.db import models class Class(models.Model): id = models.AutoField(primary_key=True) cname = models.CharField(max_length=20)
迁移
在项目目录下执行(当模型类结构改变时都需要进行迁移)
python manage.py makemigrations # 相当于去你的models.py 里面看一下有没有改动 python manage.py migrate # 把改动翻译成SQL语句,然后去数据库执行
你就会发现你的数据库中多了一些表,其中就有一个(APP名_类名)(数据库是小写的类名)的,其中字段就是类的属性.
虚拟迁移
只会在django_migrations中生成迁移记录而不会去实际的修改数据库
python manage.py migrate --fake
解释
在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表,
基本情况:
- 每个模型都是一个Python类,它是django.db.models.Model的子类。
- 模型的每个属性都代表一个数据库字段。属性的值包含了数据的类型,以及约束条件
- 综上所述,Django为您提供了一个自动生成的数据库访问API,
简单来说
类 --> 表
类的普通属性 --> 字段
类的一对一外键或多对一外键 --> 关联
对象 --> 一行数据
QuerySet对象和模型类对象
- QuerySet对象是一个类似列表的集合
-
models.Book.objects.filter(title="新年快乐") <QuerySet [<Book: 新年快乐>]>
-
- 模型类对象对应数据库的一行数据
-
models.Book.objects.get(title="新年快乐") <Book: 新年快乐>
-
- 管理器对象
-
models.Book.objects <django.db.models.manager.Manager object at 0x0000020FAD57B358>
-
模型管理器及QuerySet对象方法
# 返回QuerySet对象的方法 # 返回全部对象 Class.objects.all() # 返回满足条件的对象 Class.objects.filter(条件) # 排除满足条件的对象 Class.objects.exclude(条件) # 根据字段查 Class.objects.values(字段名,字段名) # 返回一个包含字典的QuerySet对象 models.Classo.objects.values("cname") <QuerySet [{'cname': '全栈9期'}, {'cname': '全栈8期'}, {'cname': '全栈10期'}]> # 根据字段查2 QuerySet.values_list(字段名,字段名) # 返回一个包含元祖的QuerySet对象 models.Classo.objects.values_list("cname") <QuerySet [('全栈9期',), ('全栈8期',), ('全栈10期',)]> # 排序 Class.objects.order_by("字段名") # 此外,还可以反向排序。方法是在字段名称前面加上“-”(减号) #虽然 order_by() 有一定的灵活性,但是每次都调用它相当繁琐。多数时候,我们始终使用同一个字段排序。 #此时,可以在模型中指定默认排序: class Publisher(models.Model): name = models.CharField(max_length=30) class Meta: ordering = ['name'] # 任何模型都可以使用 Meta 类指定多个针对所在模型的选项。 # 反转 Class.objects.reverse() # 根据order_by查到后排序,直接在order_by中使用-号 # 去重 Class.objects.distinct() # 返回数据库中匹配查询(QuerySet)中的对象数量 Class.objects.count() # 判断QuerySet对象中有没有数据对象 Class.objects.exists() # 执行的sql语句是用limit取第一条,返回一个布尔值, # 返回数据对象的几种 Class.objects.first() # 返回第一条记录 Class.objects.last() # 返回最后一条记录 Class.objects.get(条件) # 返回一个满足条件的数据对象,有多条满足条件的报错
# 链式查找 可以对返回的QuerySet对象继续操作
Class.objects.filter(country="U.S.A.").order_by("-name")
get , filter , exclude 条件
属性名__lt=10 # 属性值小于10 属性名__gt=10 # 属性值大于10 属性名__in=[1,2,3]# 属性值存在列表中的 属性名__contains="xx" # 属性包含"xx"的,相当于limit "%xx%" 属性名__icontains="xx" # 不区分大小写的包含 属性名__isnull = bool # 是否有值,之是一个布尔值 属性名__range=[x,y] # 属性值范围大于等于x,小于等于y,相当于sql中的bettwen x and y 类似的还有startswith,istartswith, endswith, iendswith data字段还可以 models.Class.objects.filter(data字段名__year=2017) # 2017年的
首先要把模型类导入进来,如果你导入了一个类你就可以直接用类名,如果你导入的是文件,就要用.的方式
这里直接用Class代表用到的类
增
# 第一种方式 -- 生成数据并且提交到数据库 Class.objects.create(属性=值,属性=值) # 可以将一个字典打散传入,**解包 # 第二种方式 -- 需要提交 new_info = Class(属性=值) new_info.save() # 实例 from django.shortcuts import render,HttpResponse from . import models def abb(request): new_s = models.Student.objects.create( sname="狗哥", cid=1) print(new_s,new_s.id, new_s.sname) return HttpResponse("OK") # 打开URL后打印 Student object 4 狗哥 #Student object 是一个对象
批量添加
使用create()实际上是打开数据库,写入数据然后关闭数据库,写入多条数据是会频繁开关数据库,所以Django提供了批量操作的方法.相当于sql的一次添加多条数据
向模型类中添加多个模型类对象
model.objects.bulk_create([models_obj_list])
删
models.Student.objects.filter( sname="狗哥").delete() models.Student.objects.get( sname="狗娃").delete() # 可以删除一个对象,也可以删除一个列表中的全部对象,为了防止不小心把表中的数据都删除,想删除表中的一切数据时,Django 要求必须显式调用 all() 方法。 Class.objects.delete() 无效 Class.objects.all().delete() 生效删除部分数据,无需调用 all() 方法 如上面的例子
改
# 查到 QuerySet 对象 update(属性=新的值),可以批量操作,update() 方法有返回值,是一个整数,表示修改的记录数量。 # 或者查到对象,使用赋值的方法修改,然后提交.只能对一个对象操作,不能对QuerySet 对象操作 # 方案一实例 from django.shortcuts import render,HttpResponse from . import models def abb(request): models.Student.objects.filter(sname="大驴").update(sname="二驴") # print(new_s) return HttpResponse("OK") # 方案二实例 from django.shortcuts import render,HttpResponse from . import models def abb(request): new_s = models.Student.objects.filter(cid=28).first() # 可以修改 # print(new_s) new_s.sname="大驴" new_s.save() return HttpResponse("OK") # 得到类似列表的对象 from django.shortcuts import render,HttpResponse from . import models def abb(request): new_s = models.Student.objects.filter(cid=28) # 报错 # print(new_s) new_s.sname="大驴" new_s.save() return HttpResponse("OK")
模型属性值的意义
# 默认不可为空,如要设置为空要添加 null=True 参数 models.CharField(max_length=30) # 相当于varchar(30) ,必须有max_length参数(没有char类型) models.AutoField(primary_key=True) # AutoField()int自增,必须设置primary_key=True,主键 models.IntegerField() # 有符号整数
外键
# 一对一外键,相当于models.ForeignKey(to,to_field,unique=True),不可重复 models.OneToOneField(to,to_field) # to=模型类名,to_field=模型类属性名(默认为id主键) # 一对多 models.ForeignKey(to,to_field) # 设置外键的属性在数据库中的字段名为 属性名_外键属性名,同时模型类对象会生成一个同名属性 # 设置外键的属性值是一个类的对象 # 外键属性.对应模型属性 的值则为数据库中的数据 # 反向查询 -- 根据外键属性查到关联的值为正向查询
# 被关联模型类对象.小写模型类名_set.all() # 可以在外键属性中设置related_name的值,就可以使用该值代替-外键属性名_set
# 一对一的外键则不需要_sat,直接用小写的表名,正向反向查询都是得到模型类对象
# 一对多正向查询得到模型类对象,反向查询是Django的中间对象,all()得到QuerySet对象
# 多对多正反向查询得到的都是中间对象 多对多的三种方式 # 一 ,创建第三张表,第三张表中的两个字段关联两个有关系的表 # 二 ,使用models.ManyToManyField()作为字段的值,并且由Django创建第三张表(默认,表名为APP名_小写类名_属性名,表中字段名为小写类名_id) # 三 ,使用models.ManyToManyField()作为字段的值,自己创建第三张表(传入thrugh="表名"through_fields=("关联字段名", "关联字段名")) # 方式二的一些操作create() 创建一个新的对象,保存对象,并将它添加到关联对象集中,返回新创建的对象(可以进行链式操作)模型类对象.多对多属性.create(属性=值,属性=值) # 为关联的类添加一行数据 add() # 把指定的model对象添加到关联对象集中,需要将集合打散 模型类对象.多对多属性值.add(*关联模型对象的集合) # QuerySet对象就是模型对象的集合,在第三张表中添加对应记录 模型类对象.多对多属性值.add(*数据库值集合) # 通过Django自建第三张表还可以使用数据库值添加对应记录 set() # 更新模型类对象的关联对象 模型类对象.多对多属性值.set(关联模型对象的集合) 模型类对象.多对多属性值.set(数据库值集合) remove() # 从关联对象集中移除关联对象 模型类对象.多对多属性值.remove(关联模型对象) 模型类对象.多对多属性值.remove(数据库值) clear() # 从关联对象中移除一切对象 模型类对象.多对多属性值.clear()
注意:
- remove()或clear()方法操作后如果关联对象集为空的话,必须满足null=True。
- 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
多对多属性的值
模型类实例的多对多外键是一个Django创建的中间对象,使用all()可以取到对应类对象的QuerySet集合对象
>>>models.Book.objects.get(title="新年快乐").author <django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x0000020FAD5FE6A0> >>>models.Book.objects.get(title="新年快乐").author.all() <QuerySet [<Author: 好妹妹乐队>, <Author: 陈粒>]> # 反向 >>>models.Author.objects.get(name="陈粒").book_set.all() <QuerySet [<Book: 新年快乐>]>
普通属性可以直接=.多对多可以使用"__对应模型类的属性"作为条件
还可以最为values查询的值,并且反向查询不需要加_set
>>>models.Author.objects.filter(book__title="新年快乐") # 反向查 <QuerySet [<Author: 好妹妹乐队>, <Author: 陈粒>]> >>>models.Book.objects.filter(author__name="好妹妹乐队") # 正向查 <QuerySet [<Book: 新年快乐>]> >>>models.Author.objects.filter(name="陈粒").values("name","book__title") # QuerySet.values <QuerySet [{'name': '陈粒', 'book__title': '新年快乐'}]> >>>models.Book.objects.filter(title="新年快乐").values("title","author__name") <QuerySet [{'title': '新年快乐', 'author__name': '好妹妹乐队'}, {'title': '新年快乐', 'author__name': '陈粒'}]>
聚合查询和分组查询
聚合
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的
# 用于计算的函数 from django.db.models import Avg,Min,Sum,Max >>>models.Book.objects.filter(author__name="陈粒").aggregate(Sum("price")) {'price__sum': Decimal('14.00')} #如果你想要为聚合值指定一个名称,可以向聚合子句提供它。 >>> models.Book.objects.filter(author__name="陈粒").aggregate(price=Sum("price")) {'price': Decimal('14.00')} #如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。 >>> models.Book.objects.all().aggregate(Sum("price"),Max("price"),Avg("price")) {'price__sum': Decimal('45.66'), 'price__max': Decimal('10.00'), 'price__avg': 7.61}
分组
为调用的QuerySet中每一个对象都生成一个独立的统计值属性,
查询每个人歌曲的总价格
# 从人表查,以每个人为一组,为每个人的歌曲价格求和 a = models.Author.objects.annotate(arg=Sum("book__price")) for i in a: print(i,i.arg) 好妹妹乐队 5.00 陈粒 14.00 谢安琪 10.00 陈小熊 8.00 杨亚茹 6.66 谢天笑 7.00 成龙 None # 从歌曲表查,指定以人分组 b = models.Book.objects.values("author__name").annotate(arg = Sum("price")) for i in b: print(i["author__name"],i["arg"]) 好妹妹乐队 5.00 陈粒 14.00 谢安琪 10.00 陈小熊 8.00 杨亚茹 6.66 谢天笑 7.00
F查询和Q查询
F查询
使用查询条件的值,来计算或比较
from django.db.models import F,Q # 增加价格都加一块 >>>models.Book.objects.update(price=F("price")+1) # 查询歌曲价格小于作者年龄一半的 >>>models.Book.objects.filter(price__lt=F("author__age")/2) <QuerySet [<Book: 新年快乐>, <Book: 新年快乐>, <Book: 济南济南>, <Book: 绿茶>, <Book: 冷血动物>]>
Q查询
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。
# 查询作者是陈粒或陈小熊的 >>>models.Book.objects.filter(Q(author__name="陈粒")|Q(author__name="陈小熊")) <QuerySet [<Book: 新年快乐>, <Book: 济南济南>, <Book: 小半>]> #你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。 # 查询陈粒的不包含小半的作品 models.Book.objects.filter(Q(author__name="陈粒")&~Q(title="小半")) <QuerySet [<Book: 新年快乐>]>
使用字符串进行Q查询
from django.db.models import Q s = Q() # 实例Q对象 s.connector = "or" # 对象内部条件关系为or,默认为and s.children.append(("name", "wei")) s.children.append(("age", "18")) 相当于 model.objects.filter(Q(name="wei")|Q(age=18))
字段(模型类属性的类)
AutoField # int自增列,必须填入参数 primary_key=True BigAutoField # bigint自增列,必须填入参数 primary_key=True # 注:当model中如果没有自增列,则自动会创建一个列名为id的列 SmallIntegerField # 小整数 -32768 ~ 32767 PositiveSmallIntegerField # 正小整数 0 ~ 32767 IntegerField # 整数列(有符号的) -2147483648 ~ 2147483647 PositiveIntegerField # 正整数 0 ~ 2147483647 BigIntegerField # 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 FloatField # 浮点型 DecimalField # 10进制小数 - 参数: max_digits,小数总长度 decimal_places,小数位长度 BinaryField # 二进制类型 DurationField # 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 DateTimeField # 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] DateField # 日期格式 YYYY-MM-DD TimeField # HH:MM[:ss[.uuuuuu]] BooleanField # 布尔值类型 NullBooleanField # 可以为空的布尔值 CharField # 字符类型,必须提供max_length参数, max_length表示字符长度 TextField # 文本类型 EmailField # 字符串类型,Django Admin以及ModelForm中提供验证机制 IPAddressField # 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制 GenericIPAddressField # 字符串类型,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 # 字符串类型,Django Admin以及ModelForm中提供验证 URL SlugField # 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) CommaSeparatedIntegerField # 字符串类型,格式必须为逗号分割的数字 UUIDField # 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 FilePathField # 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 - 参数: path, 文件夹路径 match=None, 正则匹配 recursive=False, 递归下面的文件夹 allow_files=True, 允许文件 allow_folders=False, 允许文件夹 FileField # 字符串,路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage ImageField # 路径保存在数据库,文件上传到指定目录 - 参数: upload_to = "" 上传文件的保存路径 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage width_field=None, 上传图片的高度保存的数据库字段名(字符串) height_field=None 上传图片的宽度保存的数据库字段名(字符串)
自定义字段
# 自定义无符号整数字段 class UnsignedIntegerField(models.IntegerField): def db_type(self, connection): return 'integer UNSIGNED' # 自定义char类型字段 class FixedCharField(models.Field): def __init__(self, max_length, *args, **kwargs): self.max_length = max_length super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs) def db_type(self, connection): """ 限定生成数据库表的字段类型为char,长度为max_length指定的值 """ return 'char(%s)' % self.max_length class Class(models.Model): id = models.AutoField(primary_key=True) title = models.CharField(max_length=25) # 使用自定义的char类型的字段 cname = FixedCharField(max_length=25) PS: db_type返回值为字段在数据库中的属性,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)',
字段的参数
null #数据库中字段是否可以为空 db_column #数据库中字段的列名 default #数据库中字段的默认值 primary_key #数据库中字段是否为主键 db_index #数据库中字段是否可以建立索引 unique #数据库中字段是否可以建立唯一索引 unique_for_date #数据库中字段【日期】部分是否可以建立唯一索引 unique_for_month #数据库中字段【月】部分是否可以建立唯一索引 unique_for_year #数据库中字段【年】部分是否可以建立唯一索引 auto_now_add #时间字段值为生成记录的时间,转化成modelform不会展示 verbose_name #Admin中显示的字段名称 blank #Admin中是否允许用户输入为空 editable #Admin中是否可以编辑 help_text #Admin中该字段的提示信息 choices #Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) limit_choices_to # 翻译为modelform时过滤展示的字段,值是一个字典{"depart_id__in":[1002,1003]} 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'), ] )
元信息
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"),) # 默认排序 ordering = ['属性名'] # admin中显示的表名称 verbose_name # verbose_name加s verbose_name_plural
多表关系和参数
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,删除关联数据 a. 与之关联的值设置为指定值,设置:models.SET(值) b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) 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】
更多条件
执行原生SQL语句
QuerySet的更多方法
all() # 获取所有的数据对象 filter() # 条件查询 exclude() # 条件查询,排除满足条件的 # 只能用于正向查询 select_related("外键") 性能相关:表之间进行join连表操作,一次性获取关联的数据。 总结: 1. select_related主要针一对一和多对一关系进行优化。 2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能 prefetch_related("外键") 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。 总结: 1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。 2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。 annotate() # 聚合 distinct() # 去重 order_by() # 排序 reverse() # 反转 models.UserInfo.objects.all().order_by('-nid').reverse() # 需要要排过序 defer() # 去除模型类对象的某些属性,调用属性时会重新查找 only() # 仅保留模型类对象的某些属性 exists() # 是否有结果 update() # 更新 delete() # 删除 first() # 第一个 last() # 最后一个 count() # 获取个数 get() # 根据条件获取一个对象 create() # 创建对象 in_bulk() # 根据主键ID进行查找 id_list = [11,21,31] models.DDD.objects.in_bulk(id_list)
补充

• 首先, Class 是我们定义的模型。这没什么可意外的,想查找数据就应该使用相应的模型。
• 然后,访问 objects 属性。这叫管理器(manager),管理器负责所有“表层”数据操作,包括(最重要的)数据查询。所有模型都自动获得一个 objects 管理器,需要查询模型实例时都要使用它。
• 最后,调用 all() 方法。这是 objects 管理器的一个方法,返回数据库中的所有行。虽然返回的对象看似一个列表,但其实是一个查询集合(QuerySet)——表示数据库中一系列行的对象。
--- 这里的表名是自动生成的,如果要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。
from django.db import models class Class(models.Model): id = models.AutoField(primary_key=True) cname = models.CharField(max_length=20) class Meta(): db_table = "新名字"
- id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
- 本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。

# 首先要切换到项目所在目录下 python manage.py check #check 命令运行 Django 系统检查框架,即验证 Django 项目的一系列静态检查。如果一切正常,你将看到这个消息: System check identified no issues (0 silenced) 。如若不然,请确保你输入的模型代码是正确的。错误消息应该会告诉你代码哪里出错了。只要觉得模型有问题,就可以运行 python manage.py check ,它能捕获全部常见的模型问题。 python manage.py makemigrations APP名 # 运行上述命令,告诉 Django 你对模型做了修改(包括新建模型) # Django 把对模型(也就是数据库模式)的改动存储在迁移中,迁移就是磁盘中的文件。运行上述命令后,books 应用的 migrations 文件夹里会出现一个名为 0001_initial.py 的文件。 migrate 命令会查看最新的迁移文件,自动更新数据库模式; python manage.py sqlmigrate APP名 migrations文件夹里的数字开头 # 例如 python manage.py sqlmigrate books 0001 #sqlmigrate 命令的参数是迁移名称,输出的结果是对应的 SQL: #sqlmigrate 命令并不创建表,其实它根本不接触数据库,而是在屏幕上输出 Django 将执行的 SQL。如果愿意,可以把输出的 SQL 复制粘贴到数据库客户端里,然而,Django 为提交 SQL 提供了更为简单的方式 —— migrate 命令: python manage.py migrate #migrate 命令会查看最新的迁移文件,自动更新数据库模式;
#当数据库有参数时,修改模型增加属性 -- 如果没有设置可以为空,特没有设置默认值 # 执行makemigrations app01后 Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py Select an option: # 1 设置默认值 # 2 退出 # 设置默认组织 models.IntegerField(default=99) # 设置为空 models.IntegerField(null=True)
配置输出sql语句
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
Django中使用已有数据库
python manage.py inspectdb 会按照你的数据库生成Model 拷贝进去用就可以了!