49

Any insight into the reasoning behind this design decision? It seems to me that having obj.save() return something, has only benefits (like method chaining) and no drawbacks.

pseudosudo
  • 6,270
  • 9
  • 40
  • 53

3 Answers3

73

It's generally considered good practice in Python to have functions that primarily affect existing objects not return themselves. For instance, sorted(yourlist) returns a sorted list but yourlist.sort() sorts the list in-place and does not return anything.

Performing multiple operations with side-effects (as opposed to no-side-effect functions where the focus is on the return value) on a single line is not really good practice. The code will be more compact in terms of number of lines, but it will be harder to read because important side-effects may be buried in the middle of a chain. If you want to use method chaining, use functions with no side effects in the beginning of the chain and then have a single function with a side effect like .save() at the end.

To put it another way, in a method chain, the beginning of the chain is the input, the middle of the chain transforms the input (navigating down a tree, sorting the input, changing case of a string etc) and the end of the chain is the functional part that does work with side-effects. If you bury methods with side-effects in the middle of the chain then it will be unclear what your method chain actually does.

James McMahon
  • 48,506
  • 64
  • 207
  • 283
Andrew Gorcester
  • 19,595
  • 7
  • 57
  • 73
  • actualy save() returns the saved object , maybe it is new on django 1.5 ? – maazza Apr 17 '13 at 10:38
  • 3
    @maazza I just tried this on Django 1.5.1 and save() still returns None. [The source code for the latest dev version](https://github.com/django/django/blob/master/django/db/models/base.py#L492) also doesn't appear to return anything. – Andrew Gorcester Apr 17 '13 at 17:12
  • true , i used a modelform http://stackoverflow.com/questions/7428245/model-form-save-get-the-saved-object – maazza Apr 17 '13 at 19:35
  • 1
    @maazza Oh, I see. I feel that is a different case in that you are calling .save() on a form and it is returning a different object, not itself. FWIW I think it would be cleaner if ModelForm did not have a .save() and instead had a method to instantiate an object in memory without side-effects, just like User() instantiates a User object. Then you can call .save() on that. But that is just my personal opinion, and there may be other issues I haven't considered that preclude doing that. – Andrew Gorcester Apr 17 '13 at 20:06
  • This answer is probably best, but if you want to return an object, just use `.save(commit=False)`. https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method – trpt4him Dec 12 '14 at 21:32
8

This reminds me of the general principle that Greg Ward espoused at Pycon2015 recently, not to confuse functions with procedures. Every function should return a value or have a side-effect, but not both.

Basically the same question is asked of dict.update().

Community
  • 1
  • 1
John Lehmann
  • 7,975
  • 4
  • 58
  • 71
5

Since this is the first result that I get when searching for "django return saved object", to compliment Andrew's answer, if you still want to return the saved object, instead of using:

ExampleModel(title=title).save()

which returns None, you'd use:

saved_instance = ExampleModel.objects.create(title=title)

And this works because ExampleModel.objects is a Model Manager rather than an instance of the class, so it's not returning itself.

Amr Awad
  • 149
  • 1
  • 3