Django Admin Form for Many to many relationship

20,113

Solution 1

I know that this is an older thread, but this was the first result that came up on google and I thought a better answer was necessary.

Via this django bug report I found the best way to have your ManyToManyField show up on both models:

class Test1(models.Model):
    tests2 = models.ManyToManyField('Test2', blank=True)

class Test2(models.Model):
    tests1 = models.ManyToManyField(Test1, through=Test1.tests2.through, blank=True)

I have tested it myself and was very pleased with the results.

Solution 2

The only built-in way is via an InlineModelAdmin, but you can use a custom ModelForm with your User ModelAdmin to create field for this purpose. See the code below for simplified setup (assumes users = ManyToManyField(related_name='domains')).

### yourapp/admin.py ###

from django import forms
from django.contrib import admin
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin.widgets import FilteredSelectMultiple

from .models import Domain

class DomainAdmin(admin.ModelAdmin):
    filter_horizonal = ('users',)

class UserAdminForm(forms.ModelForm):
    domains = forms.ModelMultipleChoiceField(
        queryset=Domain.objects.all(), 
        required=False,
        widget=FilteredSelectMultiple(
            verbose_name=_('Domains'),
            is_stacked=False
        )
    )

    class Meta:
        model = User

    def __init__(self, *args, **kwargs):
        super(UserAdminForm, self).__init__(*args, **kwargs)

        if self.instance:
          self.fields['domains'].initial = self.instance.domains.all()

    def save(self, commit=True):
        user = super(UserAdminForm, self).save(commit=False)

        user.domains = self.cleaned_data['domains']

        if commit:
            user.save()
            user.save_m2m()

        return user

class UserAdmin(admin.ModelAdmin):
    form = UserAdminForm

admin.site.register(Domain, DomainAdmin)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Solution 3

I think what you're looking for is InlineModelAdmin.

Share:
20,113
Admin
Author by

Admin

Updated on July 09, 2022

Comments

  • Admin
    Admin almost 2 years

    I have a many to many relationship between 2 tables Users an Domains. I have defined this relationship in the Domains class. So in the admin interface I see the Users when I am viewing Domains. But I do not see Domains when I am viewing Users. How can I achieve this.

  • Carl Meyer
    Carl Meyer about 15 years
    Probably not what the OP was hoping for (a way to use the regular ManyToMany filter selector from either side of the relation), but this is the solution I would use too.
  • Admin
    Admin about 15 years
    How do I refer to the relationship table (Domains_User in my case) I do not have an explicit intermediary table. In this case how do I use InlineModelAdmin for ManyToManyRelationship
  • alalonde
    alalonde over 12 years
    This didn't quite work for me when adding the 'user' (not a User in my case). I needed to check for self.instance.id in the __init__ method, and I needed to save() the new object before assigning domains to it.
  • Timo
    Timo about 10 years
    I get the error message "Test2" not defined which seems logic because Test2 is not defined when called. Your solution would be brillant if working though
  • Pentux
    Pentux over 4 years
    That's why he added the quotes in Test2, if you add the quotes in 'Test2' will work