views:

70

answers:

1

I have a model where tasks are pieces of work that each may depend on some number of other tasks to complete before it can start. Tasks are grouped into jobs, and I want to disallow dependencies between jobs. This is the relevant subset of my model:

class Job(models.Model):
    name = models.CharField(max_length=60, unique=True)

class Task(models.Model):
    job = models.ForeignKey(Job)
    prerequisites = models.ManyToManyField(
        'self',
        symmetrical=False,
        related_name="dependents",
        blank=True)

Is there any way I can express the constraint that all prerequisite tasks must have the same job? I could enforce this at the view level, but I would really like to get it to work at the model level so that the admin interface will display appropriate options when choosing prerequisites for a task. I thought I could use "limit_choices_to", but on closer inspection it seems to require a static query, not something dependent on the values in this task object.

+1  A: 

There are two separate issues here.

If you want to enforce this constraint at the model level, you might have to define an explicit "through" model and override its save() method (you can't just override Task.save() as that isn't necessarily invoked for adding entries to an M2M). Django 1.2 will have a fuller model validation framework, more like form validation.

If you want only certain choices to appear in the admin, that's a form-level issue. You can dynamically set the queryset attribute of the ModelMultipleChoiceField in a form's init method:

class TaskForm(forms.ModelForm):
   class Meta:
      model = Task

   def __init__(self, *args, **kwargs):
      super(TaskForm, self).__init__(*args, **kwargs)
      self.fields['prerequisites'].queryset = Task.objects.filter(job=self.instance.job)

You may need to introduce some additional checking here to handle the case of creating a new Task (in that case "self.instance.job" will likely be None); what set of available prerequisites you want there is not clearly defined, since a new Task doesn't yet have a job.

Carl Meyer
Is the task's save method invoked when creating a many-to-many relation? I assumed that because it had its own behind-the-scenes table it wouldn't involve task.save().I think the form-level solution is what I need for now. I haven't delved into forms yet, but I'll try it out when I get a chance and come back and accept this answer if it works.
Weeble
Ah, you're right that overriding save() isn't adequate in the case of a M2M field. There'd be a way to do it, but it might require defining an explicit "through" model and overriding its save() method. I'll edit that part of my answer.
Carl Meyer