2014-07-07 4 views
1

Есть таблицы Product и Transaction. В Product admin есть поле, которое показывает, сколько транзакций было создано с этим продуктом.Как фильтровать объекты по значениям посторонних объектов в Django admin

Теперь я хочу отфильтровать период времени, в котором были созданы эти транзакции. Например, на прошлой неделе было 30 транзакций, но за последний месяц - 100.

class Transaction(models.Model): 
    created = models.DateTimeField() 
    product = models.ForeignKey('Product') 

class Product(models.Model): 
    name = models.CharField() 

    def num_of_trans(self): 
     return Transaction.objects.filter(created="""DATE_VALUE_HERE""").filter(product=self.id).count() 

Я пытался настроить queryset в классе, который простирается SimpleListFilter, однако мне не удалось заставить его работать.

Еще одна вещь, о которой я думал, заключается в добавлении дополнительного параметра в функцию num_of_trans, но, похоже, нет способа передать этот параметр при использовании фильтра.

Буду признателен за любые советы.

EDIT

class MyListFilter(admin.SimpleListFilter): 
    title = _('period') 
    parameter_name = 'period' 

    def lookups(self, request, model_admin): 
     return (
      ('7', _('last week')), 
      ('30', _('last month')), 
     ) 

    def queryset(self, request, queryset): 
     period = timezone.now() + timedelta(days=self.value())) 
     return queryset.filter(transactions__gte=period) 
     # of course it won't work, but represents the idea 
+0

Обновите свой вопрос расширенным классом SimpleListFilter. –

+0

Я не уверен, что это сработает, но изменение num_of_trans в свойство может позволить вам использовать его в list_filter. – vsachar

ответ

0
from datetime import datetime, timedelta 

class Transaction(models.Model): 
    created = models.DateTimeField() 
    product = models.ForeignKey('Product', related_name='products') 

class Product(models.Model): 
    name = models.CharField() 

    def num_of_trans_last_week(self): 
     now = datetime.now() 
     self.products.filter(created__range=(now-timedelta(days=7), now)).count() 
+0

Ну, это один из способов сделать это, однако я бы предпочел использовать 'list_filter'. В вашем решении я вынужден создать новую функцию для каждого периода. Более того, все эти функции нужно будет вызывать каждый раз, что не так хорошо для производительности. – dev9

+0

вам не нужно создавать больше функций, вы можете установить параметр: def def_ty_trans (self, period): .... now-timedelta (days = period) '. Вы уверены, что это не сработает? –

+0

Он работает, но я не могу вызвать эту функцию с параметром. Он автоматически вызывается администратором, и все, что я могу предоставить, - это имя, никаких параметров. – dev9

0

Хотя я стараюсь не использовать чистый SQL с ОРМ, используя «лишние» это единственный способ, которым я могу себе представить, в этом случае. Делает 2 дополнительных запроса.

class MyListFilter(admin.SimpleListFilter): 
    title = _('period') 
    parameter_name = 'period' 

    def lookups(self, request, model_admin): 
     return (
      ('7', _('last week')), 
      ('30', _('last month')), 
     ) 

    def queryset(self, request, queryset): 
     if self.value(): # Count transactions by date 
      period = timezone.now() - timedelta(days=int(self.value()) 
      transaction_ids = Transaction.objects\ 
        .filter(created__gte=period)\ 
        .values_list('id', flat=True) 

      qs = queryset.extra(select={ 
        "num_transactions": """ 
         SELECT COUNT(*) 
         FROM appname_transaction 
         WHERE appname_transaction.id IN ({}) 
         AND appname_transaction.product_id = appname_product.id 
         """.format(", ".join([str(t_id) for t_id in transaction_ids])) 
      }) 

     else: # Count all 
      qs = queryset.extra(select={ 
        "num_transactions": """ 
         SELECT COUNT(*) 
         FROM appname_transaction 
         WHERE appname_transaction.product_id = appname_product.id 
         """ 
      }) 
     return qs 


class ProductAdmin(admin.ModelAdmin): 
    list_filter = [MyListFilter, ] 
    list_display = [..., 'num_transactions'] 
    ... 

    def num_transactions(self, obj): 
     return getattr(obj, 'num_transactions') 
Смежные вопросы