2

I need to display multiple models in django admin change list view. I want to use single search box to filter all of them at once. Is there an easy way to do it?

My idea was to inherit from admin site, add another view to it and iterate over models in modified change_list.html but i can't import models and ModelAdmins because i get django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet. error so i can't get the same context that django uses to render regular change_list.html.

What's the correct way to do it? Is there simpler approach?

subli
  • 63
  • 4

2 Answers2

1

As Ohad suggested, the most robust approach is probably to make formal relationships between the models from which you want the objects to display together. You have a couple of options here. Essentially you will want to make a master class and then subclass your models from it. This makes a lot of sense if your models are ontologically related to a parent concept. For example:

  • Publication
    • Book
    • Magazine issue

Books and magazines are both publications. They both share some fields, like title and publication date. But they differ in that a book usually has a single author and a magazine has volumes and issue dates. Django already provides a couple different approaches to subclassing using Model inheritance. However, after trying these myself I found that the django-polymorphic extension is way better. Here is a code example of a Django 3.0 app using django-polymorphic which has a Book model and a Magazine model with a single listing of all publications that shows all of the books and magazines in the system.

models.py

from django.db import models
from polymorphic.models import PolymorphicModel

class Publication(PolymorphicModel):
    title = models.CharField(max_length=256)
    publication_year = models.IntegerField()

class Book(Publication):
    author_first = models.CharField(max_length=256)
    author_last = models.CharField(max_length=256)

class Magazine(Publication):
    volume_number = models.IntegerField()
    issue_name = models.CharField(max_length=256)

admin.py

from django.contrib import admin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
from .models import Publication, Book, Magazine


class PublicationChildAdmin(PolymorphicChildModelAdmin):
    """ Base admin class for all child models """
    base_model = Publication  # Optional, explicitly set here.

@admin.register(Book)
class BookAdmin(PublicationChildAdmin):
    base_model = Book  # Explicitly set here!
#    show_in_index = True  # makes child model admin visible in main admin site
    list_display = ('title', 'publication_year', 'author_first', 'author_last')


@admin.register(Magazine)
class MagazineAdmin(PublicationChildAdmin):
    base_model = Magazine  # Explicitly set here!
#    show_in_index = True  # makes child model admin visible in main admin site
    list_display = ('title', 'publication_year', 'issue_name')


@admin.register(Publication)
class PublicationParentAdmin(PolymorphicParentModelAdmin):
    """ The parent model admin """
    base_model = Publication  # Optional, explicitly set here.
    child_models = (Book, Magazine)
    list_filter = (PolymorphicChildModelFilter,)  # This is optional.
    list_display = ('title', 'publication_year')

This will of course only display those fields that are common (in the Publication model). If you want to display fields that are particular to each model there are various tricks for this. Here's one quick way to do it:

admin.py

...

@admin.register(Publication)
class PublicationParentAdmin(PolymorphicParentModelAdmin):
    """ The parent model admin """
    base_model = Publication  # Optional, explicitly set here.
    child_models = (Book, Magazine)
    list_filter = (PolymorphicChildModelFilter,)  # This is optional.
    list_display = ('title', 'publication_year', 'issue', 'author')
    def author(self, obj):
        if obj.polymorphic_ctype.model == 'book':
            book = Book.objects.get(pk=obj.pk)
            return book.author_first + ' ' + book.author_last
        return None

    def issue(self, obj):
        if obj.polymorphic_ctype.model == 'magazine':
            return str(Magazine.objects.get(pk=obj.pk).issue_name)
        return None

Tada!

Publications listing showing Book and Magazine objects in one change list

Tristan
  • 1,730
  • 3
  • 20
  • 25
0

From the docs it seems that there is no easy solution.(if there is no relation between the models) https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields

So if the search is commonly used build a special model/models that combines the data that might be searched

Community
  • 1
  • 1
Ohad the Lad
  • 1,889
  • 1
  • 15
  • 24