Rob Golding

Technology Consultant

Django Model Templates (sans-Denormalisation)

I came across an interesting problem recently, while trying to model the structure of a university course in Django.

The model needed to represent the notion of a university module, which can be taught over a number of semesters and/or years, by different people, and with different students each time round. Some information remained common to each of these modules however, such as the code, name, and type of the module (single semester or full-year).

One solution that crossed my mind was to use a sort of template, and copy the common information over each time the module was taught. This would mean duplicating the common fields over a template model and the module's model, and copying the data every time a new "Module" object gets created. This just didn't sit well with me though, as I really don't like denomalising data when it's not absolutely necessary.

The solution I have settled on (for now, anyway) works something like this: I use the model template idea, but with a foreign key from the Module to it's template. Then, to access the fields on the template directly from the Module model, I use a combination of the Python property and a smart lambda function.

Here's a simplified version of the model to illustrate what I'm talking about:

class ModuleDefinition(models.Model):
    code = models.CharField(max_length=6, unique=True)
    name = models.CharField(max_length=200)
    credits = models.IntegerField(max_length=4)
    level = models.IntegerField(max_length=2)
    type = models.CharField(max_length=30, choices=MODULE_TYPE_CHOICES)
 
class Module(models.Model):
    code = property(lambda s: s._get_definition_property('code'))
    name = property(lambda s: s._get_definition_property('name'))
    credits = property(lambda s: s._get_definition_property('credits'))
    level = property(lambda s: s._get_definition_property('level'))
 
    definition = models.ForeignKey('ModuleDefinition')
    semester = models.CharField(max_length=20, choices=SEMESTER_CHOICES)
    year = models.CharField(max_length=20, choices=YEAR_CHOICES)
    convener = models.ForeignKey('auth.User', related_name='modules_convened')
    students = models.ManyToManyField('auth.User', blank=True, related_name='modules_taken')
 
    def _get_definition_property(self, property):
        return self.definition.__getattribute__(property)

In this model I've called the template a "definition", but the idea is the same. The _get_definition_property() method allows me to get a property from the parent definition programmatically, which is used in the lambda functions to add the fields from the definition to the model itself, as read-only properties. This allows me to access them as if they were a field stored on the model itself (i.e. module.code, module.name, etc.) which is handy in the templates where module.definition.code would get very old, very fast.

I'd be really interested to hear how others have tackled the same issue, as I'm sure it's not just restricted to university modules. Maybe soon I'll be making a post about how I model a timetable in Django...after all, how hard can it be!?

Oh, and if you're wondering, this code is for the MyUni project that is occupying me recently. I'm restricting myself to just thinking about how the models could work (and maybe writing little bit of code) until Rob and Ben come back to university.

Categories: Web Development, django, programming, university | Tags: , , , | RSS 2.0

  • The getattribute call would be much better written



    getattr(self.definition, property)


    (i.e. 'getattr' is the public interface for introspection, not \_\_getattribute\_\_).



    I'd be tempted to just inline it actually:



    code = property(lambda s: s.definition.code)
  • I just came from searching for a good page on Google and came to this site. I have to say these templates are nice. Thanks for sharing them in your post. I will be sure to pass this link to others.

  • I thought people might be interested in this.
  • great post as usual!
  • slafs
  • Using model inheritance, you have two options: to make the base class abstract, or not. If it is abstract, the user can only create models of the inheriting type, which it not what I wanted. If it is not abstract, the parent and child classes are linked with a OneToOne field, which means that a new ModuleDefinition would need to be created for every Module, which completely negates the point of this exercise.



    My code allows a user to create one "base" type, and then create many Modules that share the same details as specified on that base type - whereas model inheritance only allows you to create objects that share the same fields.



    Hope this clears things up a little,

    Rob

  • Luis

    Why didnt you just use model inheritance? http://docs.djangoproject.com/en/dev/topics/db/models/#id5

blog comments powered by Disqus