1. Yaroslav Kliuiev
  2. django-model-helpers



Django modelhelpers


This reusable package contains some helpers to help automate pre_save model fields processing and helpers to create 'generic' views

Model 'Mixins'



Name for this mixin is strange, please suggest alternative

This mixin allows customizing model's detail_view and list_view templates and context from model code


  1. Add "modelhelpers" to settings.INSTALLED_APPS.

  2. Then add this mixin when subclassing Django's models.Model to be able customizing template and context generation from model. Example:

    from django.db import models
    from modelhelpers.mixins import ModelConfiguredViews
    class Movie(models.Model, ModelConfiguredViews):
        DETAILS_TEMPLATE = "movies/movie_details.html"
        LIST_TEMPLATE = "movies/movie_list.html"
        title = models.CharField(max_length=100, unique=True)
        slug = models.SlugField(
            _("Name to be used in urls"), max_length=100, unique=True, blank=True)
        # ...
        def details_prepare_context(self, request, *args, **kwargs):
            if self.owner == request.user:
                return {"is_my_film": True}
            return {"is_my_film": False}

After this your model will have get_absolute_url methods both for class and for model instances.

For instance calls:

This url will point to modelhelpers.view.details_view which will use template DETAILS_TEMPLATE and template context will be build from this dict:

{"instance": Movie.objects.get(slug=slug),
 "is_my_film": True_or_False}

For class methods it will use another template and will contain "queryset" in context

You can inspect code of ModelConfiguredViews for other methods and settings. Code of this mixin is small and easy to understand and it contains docstrings but they are not really needed. It can be used for models without slug or with slugfield with custom name. You can even override methods *_get_template to get templates based on request.


I know that this is not MVC-friendly (in Django terms it should be named Model-Template-View, MTV <=> MVC) but it's sometimes useful to have model-related code in model. You can assume that Controller is "separately" placed in model's method.

Auto calling models pre_save/post_init methods

modelhelpers app subscribes two receivers for all models' post_init and presave signals. This receivers just check if model has post_init/pre_save method and calls them


This mixin allows processing fields before save() just by defining ModelClass.process_FOO methods.


This mixin adds post_init, pre_save methods to model, so if you want to define your versions of this methods then please call super(YourModel, self).post_FOO()

This method receives these arguments:
  • old_value — value of field after init.
  • new_value — value of field before save.
  • changedold_value != new_value.
  • old_values — dict that contains other post_init values of fields. Only stores values for fields that are processed (when model has process_FOO method). All field values can be stored if you set ModelClass.REMEMBER_ALL to True


from modelhelpers.mixins import AutoProcessFieldsMixin

class NewsItem(models.Model, AutoProcessFieldsMixin):
    WORD_DELIMITERS = (u" ", u"," u".")

    creation_time = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(
       _("Name to be used in urls"), max_length=100, unique=True, blank=True,
       help_text=_("Auto populated"))

   full_text = models.TextField()
   short_text = models.TextField(blank=True, help_text=_("Auto populated"))

   def get_short_text(cls, text):
       shorter = text[:cls.SHORT_TEXT_LENGTH]
       sane_limit = int(cls.SHORT_TEXT_LENGTH / 3)
       for char in cls.WORD_DELIMITERS:
           new_limit = shorter.rfind(char)
           if new_limit >= sane_limit:
               sane_limit = new_limit
       short_text = shorter[:sane_limit]
       if short_text != text:
           short_text = u"%s…" % short_text
       return short_text

   def process_full_text(self, old_value, new_value, changed, *args, **kwargs):
       if changed or not self.pk or not self.short_text:
           self.short_text = NewsItem.get_short_text(self.full_text)

   def process_creation_time(self, old_value, new_value, changed, *args, **kwargs):
       if changed or not self.pk or not self.slug:
           if not self.creation_time:
               self.creation_time = timezone.now()
           slug_value = "%s-%s" % (self.creation_time.strftime(u"%Y-%m-%d"), self.title)
           self.slug = unique_slug(NewsItem, slug_value)

   def process_title(self, old_value, new_value, changed, old_values):
       old_creation_time = old_values['creation_time']
       if changed and old_creation_time == self.creation_time:
           # force slug recalc only when creation time is not changed
           self.process_creation_time(old_creation_time, self.creation_time,changed, old_values)


get_slugify_mixin(slug_field='slug', source_field='title')

This function returns subclass AutoProcessFieldsMixin with method named ('process_%s' % source_field) that sets slug_field to slugified value of source_field


from django.db import models
from modelhelpers.mixins import get_slugify_mixin

class Movie(models.Model, get_slugify_mixin('named_url', 'name')):
    name = models.CharField(max_length=100, unique=True)
    named_url = models.SlugField(
        _("Name to be used in urls"), max_length=100, unique=True, blank=True)

There is one 'pregenerated slugifier mixin class:

TitleSlugifyMixin = get_slugify_mixin()



multi_method(class_method, instance_method)

Return different objects for class attribute access and instance attribute access.


from modelhelpers.utils import multi_method

class Foo(object):
    def inst_bar(self):
        print('instance method')

    def cls_bar(cls):
        print('class method')

    bar = multi_method(cls_bar, inst_bar)

Foo.bar()  # prints 'class method'
foo = Foo()
foo.bar()  # prints 'instance method'


unique_slug(model, text_value, slug_field='slug')

Returns slugified text_value that is unique between instances of model for field slug_field. Also handles Russian text (using pytils module)



dummy_list_or_details(request, app_label, model_name, is_list=False, slug=None,pk=None, *args, **kwargs)

For model model_name from app app_label this view gets instance of model or QuerySet.all() (when is_list == True). Then it get's additional context dict from model.prepare_context(request, *args, **kwargs) and template from model.get_template(request, *args, **kwargs).


list_view(request, app_label, model_name, *args, **kwargs)

Just calls dummy_list_or_details(...) with is_list=True


details_view(request, app_label, model_name, slug=None, pk=None, *args, **kwargs)

Just calls dummy_list_or_details(...) with is_list=False