14

Say I have the following models that have a many-to-many relationship:

models.py:

class Foo(models.Model):
    name = models.TextField()

class Bar(models.Model):
    name = models.TextField()
    foos = models.ManyToManyField(Foo, related_name='bars')

And then having defined them in admin in the following way:

admin.py

@admin.register(Foo)
class FooAdmin(admin.ModelAdmin):
    pass

@admin.register(Bar)
class BarAdmin(admin.ModelAdmin):
    pass

In Django admin, when browsing Bar instances, I can see the Foo instances Bar is associated with and can modify them from there.

However, no such luck with Foo, I can't see the Bar instances that every Foo object is associated with.

Can Django define automatic handling for this or would I need to roll my own methond?

I'm using Python 3.6.1 and Django 1.11.

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
Nobilis
  • 7,310
  • 1
  • 33
  • 67
  • In your `ManyToManyField` you're referencing `A`. Is that right? I'd expect: `ManyToManyField(Foo, related_name='bars')`. – cezar May 10 '17 at 13:44
  • @cezar, yes, sorry `A` was supposed to be `Foo`, will correct – Nobilis May 10 '17 at 14:08
  • linking [this question](https://stackoverflow.com/a/61566930/6359659) here. They are related and solved my problem as a couple. – awadhesh14 Apr 18 '22 at 13:17

4 Answers4

24

You could define a custom InlineModelAdmin like so:

class BarInline(admin.TabularInline):
    model = Bar.foos.through

and use it in your FooAdmin:

class FooAdmin(admin.ModelAdmin):
"""Foo admin."""
    model = Foo
    inlines = [
        BarInline,
    ]

Have a look at this part of the django documentation.

Hopiu
  • 446
  • 3
  • 8
4

You can define custom fields for list_display like this:

@admin.register(Foo)
class FooAdmin(admin.ModelAdmin):
    """Foo admin."""

    list_display = ('name', 'get_bars')
    search_fields = ('name',)

    def get_bars(self, obj):
        return obj.bars.all()

This is a very simple example, but I hope it can help you as a starting point.

EDIT:

You can display the associated objects in the edit form as readonly:

readonly_fields = ('get_bars',)
fields = ('name', 'get_bars')
cezar
  • 11,616
  • 6
  • 48
  • 84
  • Thank you, is this just for listing? Would I need to roll my own logic to support editing? Sorry, not very familiar with Django. – Nobilis May 10 '17 at 14:00
  • Yes, this particular example is for listing. Now I realize, you need something for the edit form, i.e. `fields`. – cezar May 10 '17 at 14:03
  • I think that'll do for now, I'll add that and be on the lookout for a way to support editing. Just one question, why doesn't Django provide a bi-directional support for editing? I'm not familiar with what happens under the hood. – Nobilis May 10 '17 at 14:09
  • According to the documentation, the `fields` i.e. the admin form can display editable fields only for the fields defined on the model. In your case `foos` is defined on `Bar` and will be displayed in its admin form, but `bars` is a related name and not defined as a field on `Foo`, thus not displaying in Foo's admin form. I don't know the exact mechanism behind. – cezar May 10 '17 at 14:27
0

There is a module called django-admin-extend which provides a generic mechanism to define "Bidirectional many to many fields". I'm not sure if it still works, because the last contribution is two years old, bit it should be worth giving it a try.

Philipp Zedler
  • 1,660
  • 1
  • 17
  • 36
0

This code below can display both Foo and Bar models in many-to-many relationship on the same page in Django Admin:

class BarInline(admin.TabularInline):
    model = Bar.foos.through

@admin.register(Foo)
class FooAdmin(admin.ModelAdmin):
    inlines = (BarInline,)

Be careful, if using model = Bar instead of model = Bar.foos.through in many-to-many relationship as shown below:

class BarInline(admin.TabularInline):
    # model = Bar.foos.through
    model = Bar # This will get error

@admin.register(Foo)
class FooAdmin(admin.ModelAdmin):
    inlines = (BarInline,)

You will get the error below:

ERRORS: <class 'xxx.admin.BarInline'>: (admin.E202) 'xxx.Bar' has no ForeignKey to 'xxx.Foo'.

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129