[Answered ]-How to handle multiple profiles per user?

2👍

First of all you could make things a lot more easy by not declaring the BaseProfile abstract:

from django.db import models
from django.contrib.auth.models import User, UserManager

class Person(User):
    public_name = models.CharField(max_length=24, default="Mr. T")
    objects = UserManager()

    def save(self):
        self.set_password(self.password)
        super(Person, self).save()

    def _getDefaultProfile(self):
        try:
            return self.baseprofile_set.get(default=True)
        except ObjectDoesNotExist:
            return False
    profile = property(_getDefaultProfile)

    def _getProfiles(self):
        return self.baseprofile_set.all()
    profiles = property(_getProfiles)

class BaseProfile(models.Model):

    person = models.ForeignKey(Person)
    is_default = models.BooleanField(default=False)    

class Teacher(BaseProfile):
    user_type = models.CharField(max_length=7, default="teacher")    

class Student(BaseProfile):
    user_type = models.CharField(max_length=7, default="student")    

class Parent(BaseProfile):
    user_type = models.CharField(max_length=7, default="parent")

The way this is nicer? Your properties didn’t know anyway what type they were returning, so the abstract baseclass only made you have an incredible annoying overhead there.

If you now are wondering how the hell you can get the data from the specific profiles since I made anything returned BaseProfile? You can do something like this:

try:
    #note the lowercase teacher referal
    print myuser.profile.teacher.someteacherfield 
except Teacher.DoesNotExist:
    print "this is not a teacher object!"

Also I do hope you didn’t use the user_type field solely for this purpose, because django has it built in better as you can see. I also hope you really have some other unique fields in your derived profile classes because otherwise you should throw them away and just past a usertype field into BaseProfile (look at choices to do this good).

Now as for the is_default, imho this method is as good as any. You can always try to add custom constraints to your dbms itself, saying there sould be 0 or 1 records containing the same FK and is_default=True (there is no django way to do this). What I also would say is, add a method make_default and in that method make sure the is_default is unique for that person (e.g. by first setting is_default to False on all profiles with the same FK). This will save you a lot of possible sorrow. You can also add this check in the save() method of BaseProfile.

Another way you could do it is by adding a Foreign Key to the Person Model that points to the default Profile. While this will ensure default to be unique on django level, it can also provide denormalization and corruption of your data, even on a more annoying level, so I’m no big fan of it. But again, if you do all adding/removing/updating of profiles through predefined methods (will be more complex now!) you should be safe.

Finally, maybe you have good reasons to inherit from User, but the default way to extend the User functionality is not this, it’s described here.

Leave a comment