跳转到主要内容

Django的实体-属性-值存储

项目描述

Build Status codecov Python Version Django Version Jazzband

Django EAV 2 - Django 的实体-属性-值存储

Django EAV 2 是 django-eav(本身又源自 eav-django)的分支。您可以在此处找到文档:这里

那么 EAV 究竟是什么?

实体-属性-值模型(EAV)是一种数据模型,以一种高效的空间方式来编码实体,其中可用于描述它们的属性(属性、参数)的数量可能非常庞大,但实际上应用到特定实体上的数量相对较小。这样的实体对应于数学中的稀疏矩阵。(维基百科)

EAV 中的数据存储为一个三元组(通常对应三个不同的表)

  • 实体:被描述的项目,例如 Person(name='Mike')
  • 属性:通常是一个指向属性表的外键,例如 Attribute(slug='height', datatype=FLOAT)
  • 属性的值,与属性和实体都有链接,例如 Value(value_float=15.5, person=mike, attr=height)

django-eav2 中的实体是您典型的 Django 模型实例。属性(名称和类型)存储在其自己的表中,这使得操作系统中可用的属性列表变得容易。值是属性和实体之间的一个中间表,每个实例持有单个值。这种实现还使得在 Django Admin 和表单实例中编辑属性变得容易。

您在此处可以找到 EAV 的详细描述

EAV - 好的、坏的或者丑的?

EAV 是灵活性和复杂度之间的权衡。因此,它不应被视为改善模式或反模式。它更像是一种 灰色模式 - 它存在于某些上下文中,用于解决某些问题。当适当使用时,它可以引入极大的灵活性,缩短原型设计时间或降低复杂性。但是,如果使用不当,它可能会使数据库模式复杂化,降低性能并使维护变得困难。像每个工具一样,不应过度使用。在接下来的段落中,我们将简要讨论其优点、缺点以及在使用 EAV 时需要注意的事项。

何时使用 EAV?

最初,EAV 是为了解决在关系模型中难以解决的问题而引入的。为了实现这一点,EAV 绕过了正常的模式限制。有人将此称为 内部平台效应 的例子。自然地,在这种情况下,RDMBS 资源无法得到有效利用。

EAV 模型的典型应用是为了解决具有大量适用属性但只有一小部分适用于特定实体的稀疏数据问题,而这些属性可能事先未知。考虑以下经典示例

数据模型师在生物医学领域经常遇到的一个问题是组织和存储高度多样化和异构的数据。例如,一个患者可能有数千个适用的描述性参数,所有这些都需要在电子病历系统中轻松访问。这些需求对建模和实现提出了重大挑战。[1]

并且

[...] 当你遇到要求实时、按需添加他们想要存储的属性的客户时,你会怎么做?在我管理的系统中,我们的客户就是这样要求的。由于我们运行的是一个SaaS(软件即服务)应用程序,我们在多个不同行业拥有许多客户,他们希望使用我们的系统来存储有关他们的客户的不同类型的信息。一家美发连锁店可能希望记录诸如“发色”、“发质”和“剪发频率”等事实;而一家投资公司可能希望记录诸如“投资组合名称”、“上次投资组合调整日期”和“当前投资组合余额”等事实。[2]

在这两个问题中,我们必须处理只适用于特定实体潜在不同子集的稀疏和异构属性。将EAV应用于数据库的子模式可以允许我们模拟所需的行为。传统的解决方案将涉及具有许多列且存储NULL值的宽表,这些列不适用于实体。

EAV在电子商务实施中的常见用例是自定义产品属性,例如在Magento中。[3]

一般来说,当

  • 模型属性需要由最终用户添加和删除(或以某种方式不可知)时,可以使用EAV。EAV支持这些操作,无需ALTER TABLE语句,并允许属性具有强类型且易于搜索。
  • 将有许多属性,值相对稀疏,这与具有大多数为空列的表形成对比。
  • 数据高度动态/易变/易受更改影响。这个问题存在于上述第二个示例中。其他示例包括快速发展的系统,例如具有不断变化的需求的原型。
  • 我们想要存储元数据或支持信息,例如自定义系统行为。
  • 需要表示多个类别的数据,每个类别的属性数量有限,但每个类别的实例数量非常小。
  • 我们希望最小化更改数据模型时程序员的输入。

有关更多关于适当用例的详细讨论,请参阅

  1. 维基百科 - 适用于EAV建模的场景
  2. StackOverflow - 实体属性值数据库与严格的关系模型电子商务
  3. WikiWikiWeb - 通用数据模型

