Quản trị viên Django: cách sắp xếp theo một trong các trường list_display tùy chỉnh không có trường cơ sở dữ liệu


122
# 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()  

Làm thế nào tôi có thể sắp xếp Khách hàng, tùy thuộc vào number_of_ordershọ có?

admin_order_fieldkhông thể sử dụng thuộc tính ở đây, vì nó yêu cầu một trường cơ sở dữ liệu để sắp xếp. Có thể nào không, vì Django dựa vào DB bên dưới để thực hiện sắp xếp? Việc tạo một trường tổng hợp để chứa số lượng đơn đặt hàng có vẻ là một việc làm quá mức cần thiết ở đây.

Điều thú vị: nếu bạn thay đổi url bằng tay trong trình duyệt để sắp xếp trên cột này - nó hoạt động như mong đợi!


"Điều thú vị: nếu bạn thay đổi url bằng tay trong trình duyệt để sắp xếp trên cột này - nó hoạt động như mong đợi!" Ý bạn là: / admin / myapp / customer /? Ot = asc & o = 2 Bạn có chắc không?
Andy Baker

vâng, cả asc và dsc. Có lẽ nó chỉ hoạt động với số thập phân.
mike_k

Tôi không nghĩ rằng nó sẽ hoạt động với nhiều trang.
Chase Seibert

Câu trả lời:


159

Tôi thích giải pháp của Greg cho vấn đề này, nhưng tôi muốn chỉ ra rằng bạn có thể làm điều tương tự trực tiếp trong quản trị viên:

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'

Bằng cách này, bạn chỉ chú thích bên trong giao diện quản trị. Không phải với mọi truy vấn mà bạn làm.


5
Vâng, đây là một cách tốt hơn nhiều. :)
Greg

2
Có một chỉnh sửa được đề xuất cho câu trả lời này. Tôi đã bỏ phiếu để từ chối nó vì nó đã loại bỏ quá nhiều văn bản. Tôi không biết Django, tôi không biết liệu thay đổi mã được đề xuất có đáng nói hay không.
Gilles 'Somali dừng tà ác'

1
@Gilles bản chỉnh sửa được đề xuất là đúng về định nghĩa number_of_orders đơn giản hơn. Điều này hoạt động: def number_of_orders(self, obj): return obj.order__count
Nils

1
Đó không phải là get_queryset()thay vì queryset()?
Mariusz Jamro

2
nên là get_queryset (tự, yêu cầu): ... cho Django 1.6+
michael

50

Tôi chưa thử nghiệm điều này (tôi muốn biết liệu nó có hoạt động hay không) nhưng còn việc xác định trình quản lý tùy chỉnh Customerbao gồm số lượng đơn đặt hàng được tổng hợp và sau đó đặt admin_order_fieldthành tổng số đó, tức là

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'

CHỈNH SỬA: Tôi vừa thử nghiệm ý tưởng này và nó hoạt động hoàn hảo - không yêu cầu phân lớp quản trị viên django!


1
Đây là câu trả lời tốt hơn so với câu được chấp nhận. Vấn đề mà tôi gặp phải khi áp dụng cái được chấp nhận là khi bạn tìm kiếm thứ gì đó cùng với bộ truy vấn cập nhật đó ở cấp quản trị, nó mất quá nhiều thời gian và cũng dẫn đến sai số cho kết quả mà nó tìm thấy.
Mutant

0

Cách duy nhất tôi có thể nghĩ đến là không chuẩn hóa trường. Đó là - tạo một trường thực được cập nhật để luôn đồng bộ với các trường mà nó bắt nguồn. Tôi thường làm điều này bằng cách ghi đè lưu trên mô hình với các trường không chuẩn hóa hoặc mô hình mà nó bắt nguồn từ:

# models.py
class Order(models.Model):
    bar = models.CharField[...]
    customer = models.ForeignKey(Customer)
    def save(self):
        super(Order, self).save()
        self.customer.number_of_orders = Order.objects.filter(customer=self.customer).count()
        self.customer.save()

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

1
Điều này chắc chắn sẽ hoạt động, nhưng không thể đánh dấu nó là được chấp nhận do có thêm trường DB. Cũng lưu ý rằng thiếu .count () ở cuối dòng thiết lập truy vấn.
mike_k

đã sửa số đếm (). Giải pháp duy nhất khác (thiếu phân lớp khối lượng lớn Contrib.admin) sẽ là hack Jquery / Ajaxy.
Andy Baker
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.