How to merge two queryset based on common key name in django

Question:

Let’s say I have 2 QuerySet:

<Queryset [a, b, c]> and <Queryset [a, d, e]>

How can I merge those two to achieve:

<Queryset [a, b, c, d, e]>

Edit with real case:

qs_home = Match.objects.filter(competition=competition) 
    .values(name=F("home_team__name")) 
    .annotate(home_win=Sum(Case(When(home_score__gt=F('away_score'), then=1)))) 
    .annotate(home_draw=Sum(Case(When(home_score=F('away_score'), then=1)))) 
    .annotate(home_lose=Sum(Case(When(home_score__lt=F('away_score'), then=1)))) 
    .annotate(home_goal=Sum('home_score')) 
    .annotate(home_conceded=Sum('away_score')) 
    .annotate(home_point=Sum(Case(
        When(home_score__gt=F('away_score'), then=3),
        When(home_score=F('away_score'), then=1)
    ))) 
    .order_by('home_team__name')
qs_away = Match.objects.filter(competition=competition) 
    .values(name=F("away_team__name")) 
    .annotate(away_win=Sum(Case(When(away_score__gt=F('home_score'), then=1)))) 
    .annotate(away_draw=Sum(Case(When(away_score=F('home_score'), then=1)))) 
    .annotate(away_lose=Sum(Case(When(away_score__lt=F('home_score'), then=1)))) 
    .annotate(away_goal=Sum('away_score')) 
    .annotate(away_conceded=Sum('home_score')) 
    .annotate(away_point=Sum(Case(
        When(away_score__gt=F('home_score'), then=3),
        When(away_score=F('home_score'), then=1)
    ))) 
    .order_by('away_team__name')

If i do qs_home.union(qs_away) the qs_away just append at the end of qs_home with the key from qs_home but the value of qs_away

Edit : My Workaround

for qs in qs_home:
    away_dict = next((item for item in qs_away if item['name'] == qs['name']), None)
    qs['away_win'] = away_dict['away_win']
    qs['away_draw'] = away_dict['away_draw']
    qs['away_lose'] = away_dict['away_lose']
    qs['away_goal'] = away_dict['away_goal']
    qs['away_conceded'] = away_dict['away_conceded']
    qs['away_point'] = away_dict['away_point']

this will append all the away_ key and value to qs_home. But of course im trying to avoid for loop if possible.

if im using union(), lets say, the len of qs_home and qs_away both 18. I get the queryset with 36 length. I want the length of the new queryset just 18 with the key, value append to each dict with the same name

Updated with my model:

class Match(models.Model):
    competition = models.ForeignKey(Competition, related_name='matches', on_delete=models.CASCADE)
    gameweek = models.PositiveSmallIntegerField(blank=True, null=True)
    home_team = models.ForeignKey(Team, related_name='home_matches', on_delete=models.CASCADE)
    away_team = models.ForeignKey(Team, related_name='away_matches', on_delete=models.CASCADE)
    home_score = models.PositiveSmallIntegerField(default=0)
    away_score = models.PositiveSmallIntegerField(default=0)

    STATUS = Choices(
        (1, 'not_started', 'Not Started'),
        (2, 'half_time', 'Half Time'),
        (3, 'full_time', 'Full Time'),
        (9, 'postponed', 'Postponed')
    )
    status = models.PositiveSmallIntegerField(choices=STATUS, default=STATUS.not_started)

Answers:

You can use union() and by looking at your queryset example, it also seems you want unique values, so try the following:

q1 = Model.objects.filter(name__in=['a', 'b', 'c'])
q2 = Model.objects.filter(name__in=['a', 'd', 'e'])
combined_qs = q1.union(q2)

This will return a new queryset that contains all distinct elements from both q1 and q2 In your case, it will be:

<QuerySet [a, b, c, d, e]>

Edit

You can make the loop more concise by using Python’s built-in zip()
function to iterate through the two QuerySets together and updating the first one with the values from the second one, like so following:

qs_home = Match.objects.filter(competition=competition) 
    .values(name=F("home_team__name")) 
    .annotate(home_win=Sum(Case(When(home_score__gt=F('away_score'), then=1)))) 
    .annotate(home_draw=Sum(Case(When(home_score=F('away_score'), then=1)))) 
    .annotate(home_lose=Sum(Case(When(home_score__lt=F('away_score'), then=1)))) 
    .annotate(home_goal=Sum('home_score')) 
    .annotate(home_conceded=Sum('away_score')) 
    .annotate(home_point=Sum(Case(
        When(home_score__gt=F('away_score'), then=3),
        When(home_score=F('away_score'), then=1)
    ))) 
    .order_by('home_team__name')
    
qs_away = Match.objects.filter(competition=competition) 
    .values(name=F("away_team__name")) 
    .annotate(away_win=Sum(Case(When(away_score__gt=F('home_score'), then=1)))) 
    .annotate(away_draw=Sum(Case(When(away_score=F('home_score'), then=1)))) 
    .annotate(away_lose=Sum(Case(When(away_score__lt=F('home_score'), then=1)))) 
    .annotate(away_goal=Sum('away_score')) 
    .annotate(away_conceded=Sum('home_score')) 
    .annotate(away_point=Sum(Case(
        When(away_score__gt=F('home_score'), then=3),
        When(away_score=F('home_score'), then=1)
    ))) 
    .order_by('away_team__name')
    
for home_dict, away_dict in zip(qs_home, qs_away):
    home_dict.update(away_dict)
    
merged_qs = qs_home
Answered By: Sunderam Dubey
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.