简体   繁体   中英

Using custom methods in filter with django-rest-framework

I would like to filter against query params in my REST API - see django docs on this . However, one parameter I wish to filter by is only available via a model @property

example models.py:

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

Here is the setup for my Listing API in accordance with 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

How can I appropriately filter by listing.category? It is not available on the listing model directly.

Use the 'action' parameter to specify a custom method - see django-filter docs

First define a method that filters a queryset using the value of the category parameter:

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

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

Listing filter should look like this:

    class ListingFilter(django_filters.FilterSet):
        ...
        category = django_filters.CharFilter(action=filter_category)
        ...

For sake of database speed, you should just add the category to your listing model

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

Then use a post_save signal to keep the field updated

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

Then filter by it's name as you would any other field:

class ListingFilter(django_filters.FilterSet):
    ...
    category = django_filters.CharFilter(name='category__name')
    ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM