David
David

Reputation: 151

Django: How to create a mixin to set common attributes for every model?

I have a model mixin that sets created_at, created_by, updated_at and updated_by which I then inherit to most of the models in my project. This model mixin works fine. Obviously setting created_at and modified_at is very easy due to auto_now_add and auto_now.

class Timestampable(models.Model):
    created_at = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
        verbose_name=_('created at')
    )
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="created%(app_label)s_%(class)s_related", on_delete=models.SET_NULL)
    updated_at = models.DateTimeField(
        auto_now=True,
        db_index=True,
        verbose_name=_('updated at')
    )
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="updated%(app_label)s_%(class)s_related", on_delete=models.SET_NULL)


    class Meta:
        abstract = True
        ordering = ['-created_at']

But, what I want is to also create a mixin (or maybe subclass) CreateView and UpdateView to set created_by and updated_by to be self.request.user everywhere those CBVs are used ( I would guess by modifying get_form() or form_valid()). I'll also need to create a similar admin mixin to modify save_model().

I have never created a custom mixin and the things I have found/tried aren't working. For example I got subclassing CreateView working by modifying get_form() but then, I couldn't further modify get_form() in the various class ModelXCreate(...) views I created.

Does anybody know how I can achieve this? It'd be super useful to be able to have such a mixin and keep things DRY.

Upvotes: 2

Views: 697

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477513

You can override the form_valid method and thus work with:

from django.contrib.auth.mixins import LoginRequiredMixin

class CreateUpdateMixin(LoginRequiredMixin):

    def form_valid(self, form):
        instance = form.instance
        if instance.pk is None:  # created
            instance.created_by = instance.updated_by = self.request.user
        else:  # updated
            instance.updated_by = self.request.user
        return super().form_valid(form)

Then we can use this mixin for our CreateView/UpdateView:

class MyCreateView(CreateUpdateMixin, CreateView):
    # …
    pass

class MyUpdateView(CreateUpdateMixin, UpdateView):
    # …
    pass

Upvotes: 1

Related Questions