Associate model classes Django with instances of another model

986 Views Asked by At

Heavily edited in response to comment by @Todor.

[I use Django 1.7.6 and Python 3.4.1.]

So in my Django project I've defined an Item model with various specific subclassing models. I've also defined a Category model. A toy version of my models looks like:

models.py

from django.db import models

class Item(models.Model):
    name = models.CharField()

class CleeseItem(Item):
    cleese = models.URLField()

class GilliamItem(Item):
    gilliam = models.IntegerField()

class JonesItem(Item):
    jones = models.EmailField()

class Category(models.Model):
    title = models.CharField()
    item_types = ????  # List of model classes

Now, the ???? above is my question. To each Category instance, I'd like to associate a list of Item subclasses. Not individual Item instances, but Item subclasses.

Example: Say I create a particular Category instance <Category: terry>. Now I want to tell Django that the GilliamItem and JonesItem subclasses belong to the "terry" Category.

How will I use this? I'd like to write a view as follows:

views.py

from django.shortcuts import render_to_response, get_object_or_404
from monty.models import Category

def category_page(request, cat_name):
    c = get_object_or_404(Category, name=cat_name)
    item_dict = {}
    for it in c.item_types:  # Recall item_types is a list of classes
        item_dict.update( {it.verbose_name_plural: it.objects.all()} )
    return render_to_response('category_page.html', {'items': item_dict})

Namely, my view (1) retrieves a Category object; (2) constructs a dictionary in which the keys are the names of the models associated with that Category and the values are QuerySets containing all instances of those models; and (3) supplies that dictionary to the template.

Example: I visit http://domain.com/category/terry/. The view retrieves the Category object with name='terry' from the database. c.item_types in line 7 produces [GilliamItem, JonesItem], so the dictionary winds up looking like { 'gilliam items': GilliamItem.objects.all(), 'jones items': JonesItem.objects.all() }.

Finally, I'd like a site administrator to be able to rearrange arbitrarily the mapping between Category objects and Item types through the admin site. (Example: One day I might decide to edit the "Terry" Category to remove JonesItem from item_types, and perhaps add PratchettItem.)

2

There are 2 best solutions below

0
catavaran On

Look at django-polymorphic. It provides foreign-key and many-to-many relationships for inherited models.

1
thecommexokid On

As I suspected, I'm making headway using the ContentTypes framework. In particular, if I edit my Category model to look like

from django.contrib.contenttypes.models import ContentType

class Category(models.Model):
    title = models.CharField()
    item_types = models.ManyToManyField(ContentType, null=True, blank=True)

I get almost exactly what I want. The only remaining problem is that this evokes all the model classes in my project, whereas I am interested only in the subclasses of Item. But I'm getting close!