跳转到主要内容

使用Django模型表单编辑JSON字段

项目描述

django-entangled

使用标准Django表单编辑JSON模型字段。

Build Status Coverage PyPI PyPI version PyPI

使用场景

Django模型可能包含接受任意数据存储为JSON的字段。Django本身提供JSON字段来存储任意可序列化的数据。

当从模型创建表单时,与JSON字段相关联的输入字段通常是<textarea ...></textarea>。这个textarea小部件对于编辑来说非常不便,因为它只包含该对象表示法的文本表示。一种可能性是使用一个通用的JSON编辑器,它通过一些JavaScript将小部件转换为一个属性值对编辑器。然而,这种方法需要我们自行管理字段键,并且还会阻止我们利用Django表单框架提供的所有良好功能,例如字段验证、数据归一化和外键的使用。

通过使用 django-entangled,可以像使用 Django 的 ModelForm 一样,将表单的所有字段或其中一部分字段存储在关联模型的一个或多个 JSON 字段中。

安装

只需安装此 Django 应用,例如通过调用

pip install django-entangled

无需向项目的 settings.py 文件中添加任何配置指令。

示例

假设,我们有一个 Django 模型来描述一组不同的产品。名称和价格字段对所有产品都是通用的,而属性则可能因产品类型而异。由于我们不希望为每种产品类型创建不同的产品模型,因此我们使用 JSON 字段来存储这些任意的属性。

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=50)

    price = models.DecimalField(max_digits=5, decimal_places=2)

    properties = models.JSONField()

在典型的表单编辑视图中,我们会创建一个继承自 ModelForm 的表单,并使用其 Meta 类中的 model 属性引用此模型。然后,properties 字段将以非结构化的 JSON 形式显示,并在一个 <textarea ...></textarea> 中渲染。这绝对不是我们想要的!相反,我们使用替代类 EntangledModelForm 创建典型的 Django 表单。

from django.contrib.auth import get_user_model
from django.forms import fields, models
from entangled.forms import EntangledModelForm
from .models import Product

class ProductForm(EntangledModelForm):
    color = fields.RegexField(
        regex=r'^#[0-9a-f]{6}$',
    )

    size = fields.ChoiceField(
        choices=[('s', "small"), ('m', "medium"), ('l', "large"), ('xl', "extra large")],
    )

    tenant = models.ModelChoiceField(
        queryset=get_user_model().objects.filter(is_staff=True),
    )

    class Meta:
        model = Product
        entangled_fields = {'properties': ['color', 'size', 'tenant']}  # fields provided by this form
        untangled_fields = ['name', 'price']  # these fields are provided by the Product model

如果我们的表单继承自另一个 ModelForm,请将类声明重写为

class ProductForm(EntangledModelFormMixin, BaseProductForm):
    ...

此外,我们向 Meta 选项中添加一个名为 entangled_fields 的特殊字典。在这个字典中,键(此处为 'properties')指的是模型 Product 中的 JSON 字段。值(此处为 ['color', 'size', 'tenant'])是一个包含在我们的表单或其基类中声明的命名表单字段的列表。这允许我们将所有标准 Django 表单字段分配给在 Django 模型中声明的任意 JSON 字段。此外,我们甚至可以使用 ModelChoiceFieldModelMultipleChoiceField 来引用另一个模型对象,使用 通用关系

由于在此表单中我们还想访问 Django 模型中的非 JSON 字段,我们在 Meta 选项中添加了一个名为 untangled_fields 的列表。在此列表中(此处为 ['name', 'price']),我们引用了模型 Product 中的非 JSON 字段。从这两个可迭代对象中,entangled_fieldsuntangled_fields,父类 EntangledModelForm 然后构建所需的 Meta 选项 fields。或者,您可以使用 fields 来管理显示的 未缠绕的字段。如果未定义 fields,django-entangled 将根据内部创建的 untangled 选项创建它。

我们可以在任何 Django 表单视图中使用此表单。一个典型用例是内置的 Django ModelAdmin

from django.contrib import admin
from .models import Product
from .forms import ProductForm

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    form = ProductForm

由于此 ModelAdmin 类使用的表单 不能动态创建,我们必须使用 form 属性显式声明它。为了在 JSON 模型字段中存储任意内容,我们必须进行的唯一更改就是这一点。

嵌套数据结构

有时,将数据存储在字典的嵌套层次结构中,而不是在 JSON 字段的第一个级别上存储所有属性值对,可能是希望的。这在例如合并多个继承自 EntangledModelFormMixin 的表单时可能很有用。

假设我们有不同类型的产品,它们都共享相同的基本产品表单

from django.contrib.auth import get_user_model
from django.forms import models
from entangled.forms import EntangledModelFormMixin
from .models import Product

class BaseProductForm(EntangledModelFormMixin):
    tenant = models.ModelChoiceField(
        queryset=get_user_model().objects.filter(is_staff=True),
    )

    class Meta:
        model = Product
        entangled_fields = {'properties': ['tenant']}
        untangled_fields = ['name', 'price']

为了使基本产品针对,例如服装,我们通常从基表单继承并添加一些附加字段,这里为 colorsize

from django.forms import fields
from .forms import BaseProductForm
from .models import Product

class ClothingProductForm(BaseProductForm):
    color = fields.RegexField(
        regex=r'^#[0-9a-f]{6}$',
    )

    size = fields.ChoiceField(
        choices=[('s', "small"), ('m', "medium"), ('l', "large"), ('xl', "extra large")],
    )

    class Meta:
        model = Product
        entangled_fields = {'properties': ['color', 'size']}
        retangled_fields = {'color': 'variants.color', 'size': 'variants.size'}

通过添加现有字段名称的名称映射,我们可以将字段 colorsize 分组到 properties 字段中的子字典 variants 中。此类字段映射通过可选的 Meta 选项 retangled_fields 声明。在此字典中,所有条目都是可选的;如果字段名称缺失,它将映射到自身。

此映射表还可以用于将字段名称映射到结果JSON数据结构中的其他键。例如,将包含下划线的字段映射到包含破折号的字段名很有用。

注意事项

由于JSON的特性,基于字段内容进行索引以及构建过滤器或排序规则并不像基于标准模型字段那样简单。因此,如果主要关注存储数据而不是深入挖掘数据,则此方法最为合适。

外键以"fieldname": {"model": "appname.modelname", "pk": 1234}的形式存储在我们的JSON字段中,这意味着我们没有数据库约束。如果一个目标对象被删除,该外键将指向无意义的位置。因此,请始终记住,我们没有任何引用完整性,因此必须以防御性方式编写代码。

为项目做出贡献

  • 请在讨论板上提出问题。
  • 关于新特性的想法也应在该板上进行讨论。
  • 仅使用问题跟踪器来报告错误。
  • 除了非常小的修复(拼写错误等)之外,在没有问题的情况下不要打开pull请求。
  • 在编写代码之前,请将您的IDE配置为尊重项目的.editorconfig

Twitter Follow

项目详细信息


下载文件

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

源分布

django_entangled-0.6.tar.gz (12.0 kB 查看散列)

上传时间

构建分布

django_entangled-0.6-py3-none-any.whl (14.8 kB 查看散列)

上传时间 Python 3

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面