Skip to content

Instantly share code, notes, and snippets.

@pawl
Last active February 14, 2020 03:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pawl/74c734a1da1758b021b9f1c69b84397e to your computer and use it in GitHub Desktop.
Save pawl/74c734a1da1758b021b9f1c69b84397e to your computer and use it in GitHub Desktop.
Django's biggest gotcha?

In my opinion, one of django's biggest gotchas is using RelatedManager.set with models that have non-nullable ForeignKey fields.

Example

Models.py (with a one-to-many relationship and a non-nullable ForeignKey):

class Reporter(models.Model):
    name = models.CharField(max_length=255)


class Article(models.Model):
    title = models.CharField(max_length=255)

    reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)

Guess how many articles are on the reporter after you do this?:

reporter = Reporter.objects.create(name="paul")

# add 2 articles to the reporter
article_1 = reporter.article_set.create(title="blah")
reporter.article_set.create(title="blah2")

# set only 1 article on the reporter
reporter.article_set.set([article_1])

I expected just one article to be on the reporter. Nope:

reporter.article_set.count()
2

How do the docs say it should work?

This method accepts a clear argument to control how to perform the operation. If False (the default), the elements missing from the new set are removed using remove() and only the new ones are added.

Well, crap, what's going on?

It turns out you need to read the docs for RelatedManager.remove for a hint:

For ForeignKey objects, this method only exists if null=True.

So, it turns out that set() is using remove() and remove() is only available on models with ForeignKeys that are null=True. What does set() do with things it needs to remove() when remove() isn't available? Nothing...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment