django / django/new-features

Make `get_or_create`'s arguments keyword-only

Open
#45 2 comments 20 reactions 0 assignees View on GitHub
Django Core Models/ORM
Dominant language
No language data
Stars
188
Forks
7
PR merge metrics
No merged PRs in 30d

Description

### Code of Conduct

- [x] I agree to follow Django's Code of Conduct

### Feature Description

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

```python
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.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.