Django多态模型的入门教程
多态性是一种在数据库中进行数据建模的技术。它允许应用程序在同一个数据库表中存储不同类型的数据。
本教程将对一个处理印刷书籍和电子书销售的在线书店进行建模。
我们将从创建简单的模型开始,随着网上书店的扩大和处理更多的产品,我们将在模型中添加更多的字段。
在一个网上书店中,一个用户可以订购一本小说的电子书版本,而另一个用户则订购同一本书的印刷版本。
在数据库中,它是同一本书,但有不同的属性。电子书版本将包括一个下载链接,而印刷版本则没有。
印刷版可以有一个重量属性,我们可以用它来计算电子书版的交付费用。
前提条件
要跟上这篇文章,你需要具备以下条件。
-
在你的电脑上安装了[Python]和[virtualenv]。
-
对[Django]和[Python]有一定了解。
多态性建模
Django ORM提供了几种对多态数据建模的方法。正如我们将在下面研究的那样,我们既可以使用标准的Django功能,也可以使用高级的Django ORM功能。
我们将从最简单的方法开始,到实现多态模型的复杂方式。
第一步是创建一个新的Django项目,我们将在本指南中全程使用。
通过执行以下命令创建一个名为models 的新工作目录。
mkdir modeling
执行下面的命令,将工作目录改为我们上面创建的目录,并创建一个新的虚拟环境。
Django项目使用虚拟环境来隔离管理项目的依赖关系,以避免与其他项目发生依赖冲突。
virtualenv venv
source venv/bin/activate
pip install django
执行下面的命令,创建一个新的Django项目,名为polymorphic 。
django-admin startproject polymorphic
由于Django项目被组织成应用程序,执行下面的命令来创建一个modeling 应用程序。这就是我们将为我们的应用程序数据库建模的地方。
python manage.py startapp modeling
Django提供了几种建模模式,我们将在本指南中进行介绍。它们包括。
- 默认建模模式
- 稀疏的建模模式
- 半结构化的建模模式
- 抽象建模模式
默认建模
本节将使用默认的Django建模功能为我们的在线书店建模。在models.py 文件中添加以下代码。
from django.contrib.auth import get_user_model
from django.db import models
# Publication is a journal that can be ordered from the bookstore a hard copy file
class Publication(models.Model):
publication_title = models.CharField(max_length=50)
publication_price = models.PositiveIntegerField()
publication_weight = models.PositiveIntegerField() # The weight of the print publication is required for calculations of the delivery fees
def __str__(self) -> str:
return self.publication_title
# UserCart manages the user publications add on the cart
class UserCart(models.Model):
visitor_cart = models.OneToOneField(
get_user_model(), # Maps the visitor fields with the currently logged-in user
primary_key=True,
on_delete=models.CASCADE,
)
publications = models.ManyToManyField(Publication)
在上面的代码片段中,我们创建了一个出版模型,它将保存商店中正在销售的书籍的信息。
UserCart 模型保存了当前登录用户的信息以及用户在结账前添加到购物车的出版物。
| 优点 | 缺点 |
|---|---|
| 易于建模和维护 | 只适合于具有相同属性的产品 |
稀疏的建模
现在我们的网上商店已经有了一些客户,他们要求用电子书代替印刷品。因此,我们需要修改Publication 模型以适应印刷品和电子书。
用下面的代码替换models.py 文件中的代码片断。
# Publication is a journal that can be ordered from the bookstore a hard copy file
class Publication(models.Model):
PUBLICATION_CHOICES = (
("Print", 'Print'),
("Ebook", 'Ebook'),
)
publication_title = models.CharField(max_length=50)
publication_price = models.PositiveIntegerField()
publication_type = models.CharField(
max_length=50,
choices=PUBLICATION_CHOICES,
) # Publication type is required to check wether a download link is required for the publication
publication_download_link = models.URLField(null=True,
blank=True, ) # The ebook publications require a download link that can be used to download the book
publication_weight = models.PositiveIntegerField() # The weight of the print publication is required for calculations of the delivery fees
def __str__(self) -> str:
return self.publication_title
# UserCart manages the user publications add on the cart
class UserCart(models.Model):
visitor_cart = models.OneToOneField(
get_user_model(), # Maps the visitor fields with the currently logged-in user
primary_key=True,
on_delete=models.CASCADE,
)
publications = models.ManyToManyField(Publication)
在上面的代码片段中,我们向Publication 模型添加了两个字段。
publication_weight 属性存储了印刷出版物的重量。我们可以使用这个字段来计算出版物的交付费用。
publication_download_link 字段存储出版物的下载链接,如果它是一本电子书。
| 优点 | 劣势 |
|---|---|
| 易于建模和维护 | 没有利用NOT NULL数据库约束 |
| 向模型添加新的属性需要修改模型 |
半结构化的建模
随着我们在线商店的图书销售量的增加,可空字段的数量也在增加。不幸的是,这正变得难以维护。
为了解决越来越多的可空字段,我们可以使用JSONField 来存储其他属性,并使用模型来存储常用属性。
用下面的代码更新modeling 应用程序中的models.py 文件。
from django.contrib.auth import get_user_model
from django.db import models
# Publication is a journal that buyers can order from the bookstore a hard copy file
from django.db.models import JSONField
class Publication(models.Model):
PUBLICATION_CHOICES = (
("Print", 'Print'),
("Ebook", 'Ebook'),
)
publication_title = models.CharField(max_length=50)
publication_price = models.PositiveIntegerField()
publication_type = models.CharField(
max_length=50,
choices=PUBLICATION_CHOICES,
) # Publication type is required to check wether a download link is required for the publication
publication_download_link = models.URLField(null=True,
blank=True, ) # The ebook publications require a download link that can be used to download the book
publication_extra_fields = JSONField() # The extra fields will be stored in the database as Json field
def __str__(self) -> str:
return self.publication_title
# UserCart manages the user publications add on the cart
class UserCart(models.Model):
visitor_cart = models.OneToOneField(
get_user_model(), # Maps the visitor fields with the currently logged-in user
primary_key=True,
on_delete=models.CASCADE,
)
publications = models.ManyToManyField(Publication)
半结构化模型允许我们在模型上保留公共字段,而其他字段如weight 和download link 在JSONField 。
| 优点 | 劣势 |
|---|---|
| 减少了空字段的数量 | 复杂的验证,我们必须在保存前独立验证所有的JSON数据字段。 |
| 更容易添加新的属性。 | 对数据库的支持有限,因为不是所有的数据库都支持JSON作为一种数据类型。 |
抽象基础建模
到目前为止,我们已经用一个单一的Django模型对我们的产品进行建模。我们的在线书店已经扩大了,我们现在想销售其他产品,即期刊。
我们之前的模型设计不够高效,无法让我们销售不同类型的产品。因此,我们需要为我们的数据库模型开发一个最佳解决方案。
用下面的代码片断更新modeling 应用程序中的models.py 。
from django.db import models
# Publication is a journal that can be ordered from the bookstore a hard copy file
from django.db.models import JSONField
# This model represents the pdf downloadable version of a publication
class Ebook(models.Model):
ebook_download_link = models.URLField()
# This the new type of publication that the users can download
class Journal(models.Model):
journal_download_link = models.URLField()
- 在上面的代码片断中,我们创建了一个标准模型
Publication,其他产品将扩展该模型。通过这种类型的设计,我们销售的产品的共同属性在Publication模型中,而我们销售的产品的具体属性在特定的产品模型中。例如,Ebook产品有一个特定的字段book_weight。
| 优点 | 劣势 |
|---|---|
| 易于维护、设计和测试 | 难以扩展,因为每个新产品都需要一个额外的模型 |
总结
在这篇文章中,你已经学会了如何创建多态的Django模型。
你现在可以在你的应用程序中实现多态的Django模型,以减少复杂性。确保你使用适合你的用例的多态性方法。