我正在实施基于Django框架的'收集管理系统'(目前,稳定的1.4.3)。
我正在设想ORM的良好关系性质,以强制一些讨厌的“OO方面”,我试图提取一个最小的例子。 (免责声明:完整的功能描述将是另一个漫长而无聊的读物,所以除非结果是设计讨论,否则我不会产生它。)
让我们假设我们使用Django模型松散地模拟OO。我们有 Release 模型(=一个类),它可以由不同的属性(=类的数据成员)组成。然后可以针对相同的发布实例化不同的实例(=对象)。每个实例都可以存储其相关发布中存在的属性的全部/部分/全部值。
这将给我们以下模型:
#An attribute is a name, that can be given a value at instance level.
class Attribute(models.Model):
name = models.CharField(max_length=60)
#A release can be compared to a class in OO :
# it's a data structure description (the attributes' list).
class Release(models.Model):
name = models.CharField(max_length=60)
#A release is caracterized by a list of attributes
# the same attribute can be present in several releases
attributes = models.ManyToManyField(Attribute, blank=True, null=True)
#An instance entry can be compared to an object in OO :
# it instantiates a release
# it can hold its own value for each of the attributes in this release.
#Nb : Attributes are all optional.
class Instance(models.Model):
#the instantiated release
release = models.ForeignKey(Release)
#Store the actual attribute-value pairs for the different instances.
class InstanceAttribute(models.Model):
instance = models.ForeignKey(Instance)
attribute = models.ForeignKey(Attribute)
value = models.CharField(max_length=60)
使用强大的Django管理员来实现这种方法会很棒。
开箱即用的所有内容都可以添加版本,并使用属性进行组合。
实例添加视图越来越复杂。 (相关的发布 ID可以通过GET转发到以下格式的网址: instance / add /?release = x )。加载此视图时,我们需要使用以下内容提出 InstanceAttribute 的InlineFormset:
我们可以通过覆盖ModelAdmin.get_formsets()
来解决#1,以便将 extra 参数设置为所需的数字,从而返回inlineformset_factory
。
仔细看看add_view source,我找不到实施#2的好方法......
答案 0 :(得分:0)
我正在用一个工作的解决方案回答我自己的问题,但至少可以说不满意。我认为通过展示我想要达到的目标,它也可以帮助读者理解上述问题。
所以(坏)的想法是将视图使用的 InstanceAttribute 表单子类化。它允许覆盖表单__init__
,同时引入具有 InstanceAttributeForm 静态数据成员的全局状态。
__init__
然后使用这些全局变量将属性绑定到由formset构造的每个表单。代码
class InstanceAttributeForm(forms.ModelForm):
#form_id count the initializations (to know which form is currently constructed by the formset)
form_id = 0
total = 0
#the list of attributes in the instantiated release
#it should be populated before the formset start the constructor calls
attributes = []
def __init__(self, *args, **kwargs):
super(InstanceAttributeForm, self).__init__(*args, **kwargs)
#"deferred template rendering' is trying to construct 3 additional forms
#bonus point if you can comment on the reason for this behavior ; )
if InstanceAttributeForm.form_id==InstanceAttributeForm.total:
return
attribute = InstanceAttributeForm.attributes[InstanceAttributeForm.form_id]
self.initial = {'attribute' : attribute}
self.fields['attribute'].queryset = Attribute.objects.filter(id=attribute.id)
InstanceAttributeForm.form_id += 1
我们必须在实际输入表单的构造函数之前初始化这些全局变量,并将 InstanceAttributeForm 挂钩到formset工厂。为此,我们可以自定义实例 ModelAdmin:
class InstanceAdmin(admin.ModelAdmin):
#Responsible for re-initializing the global state each time before calling the super add_view
def add_view(self, request, form_url='', extra_context=None):
initialize_globalstate(request)
return super(InstanceAdmin, self).add_view(request, form_url, extra_context)
#Return a formset factory specifying the exact number of required forms
# and hooking our specialised ModelForm class.
def get_formsets(self, request, obj=None):
yield inlineformset_factory(
Instance,
InstanceAttribute,
form=InstanceAttributeForm,
can_delete=False,
extra=InstanceAttributeForm.total)
为了完整性,全局状态初始化过程(在上面重写的add_view()
中调用):
def initialize_globalstate(request):
instantiated_release = Release.objects.get(id=request.GET['release'])
InstanceAttributeForm.form_id = 0
InstanceAttributeForm.attributes = instantiated_release.attributes.all()
InstanceAttributeForm.total = len(InstanceAttributeForm.attributes)