【问题标题】:Django-nonrel form field for ListFieldListField 的 Django-nonrel 表单字段
【发布时间】:2011-07-24 05:30:08
【问题描述】:

我正在 appengine 上尝试使用 django-nonrel 并尝试使用 djangotoolbox.fields.ListField 来实现多对多关系。正如我在文档中所读到的,ListField 可以用来为不支持多对多关系的 djamgo-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 等,我们将不胜感激。

【问题讨论】:

    标签: google-app-engine django-admin django-nonrel listfield


    【解决方案1】:

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

    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
    

    【讨论】:

      【解决方案2】:

      据我了解,您正试图在 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 关系所做的工作:

      假设您在 2 个类之间存在 M2M 关系,分别是 SomeClass 和 AnotherClass。您想在 SomeClass 表单上选择关系。此外,我假设您希望将引用保存为 SomeClass 中的 ListField。 (当然,您希望创建 M2M 关系,正如 here 所解释的那样,以防止在使用 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。我仍然需要做一些额外的工作才能在管理页面中使用它
      • 您能解释一下需要做哪些工作才能在管理页面中进行这项工作吗?
      【解决方案3】:

      好的,这就是我为使这一切正常工作所做的... 我会从头开始

      这就是我的模型的样子

      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
      

      这些类允许在管理员中使用列表字段。然后我创建了一个在管理站点中使用的表单

      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,因为我不太熟悉它的工作原理,并且可能会产生低效的查询。

      【讨论】:

      【解决方案4】:

      这可能无关,但对于管理界面,请确保您在设置中的 django.contrib.admin 之后列出了 djangotoolbox.. INSTALLED_APPS

      【讨论】:

        猜你喜欢
        • 2013-09-11
        • 2011-04-22
        • 1970-01-01
        • 1970-01-01
        • 2011-12-08
        • 1970-01-01
        • 2012-02-10
        • 2012-12-10
        相关资源
        最近更新 更多