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

May 22, 2017Technology
Web Application Security: 10 Best Practices

Protection of WEB App is of paramount importance and it should be afforded the same level of security as the intellectual rights or private property. I'm going to cover how to protect your web app.

Rostyslav Stekh
Rostyslav Stekh
Apr 3, 2011Technology
Sprite cache invalidation

When we use css-sprites it's important to make browser cache them for longest period possible. On other hand, we need to refresh them when they are updated. This is especially visible when all icons are stored in single sprite. When it's outdated - entire site becomes ugly. To solve this task I've implemented this small script that adds file's hash to url:background-image: url(images/icons.png?a3844c660);

Vladimir Sidorenko
Vladimir Sidorenko
Sep 23, 2010Technology
OR and AND without django.db.models.Q

I just found out that __or__ and __and__ are defined for QuerySet. This means that to do queries union or intersection, you can do:User.objects.filter(...) | User.objects.filter(...)User.objects.filter(...) & User.objects.filter(...)

Vladimir Sidorenko
Vladimir Sidorenko
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.

Marina Sharapova
Marina Sharapova