简体   繁体   中英

Joining two querysets in Django

Suppose I have the following models

class Award(models.Model):
    user = models.ForeignKey(User)


class AwardReceived(models.Model):
    award = models.ForeignKey(award)
    date = models.DateField()
    units = models.IntegerField()


class AwardUsed(models.Model):
    award = models.ForeignKey(award)
    date = models.DateField()
    units = models.IntegerField()  

Now, suppose I want to get the number of awards for all users and the number of awards used for all users (ie, a queryset containing both). I prefer to do it one query for each calculation - when I combined it in my code I had some unexpected results. Also for some of my queries it won't be possible to do it one query, since the query will get too complex - I'm calculating 8 fields. This is how I solved it so far:

def get_summary(query_date)
    summary = (Award.objects.filter(awardreceived__date__lte=query_date))
                    .annotate(awarded=Sum('awardissuedactivity__units_awarded')))

    awards_used = (Award.objects.filter(awardused__date__lte=query_date)
                        .annotate(used=Sum('awardused__date__lte__units')))

    award_used_dict = {}
    for award in awards_used:
        award_used_dict[award] = award.used

    for award in summary:
        award.used = award_used_dict.get(award, 0)

    return summary

I'm sure there must be a way to solve this without the dictionary approach? For instance, something like this: awards_used.get(award=award) , but this causes a db lookup every loop.

Or some other fancy way to join the querysets?

Note this is a simplified example and I know for this example the DB structure can be improved, I'm just trying to illustrate my question.

SOLUTION 1

Just try to concatenate your queryset using |

final_q = q1 | q2

In your example

final_q = summary | awards_used

UPDATED:

| does not works using calculated attributes, so, we can select our queryset first and then mapping our extra attributes

summary = Award.objects.filter(awardreceived__date__lte=query_date) 
awards_used = Award.objects.filter(awardused__date__lte=query_date)
final_q = summary | awards_used

final_q = final_q.annotate(used=Sum('awardused__date__lte__units')).annotate(awarded=Sum('awardissuedactivity__units_awarded'))

SOLUTION 2

Using chain built-in function

from itertools import chain
final_list = list(chain(summary, awards_used))

There is an issue with this approach, you won't get a queryset, you will get a list containing instances.

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