Использование пользовательских методов в фильтре с django-rest-framework

Я хотел бы фильтровать параметры запроса в моем REST API - см. документацию по django. Однако один параметр, по которому я хочу отфильтровать, доступен только через модель @property

пример models.py:

class Listing(models.Model):
    product = models.OneToOneField(Product, related_name='listing')
    ...
    @property
    def category(self):
        return self.product.assets[0].category.name

Вот настройка моего Listing API в соответствии с django-filter docs

    class ListingFilter(django_filters.FilterSet):
        product = django_filters.CharFilter(name='product__name')
        category = django_filters.CharFilter(name='category') #DOES NOT WORK!!

        class Meta:
            model = Listing
            fields = ['product','category']

    class ListingList(generics.ListCreateAPIView):
        queryset = Listing.objects.all()
        serializer_class = ListingSerializer
        filter_class = ListingFilter

Как правильно фильтровать по listing.category? Он недоступен непосредственно в модели листинга.


person tomcounsell    schedule 25.06.2014    source источник
comment
В таких случаях может помочь этот ответ на другой вопрос: stackoverflow.com/questions/14258338/   -  person iankit    schedule 19.02.2016


Ответы (2)


Используйте параметр action для указания пользовательского метода — см. документацию по django-filter

Сначала определите метод, который фильтрует набор запросов, используя значение параметра категории:

    def filter_category(queryset, value):
        if not value:
            return queryset

        queryset = ...custom filtering on queryset using 'value'...
        return queryset

Фильтр листинга должен выглядеть так:

    class ListingFilter(django_filters.FilterSet):
        ...
        category = django_filters.CharFilter(action=filter_category)
        ...
person tomcounsell    schedule 25.06.2014
comment
Небольшое замечание: django-filter action был заменен на method. django-filter.readthedocs.io/en/latest/migration.html - person Jason Capriotti; 02.11.2016

Ради скорости базы данных вы должны просто добавить категорию в свою модель листинга.

class Listing(models.Model):
    product = models.OneToOneField(Product, related_name='listing')
    category = models.ForeignKey(Category)

Затем используйте сигнал post_save, чтобы поле обновлялось.

from django.dispatch import receiver
from django.db.models.signals import post_save

@receiver(post_save, sender=Product)
def updateCategory(sender, instance, created, update_fields, **kwargs):
    product = instance
    product.listing.category = product.assets[0].category.name
    product.listing.save()

Затем отфильтруйте по его имени, как и по любому другому полю:

class ListingFilter(django_filters.FilterSet):
    ...
    category = django_filters.CharFilter(name='category__name')
    ...
person tomcounsell    schedule 25.06.2014