Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Editing both sides of M2M in Admin Page

First I'll lay out what I'm trying to achieve in case there's a different way to go about it!

I want to be able to edit both sides of an M2M relationship (preferably on the admin page although if needs be it could be on a normal page) using any of the multi select interfaces.

The problem obviously comes with the reverse side, as the main side (where the relationship is defined) works just fine automagically.

I have tried some of the advice here to get an inline to appear and that works but its not a very nice interface.

The advice I got on the django mailing list was to use a custom ModelForm. I've got as far as getting a multiselect box to appear but it doesnt seem to be "connected" to anything as it does not start with anything selected and does not save any changes that are made.

Here's the appropriate snippets of code:

#models.py
class Tag(models.Model):
    name = models.CharField(max_length=200)

class Project(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    tags = models.ManyToManyField(Tag, related_name='projects')

#admin.py
class TagForm(ModelForm):
    fields = ('name', 'projects')
    projects = ModelMultipleChoiceField(Project.objects.all(), widget=SelectMultiple())
    class Meta:
        model = Tag

class TagAdmin(admin.ModelAdmin):
    fields = ('name', 'projects')
    form = TagForm

Any help would be much appreciated, either getting the code above to work or by providing a better way to do it!

DavidM

like image 792
DavidM Avatar asked Nov 09 '09 10:11

DavidM


1 Answers

The reason why nothing happens automatically is that the "projects" field is not a part of the Tag model. Which means you have to do all the work yourself. Something like (in TagForm):

def __init__(self, *args, **kwargs):
    super(TagForm, self).__init__(*args, **kwargs)
    if 'instance' in kwargs:
        self.fields['projects'].initial = self.instance.project_set.all()

def save(self, *args, **kwargs):
    super(TagForm, self).save(*args, **kwargs)
    self.instance.project_set.clear()
    for project in self.cleaned_data['projects']:
        self.instance.project_set.add(project)

Note that the code is untested so you might need to tweek it some to get it to work.

like image 136
oggy Avatar answered Sep 18 '22 15:09

oggy