Our Journey to a blog with django - Part 2: The basic Model

30.07.2007 by azarai in django | python

This time i am going to define our model for a basic system. The basic version should handle content i.e. blog posts and supports tagging for them. All other feature will come later and get their own post.

At first we need a model for our tag. It should contain a title and a description. As i'd like to get a articles overview by tag, I'll also add a unique human readable id for each tag, called slug. As i am going to use the django admin as the whole administration tool, i add our model to the admin and do some visual fine tuning.

class Tag(models.Model):  
    slug = models.SlugField(prepopulate_from=('title',), primary_key=True)  
    title = models.CharField(maxlength=30)  
    description = models.TextField(help_text='Short summary of this tag')

    def __str__(self):  
        return self.title

    def get_active_articles(self):  
        return self.content_set.all().filter(draft=False, type=CONTENT_TYPE_ARTICLE)

    class Admin:  
        list_display = ('slug', 'title',)  
        search_fields = ('title', 'description',)

    class Meta:  
        ordering = ('title',)

Now we need a model for our content. As i am now pleased with the name entry, blog, post or similar for its purpose, i call it just plain content. Its generic and can be an article or even a static page. Our model need at least the following informations:

  • human readable id
  • title
  • type of content
  • publishing date
  • last modified
  • the content
  • a flag if its draft and doens't appear in public
  • a flag if content is closed, use later if commenting is enabled
  • tags
  • the author of it

Thats what i did come up with:

     (CONTENT_TYPE_ARTICLE, 'Article'),  
     ('S', 'Static Page'),       

class Content(models.Model):  
    slug = models.SlugField(prepopulate_from=('title',), unique=True, maxlength=100)  
    title = models.CharField(maxlength=100)  
    type = models.CharField("Content type", maxlength=1, choices=CONTENT_TYPES, default=CONTENT_TYPE_ARTICLE)  
    pubdate = models.DateTimeField("Publishing date",default=models.LazyDate())  
    moddate = models.DateTimeField("Last modified",auto_now=True)  
    body = models.TextField("The Content",)  
    draft = models.BooleanField(default=True)  
    closed = models.BooleanField("Closed, no further actions allowed",)  
    tags = models.ManyToManyField(Tag, blank=True)  
    author = models.ForeignKey(User,blank=True, editable=False)

    def __str__(self):  
        return self.title

    def is_article(self):  
        return self.type == CONTENT_TYPE_ARTICLE

    def save(self):  
        if not self.id:  
            self.author = threadlocals.get_current_user()  

    class Admin:  
        fields = ((None, {  
            'fields': ('title', 'slug', 'type', 'pubdate', 'body', 'draft', 'closed', 'tags')  
        list_display = ('slug', 'title', 'pubdate', 'moddate', 'type')  
        search_fields = ('title', 'body')  
        date_hierarchy = 'pubdate'  
        list_filter = ('type','pubdate','author',)

    class Meta:  
        get_latest_by = 'pubdate'  
        ordering = ('-pubdate',)

Currently i limit the content type to article style contentand plain "static" pages. As i am using the all over the app, i'll keep them in the models.py as constants. The implementation and usage of this model was quite simple, except one point i'll cover in the next section.

Any pitfall so far?
I wanted to store the owner/author of the content and thought i could handle it in plain models.py. I overwrote the save method and was looking for a way to access the current request. Didn't find one and started google. I couldn't be the only one with this problem. After some searching i found a solution in the django wiki. Basically its using a middleware to receive the current user and bypass it via threadslocal to your model. There you have to overwrite the save method and insert your current user before saving. It worked for me and i'll stick with it for now. Don't know how it performs under load. On the other hand we are miles away from ever testing this out in production :-)

I am not sure if i'll use the current style for the next part or just cover the steps i did and the pitfalls. Not sure which is better suited, but maybe you'd like to share your point of view? :-)

comments powered by Disqus