Search code examples
pythondjangomodel

Runtime Foreign Key vs Integerfield


I have a problem. I already have two solution for my problem, but i was wondering which of those is the faster solution. I guess that the second solution is not only more convienient- to use but also faster, but i want to be sure, so thats the reason why im asking. My problem is i want to group multiple rows together. The group won't hold any meta data. So im only interested in runtime.

On the one hand i can use a Integer field and filter it later on when i need to get all entries that belong to the group. I guess runtime of O(n).

class SingleEntries(models.Model):
    name = models.CharField(max_length=20)
    group = models.IntegerField(null=True)


def find_all_group_members(id):
    return SingleEntries.objects.filter(group=id)

The second solution and probably the more practicle way would be to create a foreign key to another model only using the pk there. Then i can use the reverse relation to find all the entries that belong to the group.

class Group(models.Model):
    id = models.AutoField(primary_key=True)

class SingleEntries(models.Model):
    name = models.CharField(max_length=20)
    group = models.ForeignKey(Group,on_delete=models.CASCADE,null=True)

def find_all_group_members(id):
    return Group.objects.get(id=id).singleentries_set.all()

Solution

  • The first is more efficient, since this will use one query, whereas the latter will first fetch the Group, and then another one for the SingleEntries.

    Indeed, if you work with:

    SingleEntries.objects.filter(group=id)
    

    this will make a simple query:

    SELECT appname_singleentries.*
    FROM appname_singleentries
    WHERE appname_singleentries.group_id = id

    It thus does not first fetch the Group into memory.

    The latter will however make two queries. Indeed, it will first make a query to retrieve the Group, and then it will make a query like the one above to fetch the SingleEntries.

    The two are also semantically not entirely the same: if there is no such group, then the former will return an empty QuerySet, whereas the latter will raise a Group.DoesNotExists exception.

    But you can model this with:

    class Group(models.Model):
        pass
    
    class SingleEntries(models.Model):
        name = models.CharField(max_length=20)
        group = models.ForeignKey(Group,on_delete=models.CASCADE,null=True)
    
    
    def find_all_group_members(id):
        return SingleEntries.objects.filter(group_id=id)

    So you can use a Group model without having to retrieve the Group first.