tags:

views:

56

answers:

4

In Django I have theses models:

class Artist(models.Model):
    name = models.CharField(max_length=128)
    born_place = models.ForeignKey(???)
    dead_place = models.ForeignKey(???)
    live_places = models.ManyToManyField(???)
    work_places = models.ManyToManyField(???)

class Country(models.Model):
    iso = models.CharField(max_length=2, primary_key=True)
    name = models.CharField(max_length=50)

class Region(models.Model):
    iso = models.CharField(max_length=2, blank=True)
    name = models.CharField(max_length=150)
    country = models.ForeignKey('Country')

class City(models.Model):
    name = models.CharField(max_length=150)
    region = models.ForeignKey('Region')

All the places (born_place, dead_place, live_places, work_places) can be a City or a Region or a Country. And a City should necessarily have a Region, and a Region should necessarily have a Country.

How can I achieve that?

+1  A: 

All the places (born_place, dead_place, live_places, work_places) can be a City or a Region or a Country.

What you need in this case is a generic foreign key. In Django this can be achieved through the handy contenttypes framework.

From the documentation:

There are three parts to setting up a GenericForeignKey:

  1. Give your model a ForeignKey to ContentType.

  2. Give your model a field that can store a primary-key value from the models you'll be relating to. (For most models, this means an IntegerField or PositiveIntegerField.) This field must be of the same type as the primary key of the models that will be involved in the generic relation. For example, if you use IntegerField, you won't be able to form a generic relation with a model that uses a CharField as a primary key.

  3. Give your model a GenericForeignKey, and pass it the names of the two fields described above. If these fields are named "content_type" and "object_id", you can omit this -- those are the default field names GenericForeignKey will look for.

Manoj Govindan
Also an interesting solution. I need to play with it. But my feeling is that generic foreignkey is more suited to work the other way around (like in the tag example, you add Tags to many models). In my case, if I understand correctly, I will add Artists to Country or Region or City. I think it will work but it look less elegant to me. But I can be wrong.
Etienne
A: 

Take a look at Django's Generic Relations here and here, which will let you key to various types of object, rather than only a specific one.

stevejalim
+1  A: 

well.. yea maybe you could use generic relations. But i think that you could solve you some problems by thinking outside the box.

i'd say that you could create a Territory model instead of Country,Region and City; and then use a recursive relationship

class Territory(models.Model):
   iso = models.CharField(max_length=2, blank=True)
   name = models.CharField(max_length=150)
   parent = models.ForeignKey('self')

this will create a hierarchy structure for your territory, so now you could use as many divisions as you like (for exemple, Continents, Planets,Community) and you wont have to change your models. As for the Artist, you could do something like:

class Artist(models.Model):
    name = models.CharField(max_length=128)
    born_place = models.ForeignKey(Territory)
    dead_place = models.ForeignKey(Territory)
    live_places = models.ManyToManyField(Territory)
    work_places = models.ManyToManyField(Territory)

so now... the born_place can be a City, a Region, a Planet... anything you want! i guess that was your question. I'm no expert in django, this is just a generic way to solve this kind of problems in O.O.P.

pleasedontbelong
After I post my question, I thought of this solution. The only difference in my version is that I have a "kind" field (choices field with possible value: country, region, city) in the Territory class. This way I can filter only the cities or the countries, etc. And more important, I can put constraint that the city's parent should be a region and the region's parent should be a country.For the moment, that's my favorite solution. But I need to evaluate more the 2 other solutions, generic relations and model inheritance.
Etienne
oh yeah! i forgot about the "kind" field, i usually call it "type"..the disavantage of using inheritance is that you'll have to modify your code if you want to introduce new "types" or "kinds" that's why inheritance is a good solution when you have a certain number of elements that wont change over the time.. witch might be your case... I often use this solution when working on "categories" and "subcategories" that are more dynamically created
pleasedontbelong
About "kind" I use it in general in python (not especially in Django) instead of "type" to avoid problem because "type" is a keyword in python. But I agree that "type" is a better definition. And merci for pointing me the differences between inheritance and your solution. Do you have any opinions on the generic relations approach?
Etienne
i dont like them! in my opinion,if you use generic relations you could loose integrity of your data, and maybe creating relations that dont make any sense, when you could solve the problem in a good-christian-old-fashioned way using O.O.P
pleasedontbelong
+1  A: 

You can also use model inheritance

class Territory(models.Model):
    name = models.CharField(max_length=150)

class Country(Territory):
    iso = models.CharField(max_length=2, primary_key=True)

class Region(Territory):
    iso = models.CharField(max_length=2, blank=True)
    country = models.ForeignKey('Country')

class City(Territory):
    region = models.ForeignKey('Region')

class Artist(models.Model):
    name = models.CharField(max_length=128)
    born_place = models.ForeignKey(Territory)
    dead_place = models.ForeignKey(Territory)
    live_places = models.ManyToManyField(Territory)
    work_places = models.ManyToManyField(Territory)

Best regards!

o.elias
Hmm, interesting approach. I need to play a bit with this one to see all the implications (admin, table structure, etc.).
Etienne