Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Admin list_display add column [count records of related ForeignKey]

I am new to Python and Django.

I was trying to count the foreign key items/records of a model and display it as a column in the Admin list_display.

So I have a model "Author", as Foreign key to model "Book".

Here's the models.py:

class Author(models.Model):

    name = models.CharField(
        max_length=100,
    )

    class Meta:
        ordering = ['-name ']
        verbose_name = _('Author')
        verbose_name_plural = _('Authors')

    def __str__(self):
        return self.author

    @models.permalink
    def get_absolute_url(self):
        return reverse('author_detail', kwargs={'pk': self.pk})

class Book(models.Model):
    author = models.ForeignKey(
        'Author',
        related_name='menu_items', 
        verbose_name=_('author'),
    )

    bookname= models.CharField(
        max_length=100,
    )

    publisher= models.CharField(
        max_length=100,
    )
    class Meta:
        ordering = ['-bookname']
        verbose_name = _('Book')
        verbose_name_plural = _('Books')

    def __str__(self):
        return self.bookname

    @models.permalink
    def get_absolute_url(self):
        return ('book_detail', [self.pk])

Here's the admin.py

from django.contrib import admin
from .models import Author, Book

class BookInline(admin.TabularInline):
    model = Book
    extra = 1

@admin.register(Author)
class LibraryAdmin(admin.ModelAdmin):
    def NumberOfBooks(self, obj):
        #I guess sth is wrong here when trying to count the book_set
        return obj.book_set.count()

    NumberOfBooks.short_description = "Books Count"
    list_display = ['bookname', 'publisher', 'NumberOfBooks']
    inlines = [BookInline]

I want to count how many books each Author wrote, and display like this on the second level of the admin page Home>Authors>Author:

 Author | Books Count
Someone1  NumberOfBooks
Someone2  NumberOfBooks
Someone3  NumberOfBooks
Someone4  NumberOfBooks

However I get the error:

'Author' object has no attribute 'book_set'

1. did I do the obj.book_set.count() wrong? I thought obj.book_set would refer to "book" under the same "author"(obj), sounds like I misunderstood it but I am not sure how to achieve what I intended to.

2. Instead, if I wanted to add a field to 'Authors' which would auto-count number of "book" related to each Author, should this be a better approach and so how to do it?

like image 621
adrian li Avatar asked Dec 18 '22 11:12

adrian li


1 Answers

There should be a list with all the available fields on the error page and you should find a field called menu_items (that's what you put into related_namein your Book model).

But that might be not the best option. You should be able to override get_queryset() method on your LibraryAdmin(admin.ModelAdmin) class like this:

...
from django.db.models import Count
...

@admin.register(Loan)
class LibraryAdmin(admin.ModelAdmin):

    def get_queryset(self, request):
        qs = super(LibraryAdmin, self).get_queryset(request)
        return qs.annotate(books_count=Count('menu_items'))

    def books_count(self, inst):
        return inst.books_count

    list_display = ['bookname', 'publisher', 'books_count']
    inlines = [BookInline]

This way counting will be done by your DB.

like image 94
twil Avatar answered Jan 30 '23 23:01

twil