何时避免使用?

正如我们在开头部分概述的,EAV是一个权衡。以下情况下不应使用EAV:

1. 系统是性能关键

当数据以EAV形式存储时,属性为中心的查询通常比以传统方式存储时更困难。[4]

一般来说,你的数据模型越结构化,你就越能够有效地处理它。因此,像EAV这样的松散数据存储在性能上具有明显的权衡。具体来说,应用EAV模型使得在表上执行JOIN操作变得更加复杂。

2. 低复杂度/低维护成本是优先考虑的

EAV通过在多个表中分割信息来复杂化数据模型。这增加了概念复杂性和查询数据所需的SQL语句。因此,在一个区域的优化也使得系统更难以理解和维护。

然而,重要的是要注意

仅应将EAV设计应用于需要模拟稀疏属性的数据库子模式:即使在这里,它们也需要由第三范式元数据表支持。遇到稀疏属性的数据设计问题相对较少:这就是为什么EAV设计适用的环境相对罕见。[1]

替代方案

在某些使用场景中,JSONB(二进制JSON数据)数据类型(PostgreSQL 9.4+以及其他RDBMS中的类似功能)可以用作EAV的替代品。JSONB支持索引,可以平衡性能的权衡。需要注意的是,JSONB并非RDBMS通用的解决方案,也有它自己的问题,比如类型问题。

安装

使用pip安装

pip install django-eav2

配置

eav添加到你的设置中的INSTALLED_APPS

INSTALLED_APPS = [
    ...
    'eav',
]

django.db.models.UUIDFielddjango.db.models.BigAutoField作为EAV2_PRIMARY_KEY_FIELD的值添加到你的设置中

EAV2_PRIMARY_KEY_FIELD = "django.db.models.UUIDField" # as example

注意:主键修改字段是必填的

如果在项目进行迁移的过程中需要修改eav模型的主键(UUIDField -> BigAutoField,BigAutoField -> UUIDField),你必须更改设置中的EAV2_PRIMARY_KEY_FIELD的值。

步骤1

将设置中的EAV2_PRIMARY_KEY_FIELD的值改为django.db.models.CharField

EAV2_PRIMARY_KEY_FIELD = "django.db.models.CharField"

运行迁移

python manage.py makemigrations
python manage.py migrate
步骤2

将设置中的EAV2_PRIMARY_KEY_FIELD的值改为期望的值(django.db.models.BigAutoFielddjango.db.models.UUIDField)。

EAV2_PRIMARY_KEY_FIELD = "django.db.models.BigAutoField" # as example

再次运行迁移。

 python manage.py makemigrations
 python manage.py migrate

注意:Django 2.2用户

由于Django 2.2不支持models.JSONField(),我们使用django-jsonfield-backport来提供JSONField功能。

这需要将django_jsonfield_backport添加到你的INSTALLED_APPS

INSTALLED_APPS = [
    ...
    'eav',
    'django_jsonfield_backport',
]

入门

步骤1. 注册一个模型

import eav
eav.register(Supplier)

或者使用装饰器

from eav.decorators import register_eav

@register_eav
class Supplier(models.Model):
    ...

步骤2. 创建一个属性

Attribute.objects.create(name='City', datatype=Attribute.TYPE_TEXT)

步骤3. 就这么简单!你可以开始使用了

supplier.eav.city = 'London'
supplier.save()

Supplier.objects.filter(eav__city='London')
# = <EavQuerySet [<Supplier: Supplier object (1)>]>

接下来做什么?查看文档


参考文献

[1] 探索使用实体-属性-值表示组织临床数据库的性能问题,https://doi.org/10.1136/jamia.2000.0070475
[2] EAV究竟有什么不好,究竟有多坏?,https://sqlblog.org/2009/11/19/what-is-so-bad-about-eav-anyway
[3] 开发者版Magento:第7部分—高级ORM:实体属性值,https://devdocs.magento.com/guides/m1x/magefordev/mage-for-dev-7.html
[4] 实体-属性-值数据库的数据提取和即席查询,https://www.ncbi.nlm.nih.gov/pmc/articles/PMC61332/

项目详情


下载文件

下载适合您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源代码分发

django_eav2-1.7.1.tar.gz (42.3 kB 查看哈希值)

上传时间: 源代码

构建分发

django_eav2-1.7.1-py3-none-any.whl (53.8 kB 查看哈希值)

上传时间 Python 3