Back
Feb 18, 2010

User profiles with inheritance in Django

Vladimir Sidorenko
Vladimir Sidorenko

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.

More thoughts

Jun 14, 2017Technology
How to Deploy a Django Application on Heroku?

In this article I'll show you how to deploy Django with Celery and Postgres to Heroku.

Vladimir Kalyuzhny
Vladimir Kalyuzhny
Nov 6, 2021Technology
Top 5 Restful API Design Best Practices

Learn about RESTful API design best practices with helpful developer tips

Vladimir Sidorenko
Vladimir Sidorenko
Jan 12, 2017Technology
Making Custom Report Tables Using AngularJS and Django

In this article I will tell you how to create an interactive interface with a widely customized visual look and different filtering to view reports.

Pasha Volkov
Pasha Volkov
Feb 12, 2020Technology
5 Best Payment Gateways For 2020

We reviewed the best payment gateways in 2020. Here’s our comparison of their features, advantages, and disadvantages.

Vladimir Sidorenko
Vladimir Sidorenko
Jan 28, 2017Technology
Creating a site preview like in slack (using aiohttp)

In this article we will write a small library for pulling metadata and creating a preview for a site just like Slack does.

Igor Tokarev
Igor Tokarev
Oct 3, 2016Technology
How to include JQuery plugins in Angular 2 running via webpack

Learn more about how to include jquery plugins in angular 2 running via webpack. Our tutorial is perfect for Angular beginners.

Anton Lysenkov
Anton Lysenkov