2011-06-09 30 views
9

我正在用appengine试验django-nonrel并尝试使用djangotoolbox.fields.ListField来实现多对多关系。正如我在文档中看到的,ListField是您可以用来解决djamgo-nonrel不支持多对多关系的解决方法。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) 

我以为这会工作,但事实并非如此。当我进入管理界面时,我得到和以前一样的错误。任何人都可以告诉我在这里做错了什么......或者如果您有任何其他建议或成功的例子在管理界面中使用ListField,SetFielddjangotoolbox.fields,将非常感激。

回答

10

OK,这里是我做过什么让这一切工作...... 我会从头开始

这是什么我的模型看起来像

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 

这些类允许在admin中使用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,因为我不擅长它的工作原理,并且可能会导致效率低下的查询。

+0

Rman,我遇到了实现上述示例的问题,您可以在http://stackoverflow.com/questions/7782535/django-nonrel-with-mongdb-listfield发布您的输入 – bobsr 2011-10-16 05:26:44

0

这可能是不相关的,但对于管理界面,请确保您在设置django.contrib.admin上市后djangotoolbox .. INSTALLED_APPS

3

据我了解,你想有一个M2M在django-nonrel中的关系,这不是一个开箱即用的功能。对于初学者来说,如果你想快速的黑客,你可以去用这个简单的类,并使用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关系所做的工作:

假设您在两个类别SomeClass和AnotherClass之间分别具有M2M关系。你想为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 

这应该可以让你去的开始,我实现了这个功能正常的形式,对其进行调整对于管理面板应该不那么难。

+0

感谢您的代码示例避免这种使用自定义窗体类。它帮助我弄清楚如何达到我需要的。我现在可以用我自己的形式显示一个'ListField'作为'SelectMultiple'。我仍然需要做一些额外的工作才能在管理页面中工作 – Rman 2011-06-24 23:23:23

+0

您能否解释在管理页面中完成这项工作需要做哪些工作? – raymi 2011-06-27 07:22:42

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