开发者

Django Combine a Variable Number of QuerySets

开发者 https://www.devze.com 2023-03-11 10:56 出处:网络
Is there a way to concatenate a unknown number of querysets into a list? Here are my models: class Item(models.Model):

Is there a way to concatenate a unknown number of querysets into a list?

Here are my models:

class Item(models.Model):
    name = models.CharField(max_length=200)
    brand = models.ForeignKey(User, related_name='brand')
    tags = models.ManyToManyFiel开发者_运维知识库d(Tag, blank=True, null=True)
    def __unicode__(self):
        return self.name
    class Meta:
        ordering = ['-id']

class Tag(models.Model):
    name = models.CharField(max_length=64, unique=True)
    def __unicode__(self):
        return self.name

I have two types of queries that I'm working with:

  1. items = Item.objects.filter(brands__in=brands)

  2. items = Item.objects.filter(tags__name='80s').filter(tags__name='comedy')

With regards to the second type of query, users can save searches (for example "80s comedy"), and can save multiple searches at the same time, so I will need to create a query for each search that they have saved.

I originally wanted to try and construct a single query that will handle both cases (see Django Combining AND and OR Queries with ManyToMany Field ), but I now think the best way to do this would be to combine all queries into a list.

I like what @akaihola suggests here: How to combine 2 or more querysets in a Django view? but I can't figure out how to use itertools.chain with a variable number of queries.

Does anyone know the best way to accomplish that?

EDIT: Forgot to mention, what I'm looking for are items that have a certain brand OR have all of the required tags.


Slightly unorthodox, but you could use recursion. So in your example:

def recursive_search(tags, results_queryset):
    if len(tags) > 0:
        result_qs = result_queryset.filter(tags_name=tags[0])
        if result_queryset.exists():
            return filter_recursion(tags[1:],result_queryset)
        else:
            return None
    return result_queryset

tags = ["comedy", "80s", "action", "thriller"]  # This can be variable
result_queryset = Item.objects.filter(brands__in=brands) # Could be Item.objects.all()
print recursive_search(tags, result_queryset)

So you start off with a list of the tags you are searching for, and a queryset of ALL of your items that could possibly fit your criteria (in this case we start with the list of items of a particular brand)

You then recursively go through the list of tags one by one and cut the queryset down. For each level, you re-filter the entire queryset to only those items which have all the mentioned tags.

so:

  • the first call/level would be for all the items that have the tag favourite,
  • the second call/level would be for all the items that have the tags favourite and loudest,
  • etc.

If the queryset returned by the filter is None, it means there are no items that have all the required tags, and the method will quit and return None (i.e. it quits at the first possible instance of failure). Furthermore, there should only be a single hit to the database (I think!)

I've tested this out and it should work, so give it a shot

EDIT

To concatonate the queryset returned from the brands (q1) and the queryset created above using itertools (q2):

list = []
for item in itertools.chain(q1, q2):
    list.append(item)

EDIT 2

does this not accomplish what you need in one query?

# list of tags = ['comedy','80s']
qs = Item.objects.all( Q(brand__iexact="brand name") | Q(tags__name__in=[tag for tag in list_of_tags]) )
0

精彩评论

暂无评论...
验证码 换一张
取 消