views:

68

answers:

2

Hi,

I'm creating a simple CMS in django, with multiple "modules" (each as a django app). I've set up the following models:

class FooObject(models.Model):
    id = models.SlugField(primary_key=True)
    name = models.CharField(max_length=255)
    creator = models.ForeignKey(auth.models.User, editable=False, related_name="createdby")

class FooPage(FooObject):
    content = models.TextField(blank=True, null=True)

    @models.permalink
    def get_absolute_url(self):
        return ('page', (), {'page_id':self.id}

class FooSubitem(FooObject):
    parent = models.ForeignKey(FooPage, related_name='subitems')

In each of the modules, I create a subclass of FooPage, and at least one subclass of FooSubitem, e.g.

# in FooBlog.models
class FooBlog(FooPage):
    owner = models.ForeignKey(auth.models.User, editable=False)

    @models.permalink
    def get_absolute_url(self):
        return ('blog', (), {'blog_id':self.id})

class FooPost(FooSubitem):
    post_time = models.DateTimeField(auto_now_add=True)

and

# in FooGallery.models
class FooGallery(FooPage):
    location = models.CharField(max_length=255)

    @models.permalink
    def get_absolute_url(self):
        return ('gallery', (), {'gallery_id':self.id})

class FooImage(FooSubitem):
    image_file = models.ImageField(upload_to='foogallery')

These are simplifications, but should give you a good idea of what I'm trying to do. In the admins for FooPost and FooImage, I restrict the parent selection list to their corresponding parent pages.

My problem arises when I try to use these in a template. In each view, I have the following:

page_list = FooPage.objects.all()

which returns a list of all FooPages, of both FooBlog and FooGallery types. However, when I iterate through this list:

{% for page in page_list %}{{ page.get_absolute_url }}{% endfor %}

it returns the 'page' url pattern, not the 'blog' or 'gallery' url pattern.

How do I make this work without having to rewrite the code when I want to add a FooCalendar module later on? I want to make sure this works with any possible module.

Thanks,

  • Lexo
A: 

FooPage.objects.all() returns all the objects of type FooPage, these objects will be mix of underlying db table rows for FooPage, FooBlog, FooGallery. To get the correct URL you should get the FooBlog or FooGallery object e.g.

page.fooblog.get_absolute_url()

it may throw FooBlog.DoesNotExist error if page is simply a page object i.e created via FooPage, so to get correct urls you may do something like this

   urls = []
   for page in FooPage.objects.all():
        try:
            page = page.fooblog
        except FooBlog.DoesNotExist:
            pass

            urls.append(page.get_absolute_url())

alternatively you may try to make FooPage a abstractclass if you do not want FooPage to be a real table.

Anurag Uniyal
Thanks Anurag. My problem is that I don't know which module apps (e.g. FooBlog, FooGallery) I will have installed, or may install in the future. If I end up creating 10 or more different apps (which is entirely possible) I don't want to have to re-write my code to include checks for each type of FooPage. Plus, that seems terribly inefficient and something that I should be able to do programmatically.Is there a way to "drill down" to the most specific subclass?
Lexo
Also, I tried using abstract classes but since FooSubitems need to have FooPage parents, and you can't specify a foreign key to an abstract class, it doesn't work.
Lexo
may be you can write a generic function which does the same thing, i.e. try to get derived class objects if possible, you can do that using a custom model manager (http://www.djangoproject.com/documentation/models/custom_managers/)
Anurag Uniyal
+2  A: 

The classic solution to this problem tends to be adding a ContentType to the superclass which stores the type of subclass for that instance. This way you can rely on a consistent API that returns the related subclass object of the appropriate type.

Joseph Spiros
This was exactly what I was looking for. I ended up modifying the "QuerySet Hack" described in the comments of your link. Thanks!
Lexo