I have 3 models:

class category(models.Model): title = models.CharField(max_length=255, verbose_name='название') class page(models.Model): title = models.CharField(max_length=255, verbose_name='заголовок') text = models.TextField(verbose_name='текст') class menu(models.Model): title = models.CharField(max_length=255, verbose_name='заголовок') content = models.ForeignKey(Page or Category) # <- How to? link = models.CharField(max_length=255) 

How to make menu.content refer to either a page or a category, and depending on what it refers to in menu.link should be either category/<id> or page/<id> ?

  • Further, content types can be added ... - Fuzzz3r

2 answers 2

It is possible through generic relations , something like this:

 content_type = models.ForeignKey(ContentType) object_id = models.IntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') 

But, it seems, there is no sensible method to restrict what GFK can refer to, except that in the database a post-factum (after filling in the content_types table) to thrust the constraints by ID to insure.

Another option is through multi-table inheritance .

 class MenuItem(models.Model): title = ... link = ... class Page(MenuItem): text = ... ... 

But, again, ugly - MenuItem.objects returns objects of the class MenuItem , not Page . Those. have to choose separately for each table and glue.

The third option is if the DBMS is able to inherit (like, say, PostgreSQL ), then use it and raw SQL.

 CREATE TABLE menu_items ( ... ); CREATE TABLE pages ( ...) INHERITS (menu_items); 

Then SELECT * FROM menu_items can use all fields from descendant tables. But, again, this is ugly - we rush past Django's ORM. Although it is possible to get out through unmanaged models and, possibly, views for SELECT ... FROM ONLY ... (if they are needed; since the ORM will never generate a query).

The fourth option is to have two ForeignKey , page and category , and set the constraint that one and only one of them should be NULL ( None ). And to make, for convenience, @property for a model that returns what is not None . For me, this is the best option, but it is completely unsuitable for situations where there are many types of objects.

    It seems to me that the easiest way is to make 2 fields, pagecontent and categorycontent , and fill them in different ways depending on the link content.