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()
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’
There’s a simpler way to achieve the same end: a model
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.
classproperty wrapper is provided,
to mimic a custom
Queryset without calling it first.
@classproperty def in_stock(cls): return cls.objects.filter(F.quantity > 0)
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.
@property 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)