通过相关模型上的过滤器对list_display字段进行排序

时间:2014-04-05 05:19:30

标签: django django-admin

我遇到了一个问题,我想根据管理员中相关字段的聚合进行排序,而不确定最佳方法。这是我的模特:

# 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__avglunch__service_rating__avgdinner__service_rating__avg

2 个答案:

答案 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'