Reputation: 950
I hope I can get your advice on this.
If I have a Pizza model with a many-to-many relationship to topping, I can easily get the count of the toppings on the pizza.
# models.py
class Pizza(models.Model):
toppings = models.ManyToManyField(Topping)
class Topping(models.Model):
name = models.CharField(max_length=255, null=True, blank=True)
# admin.py
@admin.register(Pizza)
class PizzaAdmin(admin.ModelAdmin):
list_display = ['topping_count']
def topping_count(self, pizza):
return pizza.toppings.count()
However, if I move the many-to-many forward relationship to Topping, this all breaks.
# models.py
class Pizza(models.Model):
pass
class Topping(models.Model):
pizzas = models.ManyToManyField(Pizza)
How can I update topping_count in PizzaAdmin so I'm able to get the toppings count via the reverse relationship?
Thank you.
Upvotes: 0
Views: 79
Reputation: 1103
The way you count your toppings is inefficient in that it does a separate DB query for each topping. I would suggest making use of annotate
:
@admin.register(Pizza)
class PizzaAdmin(admin.ModelAdmin):
list_display = ['topping_count']
def get_queryset(self, request):
qs = super(PizzaAdmin, self).get_queryset(request)
return qs.annotate(topping_count=Count('topping_set'))
def topping_count(self, pizza):
return pizza.topping_count
Explanation: annotate
will set topping_count
for each Pizza
in admin. Then your PizzaAdmin.topping_count
will just retrieve the value of topping_count from the instance.
That way it should touch your database only once regarless of the number of pizzas.
Upvotes: 1
Reputation: 32949
Get the count of the reverse relationship as pizza.topping_set.count()
Upvotes: 1
Reputation: 53659
The reverse relation uses the lowercased model name + _set
:
@admin.register(Pizza)
class PizzaAdmin(admin.ModelAdmin):
list_display = ['topping_count']
def topping_count(self, pizza):
return pizza.topping_set.count()
Alternatively, you can keep the current admin code and change the related name:
class Topping(models.Model):
pizzas = models.ManyToManyField(Pizza, related_name='toppings')
Upvotes: 2