I want to know how I can display the “highest value” from my ManyToMany Field in the admin. This is my models.py
file:
class Personal(models.Model): lastname = models.CharField(max_length = 100) firstname = models.CharField(max_length = 100) degree = models.ManyToManyField('Degree') def __str__(self): return self.lastname class Degree(models.Model): name_degree = models.CharField(verbose_name = 'Degree', max_length = 200, blank = False) rank = models.PositiveIntegerField(default = 0) def __str__(self): return self.name_degree
In my backend, I have created different types of Degree’s, all with a “ranking”. In my case, the highest degree you can have is “Doctoral Degree”, with a rank of “6”.
So if a User is creating himself in “Personal” he will have the option to select all the Degree’s he has achieved. But for my Personal list, I just to want to see the highest one, e.g. if the User selects “Bachelor’s Degree” and “Master’s Degree”, the Personal list should only contain “Master’s Degree”, because 5 > 4.
Someone with an idea on how my admin.py
file should look like?
class PersonalAdmin(admin.ModelAdmin): list_display = ('lastname', 'firstname', ) # 'degree'
Big thanks in advance!
Advertisement
Answer
You can define a method in your ModelAdmin
to calculate the maximum degree:
class PersonalAdmin(admin.ModelAdmin): list_display = ('lastname', 'firstname', 'highest_degree') @admin.display(description='Highest degree') def highest_degree(self, obj): deg = Degree.objects.filter(personal=obj).first() if deg: return deg.name_degree return None
This is however quite inefficient if we want to render a lot of Personal
s, since that will require an extra query per item.
We can let the database do the work for us with a subquery expression:
from django.db.models import OuterRef, Subquery class PersonalAdmin(admin.ModelAdmin): list_display = ('lastname', 'firstname', 'highest_degree') def get_queryset(self, request): return Personal.objects.annotate( highest_degree=Subquery( Degree.objects.filter( personal=OuterRef('pk') ).order_by('-rank').values('name_degree')[:1] ) ) @admin.display(description='Highest degree') def highest_degree(self, obj): return obj.highest_degree
This will calculate the name of the degrees in bulk.