views:

367

answers:

2

I have the following Django models: -

class Company(models.Model):
    name = models.CharField(max_length=50)
    is_active = models.BooleanField(db_index=True)

class Phase(models.Model):
    company = models.ForeignKey(Company)
    name = models.CharField(max_length=50)
    is_active = models.BooleanField(db_index=True)

class Process(models.Model):
    company = models.ForeignKey(Company)
    name = models.CharField(max_length=50)    
    phases = models.ManyToManyField(Phase, through='ProcessPhase')
    is_active = models.BooleanField(db_index=True)

class ProcessPhase(models.Model):
    process = models.ForeignKey(Process)
    phase = models.ForeignKey(Phase)
    order = models.PositiveIntegerField(help_text="At what step of your process will this phase occur?", unique=True)

A "company" has its "processes" and "phases". A process (of a company) is comprised of one or more phases (of the company). Each phase associated with a process has an "order". The requirement is that: -

  1. in a particular process of a company, a phase can appear only once;
  2. also "phase A" and "phase B" in a process cannot have the same order.

So I need to know: -

a) how to specify some "unique"s in the model definition to fulfill the above requirements;

b) what uniqueness, if any, is automatically implied by a ManyToManyField?

A: 

Shouldn't a Phase then allways belong to a Process? If that is so, you could say that the Process and order combination of a phase should be unique.

Rasmus Kaj
A phase does not necessarily "always" belong to a process. What I mean is that there might be a phase that is not (yet) associated with a process.
chefsmart
But a Phase not attached to a process will not have entries in ProcessPhase, so Rasmus's statement still stands.
Dmitry Risenberg
+3  A: 

In your case, since you say "A process (of a company) is comprised of one or more phases (of the company)", it seems like you should have a structure like:

Company <----* Process <----* Phase

Company has its Processes, Process has its Phases. It's not really a ManyToMany relation, it's OneToMany (Process has many Phases, but each Phase is connected to one Process).

If so, you should have

class Phase(models.Model):
    process = models.ForeignKey(Process, null=True) # based on your comment, if a Phase does not belong to a Process, leave it null.
    phase = models.ForeignKey(Phase)
    order = models.PositiveIntegerField(help_text="At what step of your process will this phase occur?")


    class Meta:
        unique_togather = ("process", "order")

The unique_together in Meta class is what you want, I think. It enforces both in admin and on database level the uniqueness of those 2 fields together.


edit:
(ForeignKey field can be null - see this)


based on your comment:

Don't use ManyToMany, as it auto-generates the "table-in-the-middle", while you need it specific for your needs. Instead, try defining the different model (together with your Company, Phase and Process):

class PhaseOrder(models.Model):
  process = models.ForeignKey(Process)
  phase = models.ForeignKey(Phase)
  order = models.PositiveIntegerField(help_text="At what step of your process will this phase occur?")
  class Meta:
    unique_together = (("process", "order"), ("process", "phase"))
kender
I has this structure earlier. But from the users' point of view, they create processes and phases. The phases are similar in each process, however their order might be different in different processes.
chefsmart
Then you don't need the ManyToMany field defined, just an additional table. Check my latest edit. The uniqueness is guaranteed with unique_together in Meta.
kender
If there is a project "A" and a project "B" that both have a "setup" phase, is that one setup phase shared between the projects or two setup phases, one for each project? I'd say it's probably two, as "A.setup" might be active when "B.setup" is not. Just saying that some "setup" is active is probably not very meaningfull? If so, I think kender has the right answer.
Rasmus Kaj
Your suggestion about not using the ManyToManyField seems to be the way to go. I will try it out now. The unique_together = (("process", "order"), ("process", "phase")) seems to satisfy my needs. Let me try it out.
chefsmart
In reply to Rasmus Kaj's comment above, if a phase named "setup" occurs in "process A" and also in "process B", they are to be considered independent of each other. It's like saying "setup" phase occurs at step 2 in "process A", but it occurs at step 3 in "process B". Can't avoid all that confusion ;)
chefsmart