Search code examples
djangodjango-adminmany-to-many

Present ManyToMany in Django's Admin as horizontal-filter using related_name


I have the following:

class User(models.Model)
    blablabla

class Product(models.Model)
    authorized_users = models.ManyToManyField(
        User,
        related_name='shared_products',
    )

I already configured the admin of Product to show authorized_users as an horizontal filter, in order to select all the users that can edit a product.

class ProductAdmin(admin.ModelAdmin):
    filter_horizontal = (
        'authorized_users',
    )

admin.site.register(Product, ProductAdmin)

The problem is that I want to do the same in the admin of User, meaning that I want to have an horizontal filter for shared_products, in order to select the products that this user is able to edit. I have tried the following which clearly doesn't work:

class UserAdmin(admin.ModelAdmin):
    filter_horizontal = (
        'authorized_users',
    )

admin.site.register(User, UserAdmin)

Other answers I have found recomend the usage of Inlines but as I have seen they are used to edit the model instance on the other end, which is not I what I want to do.

Does someone have an idea of how to achieve this?


Solution

  • class UserAdminForm(forms.ModelForm):
      products = forms.ModelMultipleChoiceField(
        queryset=Product.objects.all(), 
        required=False,
        widget=FilteredSelectMultiple(
          verbose_name=_('Products'),
          is_stacked=False
        )
      )
    
      class Meta:
        model = User
    
      def __init__(self, *args, **kwargs):
        super(UserAdminForm, self).__init__(*args, **kwargs)
    
        if self.instance and self.instance.pk:
          self.fields['products'].initial = self.instance.products.all()
    
      def save(self, commit=True):
        user = super(UserAdminForm, self).save(commit=False)
    
        if commit:
          user.save()
    
        if user.pk:
          user.products = self.cleaned_data['products']
          self.save_m2m()
    
        return user
    
    class UserAdmin(admin.ModelAdmin):
      form = UserAdminForm