模型层:Object Relational Mapping(ORM)
ORM
- 定义
ORM是Object Relational Mapping的简称,中文翻译为对象关系模型,是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术,ORM在业务逻辑层和数据库层之间充当了桥梁的作用。
- 由来
让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。
几乎所有的软件开发过程中都会涉及到对象和关系数据库。在用户层面和业务逻辑层面,我们是面向对象的。当对象的信息发生变化的时候,我们就需要把对象的信息保存在关系数据库中。
按照之前的方式来进行开发就会出现程序员会在自己的业务逻辑代码中夹杂很多SQL语句用来增加、读取、修改、删除相关数据,而这些代码通常都是重复的
- 本质
每个模型都是一个Python类,它是django.db.models.Model的子类。
- 优势
ORM解决的主要问题是对象和关系的映射,按照规定的语法写,自动翻译成对应的SQL语句.
1.不用自己写SQL语句
2. 开发效率高
- 劣势
ORM的缺点是会在一定程度上牺牲程序的执行效率。
ORM用多了SQL语句就不会写了,关系数据库相关技能退化...
- ORM能做的事儿:
1. 操作数据表 ===> 创建表/删除表/修改表
操作models.py里面的类
2. 操作数据行 ===> 数据的增删改查
不能创建数据库,自己动手创建数据库
Django中的ORM
Django项目如何使用ORM连接MySQL
1. 手动创建数据库
2. 在settings.py里面,配置数据库的连接信息
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'booksystem', 'HOST': '127.0.0.1', 'PORT': 3306, 'USER': 'root', 'PASSWORD': 'root', } }
3. 在项目/__init__.py告诉Django用pymysql模块代替MySQLdb来连接MySQL数据库
import pymysql pymysql.install_as_MySQLdb()
4. 在app/models.py里面定义类
# 作者类 class Author(models.Model): id = models.AutoField(primary_key=True) # 自增的ID主键 name = models.CharField(max_length=16, null=False, unique=True) book = models.ManyToManyField(to="Book") # 创建作者表和书籍表多对多的关系 # 多对多的关系会在数据库中另创建一个新的对应关系表,只存放id的对应关系 def __str__(self): return "<Author object>: {}".format(self.name)
5. 执行两个命令
在哪儿执行?
在项目的根目录(有manage.py文件的那个目录)
- python3 manage.py makemigrations --> 把models.py里面的更改记录到小本本上
- python3 manage.py migrate --> 把更改翻译成SQL语句,去数据库执行
简单使用
下面这个例子定义了一个 Person 模型,包含 first_name 和 last_name。
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30)
first_name 和 last_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。
上面的 Person 模型将会像这样创建一个数据库表:
CREATE TABLE myapp_person ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL );
一些说明:
- 表myapp_person的名称是自动生成的,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数,强烈建议使用小写表名,特别是使用MySQL作为后端数据库时。
- id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
- 本示例中的CREATE TABLE SQL使用PostgreSQL语法进行格式化,但值得注意的是,Django会根据配置文件中指定的数据库后端类型来生成相应的SQL语句。
- Django支持MySQL5.5及更高版本。
字段类型
单表字段类型
- 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):
- 小整数 -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,用于指定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
参数(上面两个都能用):
auto_now_add = True 创建数据行时(对象)就会把当前时间添加到数据库 auto_now = True 每次修改数据行(对象)时会把当前时间添加到数据库中
- TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
- DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
- FloatField(Field)
- 浮点型
- DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度 decimal_places,小数位长度
- BinaryField(Field)
- 二进制类型
抽象基类
当你想将一些共有信息放进其它一些model的时候,抽象化类是十分有用的。 你编写完基类之后,在 Meta类中设置 abstract=True
, 这个模型就不会被用来创建任何数据表。 取而代之的是,当它被用来作为一个其他model的基类时,它的字段将被加入那些子类中。 如果抽象基类和它的子类有相同的字段名,那么将会出现error(并且Django将抛出一个exception)。
一个例子
from django.db import models class CommonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True class Student(CommonInfo): home_group = models.CharField(max_length=5)
Student
模型将有三个字段:name
、age
和 home_group
。 CommonInfo
模型无法像一般的Django模型一样使用,因为它是一个抽象基类。 它无法生成一张数据表或者拥有一个管理器,并且不能实例化或者直接储存。
许多应用场景下, 这种类型的模型继承恰好是你想要的。 它提供了一种在Python层面排除常见信息的方法,同时仍然只在数据库层面为每个子模型创建一个数据库表。
meta继承:
当一个抽象基类被创建的时候, Django把你在基类内部定义的 Meta 类作为一个属性使其可用。 如果子类没有声明自己的Meta类, 它将会继承父类的Meta。如果子类想要扩展父类的Meta类,它可以子类化它。 例如:
from django.db import models class CommonInfo(models.Model): # ... class Meta: abstract = True ordering = ['name'] class Student(CommonInfo): # ... class Meta(CommonInfo.Meta): db_table = 'student_info'
Django确实会对抽象基类的Meta类做一个调整:在设置Meta属性之前,Django会设置abstract=False
。 这意味着抽象基类的子类本身不会自动变成抽象类。当然,你可以让一个抽象基类继承自另一个抽象基类, 你只要记得每次都要显式地设置 abstract=True
。
关联关系字段
Django还定义一系列字段来描述数据库之间的关联。
ForeignKey(多对一)
多对一关系。要求两个位置参数:模型相关的类和on_delete
选项。 (on_delete
实际上并不是必需的,但不提供它会给出已废弃的警告。 在Django 2.0中将需要它。)
若要创建递归关联关系 ——————— 一个对象与自己具有多对一关联关系 — 请使用models.ForeignKey('self', on_delete=models.CASCADE)
。
-
引用关系(三种情况)
如果你需要关联到一个还没有定义的模型,你可以使用模型的名字而不用模型对象本身:
from django.db import models class Car(models.Model): manufacturer = models.ForeignKey( 'Manufacturer', # 同一个应用下的表直接使用名字即可,不同应用的要'app_name'.'table_name'(即使用app名加表名) on_delete=models.CASCADE, ) # ... class Manufacturer(models.Model): # ... pass
抽象模型上定义的这种关联关系在模型子类化为具体模型时解析,并且不相对于抽象模型的app_label
:
from django.db import models class AbstractCar(models.Model): manufacturer = models.ForeignKey('Manufacturer', on_delete=models.CASCADE) class Meta: abstract = True
from django.db import models from products.models import AbstractCar class Manufacturer(models.Model): pass class Car(AbstractCar): pass # Car.manufacturer将指向这里的`production.Manufacturer`。
若要引用在其它应用中定义的模型,你可以用带有完整标签名的模型来显式指定。 例如,如果上面的Manufacturer
模型是在一个名为production
的应用中定义的,你应该这样使用它:
class Car(models.Model): manufacturer = models.ForeignKey( 'production.Manufacturer', on_delete=models.CASCADE, )
这种称为懒惰关系的引用在解析两个应用程序之间的循环导入依赖关系时可能很有用。
- 数据库表示
在幕后,Django 会在字段名上添加"_id"
来创建数据库中的列名。 在上面的例子中,Car
模型的数据库表将会拥有一个manufacturer_id
列。 (你可以通过指定db_column
来显式更改)但是,除非你编写自定义SQL,否则代码不应该处理数据库列名。 你应该永远只处理你的模型对象中的字段名称。
ForeignKey
会自动创建数据库索引。 你可以通过设置db_index
为False
来取消。 如果你创建外键是为了一致性而不是用来Join,或者如果你将创建其它索引例如部分或多列索引,你也许想要避免索引的开销。
-
参数
ForeignKey.
to
- 要进行关联的表名,to=“User”和to=User,to本质是和类做关联,当User类在当前模块被导入时,才可以不加双引号,否则要加双引号
ForeignKey.
on_delete
ForeignKey
引用的对象时,Django将模拟由on_delete
参数指定的SQL约束的行为。 自1.9版以来已弃用on_delete
将成为Django 2.0中必需的参数。 在旧版本中,默认为CASCADE。
CASCADE
- 级联删除,删除关联数据,与之关联也删除,Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。
PROTECT
- 抛出
ProtectedError
以阻止被引用对象的删除,它是django.db.IntegrityError
的一个子类。
-
SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
SET_DEFAULT
- 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
SET
()- 设置
ForeignKey
为传递给SET()
的值,如果传递的是一个可调用对象,则为调用后的结果。 在大部分情形下,传递一个可调用对象用于避免models.py 在导入时执行查询: - a. 与之关联的值设置为指定值,设置:
models.SET(值)
- b. 与之关联的值设置为可执行对象的返回值,设置:
class MyModel(models.Model): user = models.ForeignKey( to="User", to_field="id", on_delete=models.SET(func), )
DO_NOTHING
不采取任何动作。 如果您的数据库后端强制引用完整性,除非手动添加SQL
ON DELETE
约束,否则将导致IntegrityError
到数据库字段。
ForeignKey.
limit_choices_to
当这个字段使用ModelForm
或者Admin 渲染时(默认情况下,查询集中的所有对象都可以使用),为这个字段设置一个可用的选项。 它可以是一个字典、一个Q
对象或者一个返回字典或Q
对象的可调用对象。
# 在Admin或ModelForm中显示关联数据时,提供的条件: # 如: - 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) |
-
反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()。它还是
related_query_name
的默认值。如果你不想让Django 创建一个反向关联,请设置
related_name
为'+'
或者以'+'
结尾。 例如,下面这行将确定User
模型将不会有到这个模型的返回关联:user = models.ForeignKey( User, on_delete=models.CASCADE, related_name='+', )
-
反向操作时,使用的连接前缀,用于替换【表名】 如:
models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
用于过滤器或者value,不直接用于.后面
ForeignKey.
to_field
-
关联到的关联对象的字段名称。 默认地,Django 使用关联对象的主键。 如果引用其他字段,该字段必须具有
unique=True
。
ForeignKey.
db_constraint
-
控制是否在数据库中为这个外键创建约束。 默认值为
True
,这几乎是你想要的;将此设置为False
可能对数据完整性非常不利。 即便如此,有一些场景你也许想要这么设置:- 你有遗留的无效数据。
- 你正在对数据库缩容。
如果被设置成
False
,访问一个不存在的关联对象将抛出DoesNotExist
异常。
ManyToManyField(多对多)
- class
ManyToManyField
(to, **options)[source]
一个多对多关联。关联的对象可以通过字段的RelatedManager
添加、删除和创建。
数据库表示
在幕后,Django 创建一个中间表来表示多对多关系。 默认情况下,这张中间表的名称使用多对多字段的名称和包含这张表的模型的名称生成。 因为某些数据库支持的表的名字的长度有限制,这些表的名称将自动截短到64 个字符并加上一个唯一性的哈希值。 这意味着你可能会看到像author_books_9cdf4
这样的表名;这是完全正常的。 你可以使用db_table
选项手工提供中间表的名称。
参数
ManyToManyField.
to
ManyToManyField.to_filed
ManyToManyField.on_delete
ManyToManyField.
related_name
ManyToManyField.
related_query_name
-
上面几个属性参考foreignerkey的参数即可相同。
ManyToManyField.
limit_choices_to
-
与
ForeignKey.limit_choices_to
相同。ManyToManyField
对于使用through
参数自定义中间表的limit_choices_to
不生效。
ManyToManyField.
symmetrical
-
只用于与自身进行关联的ManyToManyField。 例如下面的模型:
from django.db import models class Person(models.Model): friends = models.ManyToManyField("self")
当Django 处理这个模型的时候,它定义该模型具有一个与自身具有多对多关联的
ManyToManyField
,因此它不会向person_set
类添加Person
属性。 Django 将假定这个ManyToManyField
字段是对称的 —— 如果我是你的朋友,那么你也是我的朋友。如果你希望与
self
进行多对多关联的关系不具有对称性,可以设置symmetrical
为False
。 这会强制让Django 添加一个描述器给反向的关联关系,以使得ManyToManyField
的关联关系不是对称的。
ManyToManyField.
through (使用自己指定的第三方表
)
-
Django 会自动创建一个表来管理多对多关系。 不过,如果你希望手动指定中介表,可以使用
through
选项来指定Django 模型来表示你想要使用的中介表。这个选项最常见的使用场景是当你想要关联更多的数据到关联关系的时候。
如果你没有显式指定
through
的模型,仍然会有一个隐式的through
模型类,你可以用它来直接访问对应的表示关联关系的数据库表。 它由三个字段来链接模型。如果源模型和目标不同,则生成以下字段:
id
:关系的主键。<containing_model>_id
:声明了ManyToManyField
的模型的id
<other_model>_id
: 被ManyToManyField
所指向的模型的id
如果
ManyToManyField
的源模型和目标模型相同,则生成以下字段:id
:关系的主键。from_<model>_id
:源模型实例的id
。to_<model>_id
:目标模型实例的id
。
这个类可以让一个给定的模型像普通的模型那样查询与之相关联的记录。
ManyToManyField.
through_fields
-
只能在指定了自定义中间模型的时候使用。 Django 一般情况会自动决定使用中间模型的哪些字段来建立多对多关联。 但是,考虑如下模型:
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)
Membership
有两个 foreign keys指向Person
(person
andinviter
), 这样会导致关系不清晰,Django不知道使用哪一个外键。 在这种情况下,你必须使用through_fields
明确指定Django 应该使用哪些外键,就像上面例子一样。through_fields
接受一个2元数组('field1', 'field2')
, 其中field1
是指向定义了ManyToManyField
的那个model的 foreign key的名字(在本例中就是group
,它自己定义了M2M字段,同时也在中间模型中被ForeignKey所指向 ), andfield2
就是目标模型的foreign key 的名字 (person
in this case).当中间模型具有多个外键指向多对多关联关系模型中的任何一个(或两个),你必须 指定
through_fields
。 这通用适用于recursive relationships,当用到中间模型而有多个外键指向该模型时,或当你想显式指定Django 应该用到的两个字段时。递归的关联关系使用的中间模型始终定义为非对称的,也就是
symmetrical=False
—— 所以具有源和目标的概念。 这种情况下,'field1'
将作为管理关系的源,而'field2'
作为目标。
ManyToManyField.
db_table
-
为存储多对多数据而创建的表的名称。 如果没有提供,Django 将基于定义关联关系的模型和字段假设一个默认的名称。
ManyToManyField.
db_constraint
-
控制中间表中的外键是否创建约束。 默认值为
True
,这几乎是你想要的;将此设置为False
可能对数据完整性非常不利。 即便如此,有一些场景你也许想要这么设置:- 你有遗留的无效数据。
- 你正在对数据库缩容。
不可以同时传递
db_constraint
和through
。
OneToOneField(一对一)
- class
OneToOneField
(to, on_delete, parent_link=False, **options)[source]
一对一关联关系。 概念上讲,这个字段类似ForeignKey
设置了unique=True
,不同的是关联关系的另一边会直接返回单个对象。选定了以后别的不能选了,不像多对一,别的还能选
什么时候用一对一?
当 一张表的某一些字段查询的比较频繁,另外一些字段查询的不是特别频繁,把不怎么常用的字段,单独拿出来做成一张表,然后用过一对一关联起来。
一对一关系使用案例
# 作者 class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() phone = models.IntegerField() books = models.ManyToManyField(to="Book", related_name="authors") detail = models.OneToOneField(to="AuthorDetail") def __str__(self): return self.name # 作者详情 class AuthorDetail(models.Model): # 爱好 hobby = models.CharField(max_length=32) # 地址 addr = models.CharField(max_length=128)
注意:一对一关系时,默认值会不好使,以为这列不能有相同的,全为none也不行
如果你没有指定OneToOneField
的related_name
参数,Django 将使用当前模型的小写的名称作为默认值。
例如下面的例子:
from django.conf import settings from django.db import models class MySpecialUser(models.Model): user = models.OneToOneField( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, ) supervisor = models.OneToOneField( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='supervisor_of', )
你将使得User
模型具有以下属性:
>>> user = User.objects.get(pk=1) >>> hasattr(user, 'myspecialuser') True >>> hasattr(user, 'supervisor_of') True
自定义字段
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)',
自定义char类型字段:
class FixedCharField(models.Field): """ 自定义的char类型的字段类 """ 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)
创建的表结构:
字段通用参数
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': '格式错误'}
从数据库中逆向生成ORM
正常情况在models.py中写下ORM语句,然后使用makemigrations和migrate即可将models中表结构映射到数据库中,当有了数据库的表结构却没有ORM又该如何呢?下面是从数据库中逆向生成ORM的方法:
首先呢:我们就需要根据数据库去自动生成新的models文件
python manage.py inspectdb #简单可以看一下自动映射成的models中的内容
这个语句只会将ORM语句输出到控制台中,并没有将其保存到文件里,可以手动将这些代码保存到文件里,也可以使用下面的语句。
导出并且去代替models.py
python manage.py inspectdb > models.py
这样你就会发现在manage.py的同级目录下生成了一个models.py文件,使用这个models.py文件覆盖app中的models文件,或者将文件里的语句复制到你自己的models.py文件中。
文件的头部提示信息如下:
# This is an auto-generated Django model module. # You'll have to do the following manually to clean this up: # * Rearrange models' order # * Make sure each model has one field with primary_key=True # * Make sure each ForeignKey has `on_delete` set to the desired behavior. # * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table # Feel free to rename the models, but don't rename db_table values or field names.
如果完成了以上的操作,生成的是一个不可修改/删除的models(managed=False),修改meta class中的managed = True则可以去告诉django可以对数据库进行操作,下面解释managed属性的意义
managed:
Options.
managed
- 默认为
True
,表示Django会通过migrate
创建合适的数据表,并且可通过flush
管理命令移除这些数据库表。 换句话说,Django会管理这些数据表的生命周期。如果是False
,Django 就不会为当前模型创建和删除数据表。 如果当前模型表示一个已经存在的且是通过其它方法创建的者数据表或数据库视图,这会相当有用。 这是设置为managed=False
时唯一的不同之处。 模型处理的其它任何方面都和平常一样。 这包括:- 如果你不声明它的话,会向你的模型中添加一个自增主键。 为了避免给后面的代码读者带来混乱,当你在使用未被管理的模型时,强烈推荐你指定(specify)数据表中所有的列。
- 如果一个模型设置了
managed=False
且含有ManyToManyField
,且这个多对多字段指向其他同样也是未被管理模型的,那么这两个未被管理的模型的多对多中介表也不会被创建。 但是,一个被管理模型和一个未被管理模型之间的中介表就会被创建。如果你需要修改这一默认行为,创建中介表作为显式的模型(也要设置managed
),并且使用ManyToManyField.through
为你的自定义模型创建关联。
managed=False
),那么在测试之前,你应该要确保在 测试启动时 已经创建了正确的数据表。如果你对在Python层面修改模型类的行为感兴趣,你可以设置managed=False
,并且为一个已经存在的模型创建一个副本。 不过在面对这种情况时还有个更好的办法就是使 用Proxy models.
补充:
自定义表名,列名
在我们创建一个模型时,Django的ORM会根据应用名(app name), 模型名(model name)和字段名(field name)自动在数据库中创建数据表。比如我们有一个Blog的应用,里面有Article模型, 其中Article模型有title这个字段,那么Django默认会创建一个名为blog_article的数据表,其中有title这个字段。假如我们希望把表名改为article,标题改为article_title,以便与已经存在的数据表或字段建立映射关系,我们可以按如下代码操作。
class Article(models.Model): """文章模型""" # 通过db_column自定义数据表中字段名 title = models.CharField('标题', max_length=200, db_column='article_title') slug = models.SlugField('slug', max_length=60, blank=True) def __str__(self): return self.title class Meta: db_table = 'article' # 通过db_table自定义数据表名
通过db_table指定数据表名,还可以通过db_column指定希望访问的字段名。参考:https://blog.csdn.net/weixin_42134789/article/details/84567297
自定义模型管理器对象(替换object)
假如models里面有这样一个表数据:
class User(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32) roles = models.ManyToManyField(to="Role") def __str__(self): return self.name
查询的时候使用下面的语句查询
models.User.objects.filter(name=username, pwd=password).first()
写过orm查询的都知道这句话的用法,那么User.objects究竟是什么呢?
objects是Django帮我们自动生成的管理器对象,通过这个管理器可以为数据的查询添加点共性的东西。比如添加额外的方法和修改查询结果
objects是models.Manager类的一个对象。
class Manager(BaseManager.from_queryset(QuerySet)): pass
但是如果在里自己生成管理器对象,那么原来的objects将不再好使,改成你定义的那种,比如:
class User(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32) roles = models.ManyToManyField(to="Role") user = models.Manager() # 自定义了一个Manager类对象 def __str__(self): return self.name
这时,User.objects将不好使,而User.user将完全代替User.objects,下面的图说明这些关系
例如下面,这样修改管理器类的all方法,object.all()方法的内容就会更着改变,这样就可以添加方法和修改原方法