使用Django模型表单编辑JSON字段
项目描述
django-entangled
使用标准Django表单编辑JSON模型字段。
使用场景
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 字段。此外,我们甚至可以使用 ModelChoiceField
或 ModelMultipleChoiceField
来引用另一个模型对象,使用 通用关系
由于在此表单中我们还想访问 Django 模型中的非 JSON 字段,我们在 Meta
选项中添加了一个名为 untangled_fields
的列表。在此列表中(此处为 ['name', 'price']
),我们引用了模型 Product
中的非 JSON 字段。从这两个可迭代对象中,entangled_fields
和 untangled_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']
为了使基本产品针对,例如服装,我们通常从基表单继承并添加一些附加字段,这里为 color
和 size
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'}
通过添加现有字段名称的名称映射,我们可以将字段 color
和 size
分组到 properties
字段中的子字典 variants
中。此类字段映射通过可选的 Meta
选项 retangled_fields
声明。在此字典中,所有条目都是可选的;如果字段名称缺失,它将映射到自身。
此映射表还可以用于将字段名称映射到结果JSON数据结构中的其他键。例如,将包含下划线的字段映射到包含破折号的字段名很有用。
注意事项
由于JSON的特性,基于字段内容进行索引以及构建过滤器或排序规则并不像基于标准模型字段那样简单。因此,如果主要关注存储数据而不是深入挖掘数据,则此方法最为合适。
外键以"fieldname": {"model": "appname.modelname", "pk": 1234}
的形式存储在我们的JSON字段中,这意味着我们没有数据库约束。如果一个目标对象被删除,该外键将指向无意义的位置。因此,请始终记住,我们没有任何引用完整性,因此必须以防御性方式编写代码。
为项目做出贡献
- 请在讨论板上提出问题。
- 关于新特性的想法也应在该板上进行讨论。
- 仅使用问题跟踪器来报告错误。
- 除了非常小的修复(拼写错误等)之外,在没有问题的情况下不要打开pull请求。
- 在编写代码之前,请将您的IDE配置为尊重项目的.editorconfig。
项目详细信息
下载文件
下载适合您平台文件。如果您不确定选择哪个,请了解有关安装包的更多信息。
源分布
构建分布
django_entangled-0.6.tar.gz的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | b521392b655b674fb38c9a93bbbca3ec9fcb6c07871f1a5d7d3b796e97a7e76b |
|
MD5 | b0d5bf1302a55ea42ca5b0addb044d18 |
|
BLAKE2b-256 | d1c777601875d12e66cce31f9b6a03b3f61d4d22a407619c8f313e29960e0f07 |
django_entangled-0.6-py3-none-any.whl的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | 91c562fb8ad9db0ed375069ae8b81e569913063044d962eaf861b1166d54402b |
|
MD5 | 84f38f06711d2c5a8a9fd0152e4d7cfe |
|
BLAKE2b-256 | 087cd659a78b8e8a97c058d5ebb713a9dd2839f510114209ba766ebe43302c99 |