Models and databases
模型是有关您的数据的唯一、明确的数据源。它包含您存储的数据的基本字段和行为。通常,每个模型都映射到单个数据库表。
模型
基础知识:
每个模型都是一个子类化的 Python 类 django.db.models.Model
。
模型的每个属性代表一个数据库字段。
有了所有这些,Django 为您提供了一个自动生成的数据库访问 API
;请参阅进行查询。
快速示例
这个示例模型定义了一个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
自动从某些模型元数据派生而来,但可以被覆盖。有关更多详细信息,请参阅表名称。
一个id字段被自动添加,但这种行为可以被覆盖。请参阅自动主键字段。
此示例中的CREATE TABLE SQL
使用 PostgreSQL
语法进行格式化,但值得注意的是 Django 使用为您的设置文件中指定的数据库后端量身定制的 SQL 。
一旦你定义了你的模型,你需要告诉 Django 你将使用这些模型。 为此,请编辑您的设置文件并更改 INSTALLED_APPS
设置以添加包含您的 models.py
的模块的名称。
例如,如果应用程序的模型位于 myapp.models
模块(由 manage.py startapp
脚本为应用程序创建的包结构)中,则 INSTALLED_APPS
应部分读取:
INSTALLED_APPS = [
#...
'myapp',
#...
]
当您向 INSTALLED_APPS
添加新应用程序时,请确保运行 manage.py migrate
,可选择首先使用 manage.py makemigrations 为它们进行迁移。
字段
模型最重要的部分——也是模型唯一需要的部分——是它定义的数据库字段列表。字段由类属性指定。要小心,不要选择字段名称与冲突 模型API一样比如clean,save或 delete。
字段类型
模型中的每个字段都应该是相应 Field 类的实例。 Django 使用字段类类型来确定一些事情:
- 列类型,它告诉数据库要存储的数据类型(例如 INTEGER、VARCHAR、TEXT)。
- 渲染表单字段时使用的默认
HTML
小部件(例如<input type="text">, <select>
)。 - 在 Django 的管理和自动生成的表单中使用的最小验证要求。
Django 附带了数十种内置字段类型; 您可以在模型字段参考中找到完整列表。 如果 Django 的内置字段不起作用,您可以轻松编写自己的字段; 请参阅编写自定义模型字段。
字段选项
每个字段采用一组特定于字段的参数(记录在 模型字段参考中)。例如, CharField(及其子类)需要一个 max_length参数来指定VARCHAR用于存储数据的数据库字段的大小。
还有一组适用于所有字段类型的通用参数。都是可选的。它们在参考资料中得到了完整的解释,但这里是最常用的快速总结:
-
null
如果True,Django 将像NULL在数据库中一样存储空值。默认为False。 -
blank
如果True,则允许该字段为空。默认为False。
请注意,这与null. null纯粹与数据库相关,而 blank与验证相关。如果字段具有blank=True
,则表单验证将允许输入空值。如果字段具有blank=False
,则该字段将是必需的。 -
choices
一个序列的2元组,作为该领域的选择使用。如果给出了这个,默认的表单小部件将是一个选择框而不是标准文本字段,并将选择限制为给定的选择。
选择列表如下所示:
YEAR_IN_SCHOOL_CHOICES = [
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
]
关系
显然,关系数据库的强大之处在于将表相互关联。Django 提供了定义三种最常见的数据库关系类型的方法:多对一、多对多和一对一。
多对一关系
要定义多对一关系,请使用django.db.models.ForeignKey。您可以像使用任何其他Field类型一样使用它:通过将其作为模型的类属性包含在内。
ForeignKey 需要一个位置参数:与模型相关的类。
例如,如果一个Car模型有一个Manufacturer——也就是说,一个 Manufacturer制造了多辆汽车,但每辆车Car只有一辆 Manufacturer——使用以下定义:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...
您还可以创建递归关系(与自身具有多对一关系的对象)以及与尚未定义的模型的关系;有关详细信息,请参阅模型字段参考。
建议(但不是必需)ForeignKey字段的名称 (manufacturer在上面的示例中)是模型的名称,小写。您可以随意调用该字段。例如:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
)
# ...
多对多关系
要定义多对多关系,请使用 ManyToManyField。您可以像使用任何其他Field类型一样使用它 :通过将其作为模型的类属性包含在内。
ManyToManyField 需要一个位置参数:与模型相关的类。
例如,如果 aPizza有多个Topping对象——也就是说,a Topping可以在多个比萨饼上并且每个Pizza都有多个浇头——你可以这样表示:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
跨文件模型
将模型与另一个应用程序中的模型相关联是完全可以的。为此,请在定义模型的文件顶部导入相关模型。然后,在需要的地方引用其他模型类。例如:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
ZipCode,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
模型属性
objects
模型最重要的属性是 Manager. 它是向 Django 模型提供数据库查询操作的接口,用于 从数据库中检索实例。如果Manager未定义自定义,则默认名称为 objects. 管理器只能通过模型类访问,不能通过模型实例访问。
模型方法
在模型上定义自定义方法以向对象添加自定义“行级”功能。虽然Manager方法有尽而意“表范围”的东西,模型方法应该作用于特定的模型实例。
这是一种将业务逻辑保持在一个地方——模型——的有价值的技术。
例如,这个模型有一些自定义方法:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
@property
def full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
本示例中的最后一个方法是属性。
该模型实例参考具有的完整列表,自动给每个模型的方法。您可以覆盖其中的大部分 - 请参阅下面的覆盖预定义模型方法- 但您几乎总是想要定义一些:
__str__()
一个 Python 的“魔法方法”,它返回任何对象的字符串表示。这就是 Python 和 Django 将在模型实例需要强制转换并显示为纯字符串时使用的方法。最值得注意的是,当您在交互式控制台或管理中显示对象时会发生这种情况。
你总是想定义这个方法;默认值根本不是很有帮助。
get_absolute_url()
这告诉 Django 如何计算对象的 URL。Django 在它的管理界面中使用它,任何时候它需要找出一个对象的 URL。
任何具有唯一标识它的 URL 的对象都应该定义此方法。
超类方法
重要的是要记住调用超类方法——这就是业务——以确保对象仍然保存到数据库中。如果您忘记调用超类方法,则不会发生默认行为,也不会触及数据库。
super().save(*args, **kwargs)
执行自定义 SQL
另一种常见模式是在模型方法和模块级方法中编写自定义 SQL 语句。
模型继承
Django 中模型继承的工作方式与 Python 中普通类继承的工作方式几乎相同,但仍应遵循页面开头的基础知识。这意味着基类应该子类化 django.db.models.Model.
您必须做出的唯一决定是,您是否希望父模型本身成为模型(具有自己的数据库表),或者父模型是否只是仅通过子模型可见的公共信息的持有者。
在 Django 中有三种可能的继承风格。
- 通常,您只想使用父类来保存您不想为每个子模型输入的信息。这个类不会被单独使用,所以抽象基类是你所追求的。
- 如果您对现有模型(可能完全来自另一个应用程序)进行子类化,并希望每个模型都有自己的数据库表, 多表继承是可行的方法。
- 最后,如果您只想修改模型的 Python 级行为,而不以任何方式更改模型字段,则可以使用 Proxy models。
代理模型
使用多表继承时,将为模型的每个子类创建一个新的数据库表。这通常是期望的行为,因为子类需要一个地方来存储基类中不存在的任何附加数据字段。然而,有时您只想更改模型的 Python 行为——也许是更改默认管理器,或添加新方法。
这就是代理模型继承的用途:为原始模型创建代理。您可以创建、删除和更新代理模型的实例,并且所有数据都将被保存,就像您使用原始(非代理)模型一样。不同之处在于您可以更改默认模型排序或代理中的默认管理器等内容,而无需更改原始模型。
代理模型像普通模型一样声明。您可以通过将类的proxy的Meta属性设置为True 来告诉 Django 它是一个代理模型。
例如,假设您想向Person模型添加一个方法。你可以这样做:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
MyPerson 类在与其父 Person 类相同的数据库表上运行。 特别是,任何新的 Person 实例也可以通过 MyPerson 访问,反之亦然:
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
您还可以使用代理模型在模型上定义不同的默认排序。您可能并不总是希望对Person模型进行排序,而是last_name在使用代理时按属性定期排序:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
现在普通Person查询将是无序的,OrderedPerson查询将按 排序last_name。
代理模型以与常规模型相同的方式继承Meta属性。
在包中组织模型
manage.py startapp
命令创建一个包含 models.py
文件的应用程序结构。 如果您有许多模型,将它们组织在单独的文件中可能会很有用。
为此,请创建一个模型包。 删除 models.py
并使用 __init__.py
文件和用于存储模型的文件创建一个 myapp/models/
目录。 您必须在 __init__.py
文件中导入模型。
例如,如果您在models
目录中有organic.py
和 synthetic.py
:
myapp/models/__init__.py
from .organic import Person
from .synthetic import Robot
显式导入每个模型而不是使用 from .models import *
的优点是不会使命名空间混乱,使代码更具可读性,并使代码分析工具保持有用。