djangodjango-modelsdjango-rest-frameworkdjango-filterdjango-filters

Django Rest Framework - filters models where ArrayField includes value1 OR value2, not subset


straight to the point.

This is my model:

class Product(models.Model):

brand = models.CharField(max_length=40)
model = models.CharField(max_length=40)
price = models.FloatField(default=0)

colors = ArrayField(models.CharField(max_length=20), blank=True, null=True, default=[])
sizes = ArrayField(models.IntegerField(), blank=True, null=True)

discount_price = models.FloatField(null=True, blank=True, default=list)
date_added = models.DateTimeField(auto_now_add=True)
desc = models.TextField(default=None, blank=True, null=True)
gender = models.CharField(choices=GENDER_CHOICES, blank=True, null=True, max_length=10)
for_kids = models.BooleanField(blank=True, null=True)

Serializer:

class ProductsSerializer(serializers.ModelSerializer):

pictures = ProductPicturesSerializer(many=True, read_only=True)
average_rating = serializers.SerializerMethodField()

def get_average_rating(self, obj):
    return obj.average_rating

class Meta:
    model = Product
    fields = ['id', 'brand', 'model', 'price', 'discount_price',
              'date_added', 'for_kids', 'gender',
              'average_rating', 'sizes', 'colors', 'pictures']

My custom filter:

class CharArrayFilter(filters.BaseCSVFilter, filters.CharFilter):
    pass


class ProductsFilter(filters.FilterSet):

    min_price = filters.NumberFilter(field_name="price", lookup_expr='gte')
    max_price = filters.NumberFilter(field_name="price", lookup_expr='lte')

    colors = CharArrayFilter(lookup_expr='contains')


    class Meta:
        model = Product
        fields = {
            'brand': ['in'],
            'model': ['in', 'exact'],

        }

The problem is, when Im filtering it with colors like: red,black,blue, it only displays objects which have subset of theese values in array, not one of them which is my goal. I cant find ANY lookup expression that would work,trying lookup_expr = colors__in also doesn't work if anyone is wondering. Like display all shoes which have value red OR black OR blue in this field.

Please help, I've tried everything


Solution

  • overlap is the lookup expression you are after.

    Returns objects where the data shares any results with the values passed. Uses the SQL operator &&. For example:

    >>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
    >>> Post.objects.create(name='Second post', tags=['thoughts'])
    >>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])
    
    >>> Post.objects.filter(tags__overlap=['thoughts'])
    <QuerySet [<Post: First post>, <Post: Second post>]>
    
    >>> Post.objects.filter(tags__overlap=['thoughts', 'tutorial'])
    <QuerySet [<Post: First post>, <Post: Second post>, <Post: Third post>]>