Hoe voeg je een sorteerbare telkolom toe aan de Django-beheerder van een model met een veel-op-een-relatie?

Stel dat ik een boekmodel heb met een refererende sleutel naar een uitgeversmodel.

Hoe kan ik in de Django-beheerder een kolom weergeven met het aantal boeken dat door elke uitgever is gepubliceerd, zodat ik de ingebouwde sortering kan gebruiken?


Antwoord 1, autoriteit 100%

Ik had hetzelfde probleem (ik kan de manager van mijn model niet wijzigen om langzame annotaties of joins toe te voegen). Een combinatie van twee van de antwoorden hier werkt. @Andre is heel dichtbij, de Django-beheerder ondersteunt het wijzigen van de queryset voor alleen de beheerder, dus pas hier dezelfde logica toe en gebruik vervolgens het attribuut admin_order_field. Je moet natuurlijk nog steeds het nieuwe beheerdersveld toevoegen aan list_display.

from django.db.models import Count
class EventAdmin(admin.ModelAdmin)
    list_display = (..., 'show_artist_count')
    def queryset(self, request):
    # def get_queryset(self, request):    for Django 1.6+
        qs = super(EventAdmin, self).queryset(request)
        return qs.annotate(artist_count=Count('artists'))
    def show_artist_count(self, inst):
        return inst.artist_count
    show_artist_count.admin_order_field = 'artist_count'

Antwoord 2, autoriteit 10%

Probeer dit:

maak een nieuwe Manager(en aggregerenmet telling in het veld boekrelatie):

class PublisherManager(models.Manager):
    def get_query_set(self):
        return super(PublisherManager,self).get_query_set().annotate(pubcount=Count('book'))

sorteer het op pubcount:

class Publisher(models.Model):
    ......
    objects = PublisherManager()
    class Meta:
        ordering = ('pubcount',)

Antwoord 3

Je zou inderdaad moeten beginnen met het toevoegen van:

class PublisherManager(models.Manager):
    def get_query_set(self):
        return super(PublisherManager,self).get_query_set().annotate(pubcount=Count('book'))

Maar de juiste manier om het toe te voegen als een sorteerbaar veld is:

class Publisher(models.Model):
    ......
    objects = PublisherManager()
    def count(self):
        return self.pubcount
    count.admin_order_field = 'pubcount'

En dan kun je gewoon ‘count’ toevoegen aan het list_display attribuut van model admin in admin.py


Antwoord 4

Lincoln B’s antwoord was de juiste manier voor mij.

Eerst wilde ik alleen commentaar geven op zijn oplossing, maar ik merkte dat ik een iets ander probleem aan het oplossen was. Ik had een beheerdersklas, die ik wilde “aanpassen” aan mijn behoeften – namelijk de django-taggitadmin. In een van de admin.pyvan mijn toepassing heb ik toegevoegd:

# sort tags by name in admin (count items also possible)
from taggit.admin import TagAdmin
TagAdmin.ordering = ["name"]
#   make sortable on item_count:
# 1. function for lookup
def item_count(obj):
    """This takes the item_count from object: didn't work as model field."""
    return obj.item_count # not needed: obj.taggit_taggeditem_items.count()
# 2. property in function - admin field name
item_count.admin_order_field = 'item_count'
# 3. queryset override, with count annotation
from django.db.models import Count
TagAdmin.queryset = lambda self, request: super(TagAdmin, self).queryset(request).annotate(item_count=Count('taggit_taggeditem_items'))
# 4. add to list display
TagAdmin.list_display = ["name", item_count]

De interessante observatie voor mij was dat ik niet alleen de querysetkon annoteren en "item_count"kon toevoegen aan list_display– omdat er was geen item_countmethode in TagAdmin, noch een methode of veld in de Tagmodelklasse (alleen in de queryset).


Antwoord 5

Probeer zoiets als dit:

class PublisherAdminWithCount(Admin):
    def book_count(self, obj):
        return obj.book_set.count()
    list_display = ('user_count',)
admin.site.register(Group, PublisherAdminWithCount)

Other episodes