我遇到了一个问题,我想根据管理员中相关字段的聚合进行排序,而不确定最佳方法。这是我的模特:
# models.py
from django.db import models
class Waiter(models.Model):
pass
class Customer(models.Model):
pass
class Meal(models.Model):
BREAKFAST = 1
LUNCH = 2
DINNER = 3
MEAL_TYPE_CHOICES = (
(BREAKFAST, 'Breakfast'),
(LUNCH, 'Lunch'),
(DINNER, 'Dinner'),
)
meal_type = models.IntegerField(choices=MEAL_TYPE_CHOICES)
customer = models.ForeignKey(Customer)
waiter = models.ForeignKey(Waiter)
service_rating = models.IntegerField()
在服务员管理员中,我想显示每位服务员的早餐,午餐和晚餐的平均服务评级,并希望能够按照这些单独的列进行订购。
This question解释了如何按每位服务员的所有服务评级(下面的代码)的平均排序,但我真的想通过每种膳食类型的适当服务评级来订购。最好的方法是什么?
# admin.py
from .models import Customer, Meal, Waiter
from django.contrib import admin
from django.db.models import Avg
class WaiterAdmin(admin.ModelAdmin):
list_display('avg_breakfast_rating', 'avg_lunch_rating', 'avg_dinner_rating')
def queryset(self, request):
qs = super(WaiterAdmin, self).queryset(request)
qs = qs.annotate(Avg('meal__service_rating'))
return qs
def avg_breakfast_rating(self, obj):
breakfasts = Meal.objects.filter(waiter=obj, meal_type=Meal.BREAKFAST)
return breakfasts.aggregate(avg_rating=Avg('service_rating'))['avg_rating']
avg_breakfast_rating.short_description = 'Average Breakfast Rating'
avg_breakfast_rating.admin_order_field = 'meal__service_rating__avg'
def avg_lunch_rating(self, obj):
lunches = Meal.objects.filter(waiter=obj, meal_type=Meal.LUNCH)
return lunches.aggregate(avg_rating=Avg('service_rating'))['avg_rating']
avg_lunch_rating.short_description = 'Average Lunch Rating'
avg_lunch_rating.admin_order_field = 'meal__service_rating__avg'
def avg_dinner_rating(self, obj):
dinners = Meal.objects.filter(waiter=obj, meal_type=Meal.DINNER)
return dinners.aggregate(avg_rating=Avg('service_rating'))['avg_rating']
avg_dinner_rating.short_description = 'Average Dinner Rating'
avg_dinner_rating.admin_order_field = 'meal__service_rating__avg'
基本上,我不想让admin_order_field为meal__service_rating__avg
,而是希望订购breakfast__service_rating__avg
,lunch__service_rating__avg
和dinner__service_rating__avg
。
答案 0 :(得分:2)
我会将这些字段添加到服务员:
breakfast_service_rating_avg = models.FloatField(null=True,blank=True)
lunch_service_rating_avg = models.FloatField(null=True,blank=True)
dinner_service_rating_avg = models.FloatField(null=True,blank=True)
然后Meal的save()方法将调用Waiter实例的save()方法,在保存Waiter实例之前计算* _service_rating_avg的三个平均值。
然后你总是在服务员实例上获得平均值,你可以在管理员中轻松排序。另外注释是一个非常昂贵的操作,如果可能的话我会避免它,并提供所提到的附加字段。
我希望这会帮助你!
答案 1 :(得分:2)
我认为你不能完全用ORM做到这一点(至少,我找不到办法)。但是,如果您愿意使用extra()和一些自定义SQL,那么有一个简单的解决方案。
具体情况可能因数据库而异,但这似乎适用于sqlite3:
class WaiterAdmin(admin.ModelAdmin):
list_display = ('name', 'breakfast_avg', 'lunch_avg', 'dinner_avg')
def _avg_query(self, meal_type):
return """
SELECT AVG(service_rating) FROM {meal_table}
WHERE meal_type={meal_type} AND waiter_id={waiter_table}.id
""".format(meal_table=Meal._meta.db_table,
waiter_table=Waiter._meta.db_table,
meal_type=meal_type)
def queryset(self, request):
qs = super(WaiterAdmin, self).queryset(request)
qs = qs.extra({'breakfast_avg': self._avg_query(Meal.BREAKFAST),
'lunch_avg': self._avg_query(Meal.LUNCH),
'dinner_avg': self._avg_query(Meal.DINNER),
})
return qs
def breakfast_avg(self, obj):
return obj.breakfast_avg
breakfast_avg.short_description = 'Average Breakfast Rating'
breakfast_avg.admin_order_field = 'breakfast_avg'
def lunch_avg(self, obj):
return obj.lunch_avg
lunch_avg.short_description = 'Average Lunch Rating'
lunch_avg.admin_order_field = 'lunch_avg'
def dinner_avg(self, obj):
return obj.dinner_avg
dinner_avg.short_description = 'Average Dinner Rating'
dinner_avg.admin_order_field = 'dinner_avg'