这是关于django的问题。 我有一个模特说“汽车”。这将有一些基本字段,如“颜色”,“车辆所有者名称”,“车辆成本”。
我想提供一个表单,用户可以根据他添加的汽车添加额外的字段。例如,如果用户正在添加“Car”,则他将在运行时动态地在表单中添加额外字段,例如“Car Milage”,“Cal Manufacturer”。 假设用户想要添加“卡车”,他将添加“可以携带的负载”,“允许”等。
我如何在django中实现这一目标?
这里有两个问题:
答案 0 :(得分:26)
有几种方法:
听起来你想要最后一个,但我不确定它对你来说是最好的。 Django很容易更改/更新,如果系统管理员想要额外的字段,只需为它们添加它们并使用south进行迁移。我不喜欢通用的键/值数据库模式,像Django这样强大的框架的重点在于,您可以轻松编写和重写自定义模式,而无需采用通用方法。
如果您必须允许网站用户/管理员直接定义他们的数据,我相信其他人会告诉您如何进行上述前两种方法。第三种方法是你要求的,而且有点疯狂,我会告诉你该怎么做。我不建议在几乎所有情况下都使用它,但有时它是合适的。
动态模型
一旦你知道该怎么做,这是相对简单的。你需要:
<强> 1。存储模型定义
这取决于你。我想你会有一个模型CustomCarModel
和CustomField
让用户/管理员定义和存储你想要的字段的名称和类型。您不必直接镜像Django字段,您可以创建自己的类型,用户可以更好地理解。
使用带有内联formset的forms.ModelForm
让用户构建自定义类。
<强> 2。抽象模型
同样,这很简单,只需使用所有动态模型的公共字段/方法创建基本模型。使这个模型抽象化。
第3。建立动态模型
定义一个获取所需信息的函数(可能是来自#1的类的实例)并生成一个模型类。这是一个基本的例子:
from django.db.models.loading import cache
from django.db import models
def get_custom_car_model(car_model_definition):
""" Create a custom (dynamic) model class based on the given definition.
"""
# What's the name of your app?
_app_label = 'myapp'
# you need to come up with a unique table name
_db_table = 'dynamic_car_%d' % car_model_definition.pk
# you need to come up with a unique model name (used in model caching)
_model_name = "DynamicCar%d" % car_model_definition.pk
# Remove any exist model definition from Django's cache
try:
del cache.app_models[_app_label][_model_name.lower()]
except KeyError:
pass
# We'll build the class attributes here
attrs = {}
# Store a link to the definition for convenience
attrs['car_model_definition'] = car_model_definition
# Create the relevant meta information
class Meta:
app_label = _app_label
db_table = _db_table
managed = False
verbose_name = 'Dynamic Car %s' % car_model_definition
verbose_name_plural = 'Dynamic Cars for %s' % car_model_definition
ordering = ('my_field',)
attrs['__module__'] = 'path.to.your.apps.module'
attrs['Meta'] = Meta
# All of that was just getting the class ready, here is the magic
# Build your model by adding django database Field subclasses to the attrs dict
# What this looks like depends on how you store the users's definitions
# For now, I'll just make them all CharFields
for field in car_model_definition.fields.all():
attrs[field.name] = models.CharField(max_length=50, db_index=True)
# Create the new model class
model_class = type(_model_name, (CustomCarModelBase,), attrs)
return model_class
<强> 4。用于更新数据库表的代码
上面的代码将为您生成动态模型,但不会创建数据库表。我建议使用South进行表操作。以下是一些功能,您可以将这些功能连接到保存前/后保存信号:
import logging
from south.db import db
from django.db import connection
def create_db_table(model_class):
""" Takes a Django model class and create a database table, if necessary.
"""
table_name = model_class._meta.db_table
if (connection.introspection.table_name_converter(table_name)
not in connection.introspection.table_names()):
fields = [(f.name, f) for f in model_class._meta.fields]
db.create_table(table_name, fields)
logging.debug("Creating table '%s'" % table_name)
def add_necessary_db_columns(model_class):
""" Creates new table or relevant columns as necessary based on the model_class.
No columns or data are renamed or removed.
XXX: May need tweaking if db_column != field.name
"""
# Create table if missing
create_db_table(model_class)
# Add field columns if missing
table_name = model_class._meta.db_table
fields = [(f.column, f) for f in model_class._meta.fields]
db_column_names = [row[0] for row in connection.introspection.get_table_description(connection.cursor(), table_name)]
for column_name, field in fields:
if column_name not in db_column_names:
logging.debug("Adding field '%s' to table '%s'" % (column_name, table_name))
db.add_column(table_name, column_name, field)
你有它!您可以调用get_custom_car_model()
来提供django模型,您可以使用它来执行常规的django查询:
CarModel = get_custom_car_model(my_definition)
CarModel.objects.all()
<强>问题强>
get_custom_car_model
信号为每个定义实例运行class_prepared
。ForeignKeys
/ ManyToManyFields
可能无效(我还没试过)<强>概述强>
如果您对增加的并发症和问题感到满意,请尽情享受吧!由于Django和Python的灵活性,它正在运行,它完全按预期工作。您可以将模型提供给Django的ModelForm
,让用户编辑他们的实例,并直接使用数据库的字段执行查询。如果上面有任何你不理解的内容,你可能最好不采用这种方法(我故意没有解释一些初学者的概念)。保持简单!
我真的不认为很多人需要这个,但我自己使用它,我们在表中有很多数据,真的,真的需要让用户自定义列,这些列很少改变。
答案 1 :(得分:8)
<强>数据库强>
再次考虑您的数据库设计。
你应该考虑一下你想要代表的对象在现实世界中是如何相互关联的,然后尽可能多地概括这些关系,(所以不要说每辆卡车都有许可证,你假设每辆车都有一个属性,可以是许可证,装载金额等等。
所以试试吧:
如果您说您有车辆且每辆车都有许多用户指定的属性,请考虑以下型号:
class Attribute(models.Model):
type = models.CharField()
value = models.CharField()
class Vehicle(models.Model):
attribute = models.ManyToMany(Attribute)
如前所述,这是一个一般性的想法,使您可以根据需要为每辆车添加尽可能多的属性。
如果您希望用户可以使用特定的一组属性,则可以在Attribute.type
字段中使用choices
。
ATTRIBUTE_CHOICES = (
(1, 'Permit'),
(2, 'Manufacturer'),
)
class Attribute(models.Model):
type = models.CharField(max_length=1, choices=ATTRIBUTE_CHOICES)
value = models.CharField()
现在,也许您希望每辆车排序都拥有自己的可用属性集。这可以通过添加另一个模型并将Vehicle
和Attribute
模型中的外键关系设置为它来完成。
class VehicleType(models.Model):
name = models.CharField()
class Attribute(models.Model):
vehicle_type = models.ForeigngKey(VehicleType)
type = models.CharField()
value = models.CharField()
class Vehicle(models.Model):
vehicle_type = models.ForeigngKey(VehicleType)
attribute = models.ManyToMany(Attribute)
通过这种方式,您可以清楚地了解每个属性与某些车辆的关系。
<强>表单强>
基本上,使用此数据库设计,您需要两种表单来将对象添加到数据库中。具体而言,车辆为model form,属性为model formset。您可以使用jQuery在Attribute
formset上动态添加更多项目。
<强> 注意 强>
您还可以将Attribute
类分隔为AttributeType
和AttributeValue
,这样您就不会在数据库中存储冗余属性类型,或者您希望限制用户的属性选择但是能够使用Django管理站点添加更多类型。
要完全酷,您可以在表单上使用自动填充功能向用户建议现有的属性类型。
提示:详细了解database normalization。
其他解决方案
之前的回答中所述另一方面,您可以为每种车型硬编码模型,以便每种车型由基础车辆的子类表示,每个子类可以有自己的特定属性,但解决方案不是很灵活(如果你需要灵活性)。
您还可以在一个数据库字段中保留其他对象属性的JSON表示,但我不确定在查询属性时这会有用。
答案 2 :(得分:2)
这是我在django shell中的简单测试 - 我刚输入,看起来工作正常 -
In [25]: attributes = {
"__module__": "lekhoni.models",
"name": models.CharField(max_length=100),
"address": models.CharField(max_length=100),
}
In [26]: Person = type('Person', (models.Model,), attributes)
In [27]: Person
Out[27]: class 'lekhoni.models.Person'
In [28]: p1= Person()
In [29]: p1.name= 'manir'
In [30]: p1.save()
In [31]: Person.objects.a
Person.objects.aggregate Person.objects.all Person.objects.annotate
In [32]: Person.objects.all()
Out[33]: [Person: Person object]
看起来非常简单 - 不确定为什么它不应该被认为是一个选项 - 反射是非常常见的其他语言,如C#或Java-无论如何我是django事物的新手 -
答案 3 :(得分:1)
您是在前端界面还是在Django管理员中讨论过?
如果没有大量的工作,你就无法动态创建真实的领域。 Django中的每个模型和字段在数据库中都有一个关联的表和列。要添加新字段,通常需要原始sql或使用South的迁移。
从前端界面,您可以创建伪字段,并将它们以json格式存储在单个模型字段中。
例如,在模型中创建other_data文本字段。然后允许用户创建字段,并将其存储为{'userfield':'userdata','mileage':54}
但我想如果你使用像车辆这样的有限类,你会创建一个具有基本车辆特征的基础模型,然后为每种车型创建继承基础模型的模型。
class base_vehicle(models.Model):
color = models.CharField()
owner_name = models.CharField()
cost = models.DecimalField()
class car(base_vehicle):
mileage = models.IntegerField(default=0)
等