I blog when I go abroad, and occasionally when I do stuff in the UK too. There's a nicer interface over here.

Tuesday, September 23, 2008

Introducing django-slots

Sigh. Was it inevitable? I don't think it was, but it's happened anyway: I'm putting a purely technical post on my blog. Sorry and all that. Those of you who couldn't give a toss about python, django, coding, my job, and so forth can turn away now. Normal lack of service will resume shortly.

django-slots

This post introduces django-slots, a system for scheduling relationships between django models. It's an open-source (head-above-parapet) project which allows django developers to include time-based foreign keys in their applications/projects.

At the time of writing django-slots should be considered pretty nascent. Some reasons for this are detailed near the end. Nonetheless I believe even its current state provides enough useful functionality to justify its release.

Background and rationale

The first iteration of django-slots was a weekend pet project of mine, inspired by two things. Firstly, the team to which I belong at work were busy implementing several different solutions to what I considered a single problem: making a relationship between two objects occur for a period of time. Secondly, I believe that as a software engineer my job is to make my job easier; and as a software engineer on a CMS this mostly means that my job is to make everyone else's job easier too. This comes down to two things:
  1. Engineers should not be required to make changes happen at a particular time (and this means doing deployments etc).
  2. Users should not be using my software at times when I could really do without them calling me up saying it's broken (ie weekends, midnight, etc)
Both problems are solved by writing software which allows the future state of the data in my CMS to be scheduled, and previewed, in advance.

What django-slots is not

  • django-slots is not a system for making things appear and disappear, or exist and not exist
  • django-slots is not a tool to explicitly make something happen. It is not a replacement or wrapper for cron; nothing is ever triggered.
  • django-slots is not a replacement for foreign keys, or other normal relationships between entities
  • django-slots is not perfect or finished. By a long way.

What django-slots is

django-slots aims to provide developers with a way to satisfy the generic requirement of scheduling changes to relationships, designed with websites in mind. The most common concrete and specific example is probably to schedule a particular ad/sponsor/promotion to appear on a site between two times.

django-slots allows developers the freedom to define what "something" is through an intermediary mechanism. Unlike a normal ForeignKey, a relationship between two models exists separately from the instances of those models; the instance-instance relationship is bound to a period of time, known as a slot.

With this approach django-slots also provides a platform on which developers can build other tools to report, audit, preview, and more. A timeline of relationships means you can see the state of your data in the past, present, and future.

Furthermore, django-slots decouples models from one another, allowing them to exist and develop independently. No changes are required to the models which are scheduled, and no schema changes are involved in declaring the schedules attribute. django-slots is designed to be simple.

Finally, the mechanisms in use to implement the relationships inside django-slots are available for use by other applications. Specifically this means the definition of generic relationships between arbitrary model types on both sides (as opposed to the one-sided relationship already possible with GenericForeignKey). Where django-slots is concerned only with time, I envisage other applications in areas where similar concepts (universally identifiable points, etc) apply, eg geography.

Code

django-slots is hosted on Google Code, and there is a minimal installation guide on the wiki system which it provides.

http://code.google.com/p/django-slots/wiki/QuickstartGuide

Description

django-slots is used by telling your models to use a provided mixin class, and declaring a schedules attribute. This attribute should be a tuple of other class objects, which must be other django models.

By setting up your model like this you are declaring that a relationship can exist between it and those in the tuple. Your model is extended with properties and methods for querying and managing instances of these relationships. You can then schedule a relationship to exist, retrieve the current relationship or that for a given time, and retrieve a timeline of all relationships between your instance and instances of the other models.

API/usage


# models.py
from django.db import models
from slots import ScheduleMixin

class Style(models.Model):
# define your style model here
...

class Page(ScheduleMixin, models.Model):
# Style is the foreign key which varies according to time.
# NB. you don't need a default it it makes no sense to have one
...
default_style = models.ForeignKey(Style)
schedules = (Style,)

# views.py
def detail(request,...):
page = Page.objects.all()[0]
# the Style scheduled for right now, if there is one
style = page.current_for_model('Style')
if style is None:
style = page.default_style
# do stuff with style
...

# properties
# dictionary of schedules keyed by model,
# each entry is an array of slots ordered by time
page.schedule
# dictionary of all objects (or None) currently
# scheduled, keyed by model name.
page.current
# returns next scheduled objects (ie, where start time is
# later than right now) in same format as current
page.next
# returns last scheduled objects (ie, where end time is
# earlier than right now) in same format as current
# per-type query methods
page.last
# just the array of slots for Style
page.schedule_for_model('Style')
# the Style object currently scheduled, or None
page.current_for_model('Style')
# the Style object scheduled next, or None
page.next_for_model('Style')
# the Style object which most recently finished, or None
page.last_for_model('Style')
# finding what's scheduled at a particular time.
# NB this only works on a per-relationship basis;
# you cannot pass a datetime object to page.current()
page.current_for_model('Style',jan_1st)
# scheduling an object
page.add_to_schedule(style_object, start_datetime, end_datetime,
notes)
# a signal catches this and deletes all relevant slots
style_object.delete()

What's missing

As mentioned above django-slots is by no means complete. To my mind there are a few fairly crucial missing pieces right now:
  1. Removal (descheduling) of individual slots
  2. An admin interface.
  3. A test suite.
And there are bound to be far, far more. Hopefully such holes will be filled; better yet, hopefully others will (help) fill them.

Colophon

django-slots should work in any out of the box django installation, though it was written alongside django 1.0. The only configuration requirement is that django.contrib.contenttypes is in INSTALLED_APPS (this is the default).

1 comment:

Cyril Doussin said...

Good work guys, this will be really useful stuff.
One thing: I'd put all mixin properties under the same object, eg. page.schedule.items instead of page.schedule, page.schedule.current instead of page.current etc.
I'm just thinking many people would potentially have conflicting names for their model properties (a "current" property on a model for example is not uncommon).
Looking forward to playing with it!