Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django filter filter with lists

So, I have this model:

class Product(models.Model):
    colors = models.TextField(max_length=150,default = 'blue,green,blue')

and i want to filter it with a list of colors for example.
Any idea on how can i do it?
colors = ["blue","green"]
I need something like this.

products = Product.objects.filter(colors__icontains = colors)

Any kind of help or sugestion on how can i modify the model to filter will be apreciated .

like image 616
Rus Mine Avatar asked Nov 18 '15 13:11

Rus Mine


2 Answers

Since you are storing colors as plain text and not using a related model, you cannot use the kind of filters you want.

The right way to do it is to use a ManyToManyField: a color can have multiple products and a product can have multiple colors:

class Color(models.Model):
    name = models.CharField(max_length=255)

class Product(models.Model):
    colors = models.ManyToManyField(Color, related_name='colors')

Then, you can add colors like this:

blue = Color(name='blue')
blue.save()
red = Color(name='red')
red.save()
my_product = Product()
my_product.save()
my_product.colors.add(blue)

If you want to query all products that are red OR blue, just do:

Product.objects.filter(colors__in=[red, blue]) # Red and blue being Color instances

If you want all products that are red AND blue, just do as described here:

Product.objects.filter(colors=red).filter(colors=blue) # Red and blue being Color instances

Chaining filters like this is not especially convenient, so you'll probably want a custom QuerySet that will do it for you :

class AllManyToManyQuerySet(models.QuerySet):
    def filter_all_many_to_many(self, attribute, *args):
        qs = self
        for arg in args:
            qs = qs.filter(**{attribute: arg})
        return qs

class Product(models.Model):
    colors = models.ManyToManyField(Color, related_name='colors')
    objects = AllManyToManyQuerySet.as_manager()

And use it like this:

Product.objects.all().filter_all_many_to_many('colors', red, blue) # red and blue being color instances

Another filter method is:

product_list = Product.objects.filter(reduce(operator.and_, [Q(colors__name=c) for c in colors]))

It's untested, but it should probably work, and you can use the queryset on other classes if you need it elsewhere, keeping you code clean and DRY ;)

like image 156
Agate Avatar answered Sep 17 '22 22:09

Agate


You could just iterate over it, nothing gets executed till you require the data

products = Product.objects.all()
for color in colors:
    products = products.filter(colors__icontains=color)
like image 22
Sayse Avatar answered Sep 17 '22 22:09

Sayse