Back
Feb 18, 2010

User profiles with inheritance in Django

Usually users' profiles are stored in single model. When there are multiple user types, separation is made by some field like user_type.

Situation is a little more complicated when different data is needed for each user type.

Of course it's better when all users have same type and are granted different permissions. But since we got task to have separate user types - we'll have to solve it.

Simple solution

The most obvious solution is to make base profile with type identifier, which will pull needed model instance:

request.user.get_profile().get_final_profile()

But I don't really like this solution. Firstly, get_final_profile method is basically a big switch-case. Secondly - we must not forget to call extra method.

I wanted to simply write request.user.profile ans get needed profile instance. This is possible by combining two known tricks - Upcast and AutoOneToOneField.

Upcast

Allows to get sublass instance from base class instance.

AutoOneToOneField

Allows creating related objects on request. This field is not needed per se, only idea. Taking AutoOneToOneField as a base, it's easy to create a new one that calls upcast when related objects is requested.

Solution

class UpCastModel(models.Model):
    """
    Base class for models that are ment to be inherited.
    Introduces upcast method that returns child instance.
    """
    final_type = models.ForeignKey(ContentType, editable=False)

    def save(self, args, **kwargs):
        if not self.pk:
            self.final_type = ContentType.objects.get_for_model(type(self))
        super(UpCastModel, self).save(args, **kwargs)

    def upcast(self):
        if not hasattr(self, '_upcast'):
            if self.final_type.model_class == self.class:
                self._upcast = self
            else:
                self._upcast = self.final_type.get_object_for_this_type(id=self.pk)
        return self._upcast

    class Meta:
        abstract = True


class UpCastSingleRelatedObjectDescriptor(SingleRelatedObjectDescriptor):

    def get(self, instance, instance_type=None):
        parent = super(
            UpCastSingleRelatedObjectDescriptor, self
        ).get(instance, instance_type)
        return parent.upcast()


class UpCastOneToOneField(models.OneToOneField):
    """
    UpCastOneToOneField gets child of related object.
    """
    def contribute_to_related_class(self, cls, related):
        setattr(
            cls, related.get_accessor_name(),
            UpCastSingleRelatedObjectDescriptor(related)
        )

Now profile looks like this:

class BaseProfile(UpcastModel):
    user = UpCastOneToOneField(User)


class BoyProfile(BaseProfile):
    pass


class GirlProfile(CitizenProfile):
    pass

In order to get users, profile, we just need to write requst.user.profile.

Subscribe for the news and updates

More thoughts
Dec 22, 2024Technology
Python and the Point Rush in DeFi

This article demonstrates how to use Python to automate yield calculations in decentralized finance (DeFi), focusing on the Renzo and Pendle platforms. It guides readers through estimating potential rewards based on factors like token prices, liquidity, and reward distribution rules, emphasizing the importance of regular data updates and informed decision-making in DeFi investments.

Sep 1, 2021TechnologyBusiness
Top 10 Web Development Frameworks in 2021 - 2022

We have reviewed the top web frameworks for server and client-side development and compared their pros and cons. Find out which one can be a great fit for your next project.

Sep 21, 2020Technology
How to Optimize Django ORM Queries

Django ORM is a very abstract and flexible API. But if you do not know exactly how it works, you will likely end up with slow and heavy views, if you have not already. So, this article provides practical solutions to N+1 and high loading time issues. For clarity, I will create a simple view that demonstrates common ORM query problems and shows frequently used practices.

May 26, 2017Technology
Tutorial: Django User Registration and Authentication

In this beginners friends article I'll explain how to make authentication with Google account on your Django site and how to make authentication for you REST API.

Oct 11, 2010Technology
Char search in Emacs as in Vim

In VIM there is a command for char search: f. After first use it can be repeated with ;. I like to navigate in line with it. You see that you need to go to bracket in a middle of a line - you press f( and one-two ; and you are there. There's no such command in Emacs, so I had to write my own. I've managed even to implement repetition with ;.

Mar 6, 2010Technology
Ajax form validation

There was a task to submit form with ajax, with server side validation of course. Obvious solution is to do validation and return json with erros. I didn't like idea of writing separate view for validation and then inserting errors in form html on client side. Especially since I already had a generic template for django form with errors display. In this article I'll describe how I solved the task.