Pinax is a collection of reusable django apps that brings together features that are common to many websites. It allows developers to focus on what makes their site unique. Here is an example of adding your own functionality to Pinax. It will also be an example of writing a reusable app since every individual app currently in Pinax can be used separately. Also, I’ve bundled the example files into a google code project.

My example will be to create a list of books and allow them to be tied to any object using Django’s ContentType framework. The books could be recommended reading for the members of a tribe (pinax group), a class, or anything in your project and will include title, description, and tags (requires django-tagging). In another post I’ve shown how to create template tags to make it easy to show the list of books and a form to add a book. Obviously, there is a lot more that could be done with this app, but I will leave it out of the example to keep it simple.

Starting the App

Create a folder in the apps directory or any place that is on the python path (ex. /path/to/pinax/projects/complete_project/apps/books/) and include these files:

__init__.py even though it might be empty, it is required

forms.py

models.py

urls.py

views.py

models.py

I will start with creating the model for the project. Below is all of the code I am placing in the file. I’ve added a lot of comments to explain everything that is happening.

#import all of the things we will be using from django. db import models from tagging. fields import TagField # to help with translation of field names from django. utils . translation import ugettext_lazy as _ # to have a generic foreign key for any model from django. contrib . contenttypes import generic # stores model info so this can be applied to any model from django. contrib . contenttypes . models import ContentType class Book ( models. Model ) : "" " The details of a Book " "" # fields that describe this book name = models. CharField ( _ ( 'name' ) , max_length= 48 ) description = models. TextField ( _ ( 'description' ) ) # to add to any model content_type = models. ForeignKey ( ContentType ) object_id = models. PositiveIntegerField ( ) content_object = generic. GenericForeignKey ( 'content_type' , 'object_id' ) # for the list of tags for this book tags = TagField ( ) # misc fields deleted = models. BooleanField ( default= 0 ) created = models. DateTimeField ( auto_now_add= True ) # so that {{book.get_absolute_url}} outputs the whole url @models. permalink def get_absolute_url ( self ) : return ( "book_details" , [ self . pk ] ) # outputs name when printing this object as a string def __unicode__ ( self ) : return self . name

forms.py

Use Django’s ModelForm to create a form for our book model.

from django import forms from books. models import Book class NewBookForm ( forms. ModelForm ) : class Meta: model = Book exclude = ( 'deleted' , 'content_type' , 'object_id' , 'created' )

views.py

In this file we create a view to show the details of a book and a view to create a new book for an object.

from django. shortcuts import render_to_response from django. shortcuts import get_object_or_404 from django. http import HttpResponseRedirect from django. template import RequestContext from django. core . urlresolvers import reverse from django. utils . translation import ugettext_lazy as _ from django. contrib . auth . decorators import login_required from tribes. models import Tribe from books. models import Book from django. contrib . contenttypes . models import ContentType @login_required def new ( request, content_type_id, object_id, template_name= "books/new.html" ) : "" " creates a new book " "" from books. forms import NewBookForm # if a new book was posted if request. method == 'POST' : book_form = NewBookForm ( request. POST ) if book_form. is_valid ( ) : # create it book = book_form. save ( commit= False ) content_type = \ ContentType. objects . get ( id =content_type_id ) content_object = \ content_type. get_object_for_this_type ( id =object_id ) book. content_object = content_object book. save ( ) request. user . message_set . create ( message= _ ( "Successfully created book '%s'" ) % book. name ) # send to object page or book page try : return HttpResponseRedirect ( content_object. get_absolute_url ( ) ) except : return HttpResponseRedirect ( reverse ( 'book_details' , args= ( book. id , ) ) ) # if invalid, it gets displayed below else : book_form = NewBookForm ( ) return render_to_response ( template_name, { 'book_form' : book_form, } , context_instance=RequestContext ( request ) ) @login_required def details ( request, book_id, template_name= "books/details.html" ) : "" " displays details of a book " "" book = get_object_or_404 ( Book, id =book_id ) return render_to_response ( template_name, { 'book' : book, } , context_instance=RequestContext ( request ) )

urls.py

To tie our views to some urls, add this to the urls.py file.

from django. conf . urls . defaults import * from django. conf . urls . defaults import * urlpatterns = patterns ( '' , # new book for object url ( r '^new/(?P<content_type_id> \d +)/(?P<object_id> \d +)' , 'books.views.new' , name= "new_book" ) , # display details of a book url ( r '^details/(?P<book_id> \d +)$' , 'books.views.details' , name= "book_details" ) , )

More Features

The rest of the application is described in the post titled: How to Write Django Template Tags. You can also check out all of the code from the google project by doing the following command:

svn co http://django-books.googlecode.com/svn/trunk books

in a directory on the python path.

I use webfaction to host a lot of my django projects. It has an easy setup that will get you developing quickly and a great community of talented programmers. There is also a quick setup for rails, wordpress, and a lot more.

Related posts: