Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Chaining methods: returning `self` vs returning a new cloned object

Due to some restrictions in a project I'm working on, I had to replace Django's QuerySet class with a custom one. QuerySet objects can have their methods chained (eg QuerySet().filter(...).exclude(...) and so on), so in my implementation, every method simply returns self. So my class looks like this:

class MyQuerySet:
    ...
    def filter(self, *args, **kwargs):
        # Do some stuff and then:
        return self

This way I imitated Django's QuerySet behaviour.

However, looking at the Django code, I noticed that instead of returning self, QuerySet's methods return a cloned object every time they are called. It looks like this (removed unnecessary stuff):

class QuerySet(...):
    ...
    def filter(self, *args, **kwargs):
        clone = self._clone()
        # Do some stuff and then
        return clone

    def _clone(self,...):
        klass = self.__class__
        obj = klass(...)
        return obj

So basically, every time a method is called, QuerySet will clone itself, instantiate a new object and return it.

My question is: WHY? Is my way wrong?
My fear is that the way I do it, something might break, otherwise I can't explain why Django team did what it did.

like image 755
user1102018 Avatar asked Nov 02 '22 21:11

user1102018


1 Answers

Django does this so that the base query can be kept around and reused, without inheriting changes from a future "child" query, like your exclude() on your filter(). I'm guessing somebody tried storing queries for later, and realized that didn't work well without copying.

I cloned the django repo and did a quick git log on django/db/models/query.py, searching for the phrase clone.

The patch that introduces this change is here:

https://github.com/django/django/commit/d4a3a4b

like image 127
jpaugh Avatar answered Nov 15 '22 05:11

jpaugh