Django-nonrel中的ListField表单字段

9
我正在尝试在Appengine上使用django-nonrel,并尝试使用djangotoolbox.fields.ListField来实现多对多关系。根据文档,ListField是可以用来解决django-nonrel不支持多对多关系的问题。
以下是我的模型摘录内容:
class MyClass(models.Model):
    field = ListField(models.ForeignKey(AnotherClass))

如果我理解正确,我正在创建另一个类的外键列表,以显示与另一个类的多个实例之间的关系。

使用这种方法一切都正常...没有异常。我可以在代码和视图中创建'MyClass'对象。但是,当我尝试使用管理界面时,我会收到以下错误:

No form field implemented for <class 'djangotoolbox.fields.ListField'>

我想尝试一些之前没有做过的事情,创建自己的字段。实际上,在管理界面中编辑MyClass实例的表格也是我自己创建的。下面是我的操作步骤:

class MyClassForm(ModelForm):
    field = fields.MultipleChoiceField(choices=AnotherClass.objects.all(), widget=FilteredSelectMultiple("verbose_name", is_stacked=False))
    class Meta:
        model = MyClass

然后我将MyClassForm作为要在管理界面中使用的表单进行传递。

class MyClassAdmin(admin.ModelAdmin):
    form = MyClassForm

admin.site.register(MyClass, MyClassAdmin)

我以为这样会有效果,但事实并非如此。当我进入管理员界面时,出现了与之前相同的错误。有人能告诉我在这里做错了什么吗...或者如果您对在管理员界面中使用 djangotoolbox.fields 中的 ListFieldSetField 等有任何其他建议或成功经验,那将非常感激。

4个回答

11

好的,这里是我所做的一切以使其正常工作... 我将从头开始

这就是我的模型长什么样子

class MyClass(models.Model):
    field = ListField(models.ForeignKey(AnotherClass))

我想使用管理界面创建/编辑该模型的实例,并使用多选小部件来处理列表字段。因此,我创建了以下自定义类:

class ModelListField(ListField):
    def formfield(self, **kwargs):
        return FormListField(**kwargs)

class ListFieldWidget(SelectMultiple):
    pass

class FormListField(MultipleChoiceField):
    """
    This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
    """
    widget = ListFieldWidget

    def clean(self, value):
        #TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
        return value

这些类允许在管理界面中使用listfield。然后我创建了一个表单,在管理站点上使用。

class MyClassForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(MyClasstForm,self).__init__(*args, **kwargs)
        self.fields['field'].widget.choices = [(i.pk, i) for i in AnotherClass.objects.all()]
        if self.instance.pk:
            self.fields['field'].initial = self.instance.field

    class Meta:
        model = MyClass

在完成这些步骤后,我创建了一个管理员模型并将其注册到管理员站点

class MyClassAdmin(admin.ModelAdmin):
    form = MyClassForm

    def __init__(self, model, admin_site):
        super(MyClassAdmin,self).__init__(model, admin_site)

admin.site.register(MyClass, MyClassAdmin)

这段代码在我的程序中已经正常运行。请注意,这种方法可能并不适用于google_appengine,因为我不太熟悉它的工作方式,可能会导致查询效率低下等问题。


Rman,我在实现上述示例时遇到了问题,你能否在http://stackoverflow.com/questions/7782535/django-nonrel-with-mongdb-listfield上发布你的输入? - bobsr

3
据我所知,您正在尝试在django-nonrel中建立M2M关系,这不是一项开箱即用的功能。首先,如果您想要一个快速的解决方案,您可以使用这个简单的类并使用CharField手动输入外键:
class ListFormField(forms.Field):
    """ A form field for being able to display a djangotoolbox.fields.ListField. """

    widget = ListWidget

    def clean(self, value):
        return [v.strip() for v in value.split(',') if len(v.strip()) > 0]

但是,如果您想从通常需要使用ModelMultipleChoiceField的模型列表中进行多个选择,则在django-nonrel中也无法使用该功能。以下是我使用MultipleSelectField来模拟M2M关系的方法:

假设您有两个类之间的M2M关系,SomeClass和AnotherClass分别。您希望在SomeClass表单上选择关系。此外,我假设您希望将引用保存为SomeClass中的ListField。(当然,如此处所述创建M2M关系是必要的,以防止在使用App Engine时出现索引爆炸)。

因此,您的模型如下:

class SomeClass(models.Model):
    another_class_ids = ListField(models.PositiveIntegerField(), null=True, blank=True)
    #fields go here

class AnotherClass(models.Model):
    #fields go here

在你的表单中:

class SomeClassForm(forms.ModelForm):

    #Empty field, will be populated after form is initialized
    #Otherwise selection list is not refreshed after new entities are created.
    another_class = forms.MultipleChoiceField(required=False)

def __init__(self, *args, **kwargs):
    super(SomeClassForm,self).__init__(*args, **kwargs)
    self.fields['another_class'].choices = [(item.pk,item) for item in AnotherClass.objects.all()]

    if self.instance.pk: #If class is saved, highlight the instances that are related
        self.fields['another_class'].initial = self.instance.another_class_ids

def save(self, *args, **kwargs):  
    self.instance.another_class_ids = self.cleaned_data['another_class']         
    return super(SomeClassForm, self).save()

class Meta:
    model = SomeClass

希望这能让你开始动手,我已经将这个功能实现于普通表单中,调整为管理面板也不难。


感谢提供示例代码。它帮助我找到了我需要的东西。现在我可以在自己的表单中将ListField显示为SelectMultiple。但我仍需做一些额外的工作,以使其在管理页面中正常工作。 - Rman
你能解释一下需要做哪些工作才能在管理页面中使其正常工作吗? - raymi

0

这可能与管理员界面无关,但请确保在设置中将 djangotoolbox 列在 django.contrib.admin 之后.. INSTALLED_APPS


0

您可以通过查询模型对象来避免使用自定义表单类进行此类用途

class ModelListField(ListField):
  def __init__(self, embedded_model=None, *args, **kwargs):
  super(ModelListField, self).__init__(*args, **kwargs)
  self._model = embedded_model.embedded_model

  def formfield(self, **kwargs):
    return FormListField(model=self._model, **kwargs)

class ListFieldWidget(SelectMultiple):
  pass

class FormListField(MultipleChoiceField):
  widget = ListFieldWidget

  def __init__(self, model=None, *args, **kwargs):
    self._model = model
    super(FormListField, self).__init__(*args, **kwargs)
    self.widget.choices = [(unicode(i.pk), i) for i in self._model.objects.all()]

  def to_python(self, value):
    return [self._model.objects.get(pk=key) for key in value]

  def clean(self, value):
    return value

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接