google-app-enginedjango-admindjango-nonrellistfield

Django-nonrel form field for ListField


I'm experimenting with django-nonrel on appengine and trying to use a djangotoolbox.fields.ListField to implement a many-to-many relation. As I read in the documentation a ListField is something that you can use to make a workaround for djamgo-nonrel not supporting many-to-many relations.

This is an excerpt from my model:

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

So if I am getting this right I am creating a list of foreign keys to another class to show a relationship with multiple instances of another class

With this approach everything works fine ... No Exceptions. I can create `MyClass' objects in code and views. But when I try to use the admin interface I get the following error

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

So I though I would try something that I haven't done before. Create my own field. Well actually my own form for editing MyClass instances in the admin interface. Here is what I did:

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

then I pass MyClassForm as the form to use to the admin interface

class MyClassAdmin(admin.ModelAdmin):
    form = MyClassForm

admin.site.register(MyClass, MyClassAdmin)

I though that this would work but It doesn't. When I go to the admin interface I get the same error as before. Can anyone tell what I am doing wrong here ... or if you have any other suggestions or success stories of using the ListField, SetField, etc. from djangotoolbox.fields in the admin interface it would be very much appreciated.


Solution

  • OK, here is what I did to get this all working ... I'll start from the beginning

    This is what what my model looked like

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

    I wanted to be able to use the admin interface to create/edit instances of this model using a multiple select widget for the list field. Therefore, I created some custom classes as follows

    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
    

    These classes allow the listfield to be used in the admin. Then I created a form to use in the admin site

    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
    

    After having done this I created a admin model and registered it with the admin site

    class MyClassAdmin(admin.ModelAdmin):
        form = MyClassForm
    
        def __init__(self, model, admin_site):
            super(MyClassAdmin,self).__init__(model, admin_site)
    
    admin.site.register(MyClass, MyClassAdmin)
    

    This is now working in my code. Keep in mind that this approach might not at all be well suited for google_appengine as I am not very adept at how it works and it might create inefficient queries an such.