boa-restrictor

Contents:

  • Disclaimer
  • Configuration
  • Python rules
  • Django rules
    • Prohibit usage of TestCase.assertRaises() (DBR001)
    • Don’t import “django.db” in the view layer (DBR002)
    • Prohibit usage of “assertTrue” and “assertFalse” in Django unittests (DBR003)
    • Prohibit usage of “datetime.now” (DBR004)
    • Don’t import “django.db” in the API layer (DBR005)
    • Avoid using old tuple-based Django model choices (DBR006)
    • CharField must have max_length set (DBR007)
    • Relational model fields must declare an explicit “related_name” (DBR008)
  • Changelog
boa-restrictor
  • Django rules
  • Relational model fields must declare an explicit “related_name” (DBR008)
  • View page source

Relational model fields must declare an explicit “related_name” (DBR008)

This rule ensures that relational model fields (ForeignKey, OneToOneField, ManyToManyField) declare an explicit related_name.

Relying on the auto-generated default (<model>_set) makes reverse accessors implicit: they are easy to overlook, collide when two relations point at the same model, and silently break when a model is renamed. An explicit related_name documents the reverse relation and keeps it stable. To intentionally disable the reverse relation, pass related_name="+" — that counts as explicit and is accepted.

Models whose effective Meta declares a default_related_name are exempt: Django then derives an explicit reverse accessor for every relation on that model, which is exactly what this rule enforces. The Meta is resolved through inheritance as far as the current file allows — abstract base models and base Meta classes defined in the same file are honoured (including class Meta(Parent.Meta) and multi-level chains).

Because the linter processes one file at a time, a default_related_name inherited from a base defined in another file cannot be seen and the relation will still be flagged. Silence those with # noqa: DBR008.

Files inside a migrations/ directory are exempt, since they are generated.

Wrong:

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

Correct:

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="books")

Also correct (exempted via ``Meta.default_related_name``, including inheritance within the same file):

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

    class Meta:
        default_related_name = "books"


class CommonInfo(models.Model):
    class Meta:
        abstract = True
        default_related_name = "objects_set"


class Review(CommonInfo):
    # Inherits default_related_name from the abstract base above — not flagged.
    book = models.ForeignKey(Book, on_delete=models.CASCADE)
Previous Next

© Copyright 2026, Beyonder Deutschland.

Built with Sphinx using a theme provided by Read the Docs.