Search code examples
sqldjangotumblr

Using Django, how would I generate a Tumblr-style series of date-ordered pages of different objects?


I have a Django app with classes for different things such as Post, Quote, Image, Link, etc. Like items in Tumblr. Each of these objects has a datetime.

I want to display a combined list of the 20 most recent items of all types. And I want to be able to page back through older items. ie, just like Tumblr's pages.

But I can't think how to do queries like this across many different types of object... Would I have to create a separate class, something like:

class CombinedItem(models.Model):
    date = models.DateTimeField()
    item_type = models.CharField() # eg, 'post', 'quote', 'image', 'link'
    item_id = models.IntegerField()

And add a new entry to that whenever I add a new Post, Quote, Image, etc. And then, for my list, query that CombinedItem class for the most recent items, and then fetch the main details for each item it returns? If so, what's the most efficient way of doing all that? Thanks.


Solution

  • Your best bet would be to actually work in reverse. Each of those is a type of user-generated content, so they should all inherit from a common base:

    class ContentItem(models.model):
        # common fields here
    
    class Post(ContentItem):
        # post specific fields
    
    ....
    

    Then, you can simply query the base model ContentItem and get a list of everything.

    The only potential issue is that the results are ContentItems and not Posts, Quotes, etc. However, in Django's implementation of MTI (multi-table inheritance), the child class is an attribute on the parent class. So a "Post" ContentItem will have a .post attribute from which you can access the Post. You can combine this with hasattr to create utility methods that you can use in your view. For example:

    class ContentItem(models.Model):
        ...
        def is_post(self):
            return hasattr(self, 'post')
    

    Then, in your template:

    {% if content_item.is_post %}
        <!-- do some post-specific rendering here -->
    {% endif %}