views:

216

answers:

3

I'm modeling a database relationship in django, and I'd like to have other opinions. The relationship is kind of a two-to-many relationship. For example, a patient can have two physicians: an attending and a primary. A physician obviously has many patients.

The application does need to know which one is which; further, there are cases where an attending physician of one patient can be the primary of another. Lastly, both attending and primary are often the same.

At first, I was thinking two foreign keys from the patient table into the physician table. However, I think django disallows this. Additionally, on second thought, this is really a many(two)-to-many relationship.

Therefore, how can I model this relationship with django while maintaining the physician type as it pertains to a patient? Perhaps I will need to store the physician type on the many-to-many association table?

Thanks, Pete

A: 

I agree with your conclusion. I would store the physician type in the many-to-many linking table.

Joel Cunningham
+1  A: 

Consider using a many-to-many join table. Use application logic to prevent more than two physicians per patient.

Physician
    Physician_ID
    ...

Patient
    Patient_ID
    ...

Physician_Patient
    Physician_ID int not null
    Patient_ID int not null
    Type ENUM ('Primary', 'Attending')
    PRIMARY KEY (Physician_ID, Patient_ID)
    KEY (Patient_ID)
gahooa
You may be able to even put a database-level constraint in, too. Depending upon your DBMS.
Matthew Schinckel
Django's ORM layer doesn't have a model that represents a join table. Join tables are created automatically, and extra columns can't be added to them. You can create a model with ForeignKey fields that can conceptually represent a join table, and add extra fields to that, but it would be a bit unwieldy in the face of other solutions.
mipadi
Sure it does. http://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships I don't see any reason to use an M2M relationship here, though. A regular foreign key is just fine.
Glenn Maynard
As I said: "You can create a model with ForeignKey fields that can conceptually represent a join table, and add extra fields to that, but it would be a bit unwieldy in the face of other solutions." :)
mipadi
+8  A: 

How about something like this:

class Patient(models.Model):
    primary_physician = models.ForeignKey('Physician', related_name='primary_patients')
    attending_physicial = models.ForeignKey('Physician', related_name='attending_patients')

This allows you to have two foreign keys to the same model; the Physician model will also have fields called primary_patients and attending_patients.

mipadi
Hmm, this is interesting. I'm glad that I asked. django did suggest using related_name in error.
slypete
Yes -- it's because, in absence of "related_name", Physician would acquire a "patient_set" reverse relation. But this causes an error because both fields would try to create a "patient_set" reverse relation. If you have a foreign key to the same model, you have to specify "related_name" to avoid this issue.
mipadi
This will lead to duplicates while retrieving a physician's patient list. The application will need to take the union.
slypete
Do you see that as a downside to this approach?
slypete
Maybe, maybe not. I think's is probably the _best_ way, using Django's ORM layer. You can always turn the results into a set and take the union; or if performance is an issue, you can always write custom SQL queries, I suppose.
mipadi
Better yet, you could probably create a custom manager for the Physician model (call it "patients") with a method like "unique" that would return both types of patients, without duplicates. You could even use custom SQL in the manager, if need be. http://docs.djangoproject.com/en/dev/topics/db/managers/#topics-db-managers
mipadi
Just say Patient.objects.filter(Q(primary_physician=a_physician) | Q(attending_physicial=a_physician)). I think this model is the *best* way, even from a raw schema perspective; it models the relationship exactly and naturally. ("Q" is new to 1.1, which isn't out yet, but I wouldn't go near 1.0; it's missing too much stuff. I've found Django trunk to be very stable, at least currently.)
Glenn Maynard
Glenn, it's funny you mention this because they've just released 1.1rc tonight! Thanks for the advice since I'm new to django and python.I agree with you that mipadi offers the best solution.
slypete
@Glenn: Django has Q-objects since at least version 0.96 (http://www.djangoproject.com/documentation/0.96/db-api/#complex-lookups-with-q-objects). You're probably thinking of F-objects, which are new in Django 1.1 (http://docs.djangoproject.com/en/dev/topics/db/queries/#filters-can-reference-fields-on-the-model)
piquadrat
@ Glenn: Thanks -- forgot about Q objects.
mipadi