I'm trying to add a computed column to one of my models on the admin page, specifically adding an average mark from a foreign key in my Mark model, and adding it to the relevant Student in my student model.
When I run an aggregate avg command within my StudentAdmin class, it returns the average of the grades id field, not the mark field.
Here is the relevant code:
models.py
class Student(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.EmailField(unique=True)
def __str__(self) -> str:
return self.last_name
class Meta:
ordering = ['last_name']
class Module(models.Model):
module = models.CharField(max_length=255)
class Grade(models.Model):
mark = models.PositiveSmallIntegerField(null=True)
module_id = models.ForeignKey(Module, on_delete=models.PROTECT)
student_id = models.ForeignKey(Student, on_delete=models.PROTECT)
def __str__(self) -> str:
return self.mark
...
admin.py
...
@admin.register(models.Student)
class StudentAdmin(admin.ModelAdmin):
list_display = ['id', 'first_name', 'last_name', 'average_grade']
def average_grade(self, student):
return student.average_grade
def get_queryset(self, request):
return super().get_queryset(request).annotate(
average_grade = Avg('grade')
)
How do I specify which column from Grade to use?
To access the attributes of related models, we use __
(double underscore) after model name, so change your query as below:
return super().get_queryset(request).annotate(
average_grade = Avg('grade__mark')
)
There is also error in your code, in your Grade
model, mark
is represented as integer
, but str
method returns a string, so you have to convert it to str
before returning.
class Grade(models.Model):
...
def __str__(self) -> str:
return str(self.mark)
Advice: You should consider providing related_name
in your ForeignKey
fields as below:
module_id = models.ForeignKey(Module, related_name="module_grades",on_delete=models.PROTECT)
student_id = models.ForeignKey(Student, related_name="grades",on_delete=models.PROTECT)