Victor Castillo Torres
Victor Castillo Torres

Reputation: 10811

Fastest way to combine two models by attribute in Django

I have two models which have an attribute in common, I would like two combine them if the attributes in both are equal, each one fetches the data from different databases:

First Model

class User(models.Model):
      matricula = models.CharField(max_length=100)

      def __unicode__(self):
          return self.matricula

Second Model

class UserMoodle(models.Model):
      matricula = models.CharField(max_length=100)

      def __unicode__(self):
          return self.matricula

Example

If I have:

lists_users = [<User: 123>, <User: 2345>,<User:567>]
lists_users_moodle =  [<UserMoodle:123>, <UserMoodle:2345>, <UserMoodle:897>]

I would like to combine them and get this result:

combined_models_lists = [[<User: 123>, <UserMoodle: 123>], [<User: 2345>, <UserMoodle: 2345>], [<User: 567>, None],[None, <UserMoodle: 897>]]

Thanks in advance!

Upvotes: 0

Views: 63

Answers (1)

Ashwini Chaudhary
Ashwini Chaudhary

Reputation: 250951

One way would be to create dicts from those lists(or QuerySet) first and then iterate over union of keys of those dicts to get the desired output:

dict_users = {x.matricula : x for x in lists_users}
dict_moodle = {x.matricula : x for x in lists_users_moodle}

combined_models_lists  = [[dict_users.get(x), dict_moodle.get(x)] 
                                          for x in set(dict_users).union(dict_moodle)]

Another way will be to iterate over sorted version of each QuerySet and then population a list, this way we will use lesser memory compared to the above method:

users = User.objects.all().order_by('matricula') #order by field `matricula`
moodles = UserMoodle.objects.all().order_by('matricula')

out = []

it1 = iter(users)
it2 = iter(moodles)

prev1 = next(it1)
prev2 = next(it2)

while True:
    if prev1.matricula  == prev2.matricula:
        out.append([prev1, prev2])
        try:
            prev1 = next(it1)
            prev2 = next(it2)
        except StopIteration:
            out.extend([x, None] for x in it1)
            out.extend([None, x] for x in it2)
            break

    if prev1.matricula  < prev2.matricula :
        out.append([prev1, None])
        try:
            prev1 = next(it1)
        except StopIteration:
            out.append([None, prev2])
            out.extend([None, x] for x in it2)
            break

    if prev1.matricula  > prev2.matricula :
        out.append([None, prev2])
        try:
            prev2 = next(it2)
        except StopIteration:
            out.extend([prev1, None])
            out.extend([x, None] for x in it1)
            break

Upvotes: 2

Related Questions