An example Model used in the tests.

from django.db import models
from model_values import F, Manager, classproperty

class Book(models.Model):
    title = models.TextField()
    author = models.CharField(max_length=50)
    quantity = models.IntegerField()
    last_modified = models.DateTimeField(auto_now=True)

    objects = Manager()

Table logic

Django recommends model methods for row-level functionality, and custom managers for table-level functionality. That’s fine if the custom managers are reused across models, but often they’re just custom filters, and specific to a model. As evidenced by django-model-utils’ QueryManager and PassThroughManager.

There’s a simpler way to achieve the same end: a model classmethod. In some cases a profileration of classmethods is an anti-pattern, but in this case functions won’t suffice. It’s Django that attached the Manager instance to a class.

Additionally a classproperty wrapper is provided, to mimic a custom Manager or Queryset without calling it first.

    def in_stock(cls):
        return cls.objects.filter(F.quantity > 0)

Row logic

Some of the below methods may be added to a model mixin in the future. It’s a delicate balance, as the goal is to not encourage object usage. However, sometimes having an object already is inevitable, so it’s still worth considering best practices given that situation.

Providing wrappers for any manager method that’s pk-based may be worthwhile, particularly a filter to match only the object.

    def object(self):
        return type(self).objects[self.pk]

From there one can easily imagine other useful extensions.

    def changed(self, **kwargs):
        return type(self).objects.changed(self.pk, **kwargs)

    def update(self, **kwargs):
        for name in kwargs:
            setattr(self, name, kwargs[name])
        return self.object.update(**kwargs)