Администратор Django: как сортировать по одному из пользовательских полей list_display, у которого нет поля базы данных - PullRequest
108 голосов
/ 30 января 2010
# admin.py
class CustomerAdmin(admin.ModelAdmin):  
    list_display = ('foo', 'number_of_orders')

# models.py
class Order(models.Model):
    bar = models.CharField[...]
    customer = models.ForeignKey(Customer)

class Customer(models.Model):
    foo = models.CharField[...]
    def number_of_orders(self):
        return u'%s' % Order.objects.filter(customer=self).count()  

Как я могу отсортировать клиентов, в зависимости от того, number_of_orders у них есть?

admin_order_field свойство не может быть использовано здесь, так как для сортировки требуется поле базы данных. Возможно ли это вообще, поскольку Django полагается на базовую БД для выполнения сортировки? Создание агрегатного поля, содержащего количество заказов, выглядит здесь как излишнее.

Забавная вещь: если вы измените URL вручную в браузере для сортировки по этому столбцу - он будет работать как положено!

Ответы [ 3 ]

142 голосов
/ 16 сентября 2011

Мне понравилось решение Грега для этой проблемы, но я хотел бы отметить, что вы можете сделать то же самое прямо в админе:

from django.db import models

class CustomerAdmin(admin.ModelAdmin):
    list_display = ('number_of_orders',)

    def get_queryset(self, request):
    # def queryset(self, request): # For Django <1.6
        qs = super(CustomerAdmin, self).get_queryset(request)
        # qs = super(CustomerAdmin, self).queryset(request) # For Django <1.6
        qs = qs.annotate(models.Count('order'))
        return qs

    def number_of_orders(self, obj):
        return obj.order__count
    number_of_orders.admin_order_field = 'order__count'

Таким образом, вы можете аннотировать только внутри интерфейса администратора. Не с каждым вашим запросом.

46 голосов
/ 01 марта 2010

Я не проверял это (мне было бы интересно узнать, работает ли он), но как насчет определения настраиваемого менеджера для Customer, который включает в себя количество агрегированных заказов, а затем установки admin_order_field для этого агрегата? т.е.

from django.db import models 


class CustomerManager(models.Manager):
    def get_query_set(self):
        return super(CustomerManager, self).get_query_set().annotate(models.Count('order'))

class Customer(models.Model):
    foo = models.CharField[...]

    objects = CustomerManager()

    def number_of_orders(self):
        return u'%s' % Order.objects.filter(customer=self).count()
    number_of_orders.admin_order_field = 'order__count'

РЕДАКТИРОВАТЬ: Я только что проверил эту идею, и она отлично работает - не требуется подклассов администратора django!

0 голосов
/ 02 февраля 2010

Единственный способ, которым я могу думать, - это денормализовать поле.То есть - создайте реальное поле, которое обновляется, чтобы синхронизироваться с полями, из которых оно получено.Я обычно делаю это, переопределяя save на модель с денормализованными полями или на модель, из которой она получается:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...