Skip to content

Make get_or_create's arguments keyword-only #45

Open
@mscheper

Description

@mscheper

Code of Conduct

  • I agree to follow Django's Code of Conduct

Feature Description

To save developer confusion, change the arguments for get_or_create, like this:

def get_or_create(self, *, defaults=None, **kwargs)

Although this would technically create backward-compatibility issues, I would argue that, in practice, these would be very rare and easily fixed.

Alternatively, deprecate this function and create a new one, perhaps create_or_get, that works this way.

Problem

When I unwittingly neglected to unpack a dict as arguments for get_or_create, the result was a MultipleObjectsReturned exception. While this was clearly a mistake (which I made at 1am today), the exception was misleading. It set me off on a wild goose chase, trying to work out how multiple objects matching the arguments could possibly exist.

The reason was that defaults is a positional argument. This swallowed the dict I passed, and caused a zero-argument call to get, which raised the exception.

The dict was actually locals(), which I used to pass the arguments for a service module function to a one-liner get_or_create—I just forgot to put ** in front of it. It's not the first time I've passed function arguments on that way (DRY and all that), and it's certainly not an unheard-of technique in Python.

OTOH, in a lot of Django code, and indeed the Django docs, I've only ever seen defaults passed by keyword, after criteria arguments. And while a zero-argument get() make sense for single-row tables, it's hard to imagine any developer positionally passing defaults to get_or_create.

Request or proposal

proposal

Additional Details

While not strictly backwards compatible, I believe the developer confusion saved would greatly outweigh the minor and unlikely hassle that one or two devs may experience from this change. As I said, it's hard to imagine it actually breaking any existing code, and even if it did, it would cause an easy-to-fix NameError or TypeError: just type 'defaults=' . But it would likely save developers getting confused by MultipleObjectsReturned for the same reason I was.

Implementation Suggestions

Just add the three characters to the function definition. Alternatively, deprecate get_or_create, and create a different method, as in the feature description.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Idea

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions