In playing with, and trying to learn Django, I've been tampering with Paul Bisex's wikiproject. While he stated that he doubt's anyone would actually want to use his simple creation, I saw potential. There were a few minor improvments that I imedietly saw, but I'll get to those later.

The first thing I want to dicuss is what I see as a nessecary step in any content that can potentially be edited by anyone with internet access. Your just asking to be spammed without any safeguards in place. The easiest is a simple authentication system. This is even easier with Django's built in user authentication system. While we could set up groups and set permission levels, a super simple wiki only needs one permission - edit. That being the case, we only need to allow editing if a user is logged in, regardless of permissions. According to the docs:

The simple, raw way to limit access to pages is to check request.user.is_anonymous() and ... display an error message.

So, if a user is logged in, everything should work as normal. However, if a request is made for a non-existant page by an anonymous (not logged in) user, then they should not be given the opportunity to edit the page. While some choose to redirect to a login page here, I prefer the 404 Not Found HTTP Response Header. That allows no room for confusion1 among users or search engines. This is easy enough with the HttpResponse subclass HttpResponseNotFound.

First we need to import HttpResponseNotFound so let's add it to the first line of views.py:

from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect, HttpResponseNotFound

Then, alter the page view as follows:

def page(request, title):
    "Display page, 404 Not Found error, or create page if logged in"
    try:
        page = wikipages.get_object(title__exact=title)
        return render_to_response('page', locals())
    except wikipages.WikipageDoesNotExist:
        if request.user.is_anonymous():
            # send 404 Not Found
            return HttpResponseNotFound()
        else:
            # create a dummy page object
            page = wikipages.Wikipage()
            page.title = title
            return render_to_response('edit', locals())

At first I was going to do the same thing for the edit view, but return a 403 Forbidden HTTP Response Header. However, it occured to me that when an edit is expressly requested, redirecting to a login page would be more intuitive. This, as usual, is super easy with Django. As the docs point out:

As a shortcut, you can use the convenient login_required decorator.

Its that easy. The trick is that the syntax is different for Python versions 2.3 and 2.4 but either way, its only one line of code outside the edit view. First, we need to import it:

from django.views.decorators.auth import login_required

Python 2.3:

def edit(request, title):
    # ...
edit = login_required(edit)

Python 2.4:

@login_required
def edit(request, title):
    # ...

Which brings us to the next step; a login page. As login_required automaticly redirects to /accounts/login/, we will need to create a login page accordingly. First, we need to tell Django what to do with that url, so open url.py and add the following as the first listing:

(r'^accounts/login/', 'django.views.auth.login.login'),

Yes, we are actually using the view code built right into Django, which means less work for us - generally a good thing. That view code calls a template registration/login.html. Of course, that does not exist, but we can use the Admin app default as a base. You should find these at ...django_src/django/contrib/admin/templates/. I choose to copy that templates/ directory in its entirety to my wikiproject/ directory. You may want to browse around and see what you may find useful in there and delete what you won't need.2 Before deleting the admin/ directory, make sure to copy login.html from there to registration/, which, of course we do NOT want to delete as it contains the templates we need.

Now we need to tell Django were to find these templates. Open the settings.py file and add the following to TEMPLATE_DIRS

'templates/',

These templates extend the Admin base template and if you have the admin app enabled they will work now with the Admin styling. Go ahead and give them a test.3 Type the url to edit a page directly into the browser's address bar, and you should be redirected to the login page. Assuming you have created a user4, enter that username and password and submit. You should be redirected to the edit form for your page.

Of course, who wants the admin style only for a login page used by non-admins?5 Open registration/login.html and alter the first line to extend our wiki's base:

{% raw %}{% extends "base" %}{% endraw %}

As we don't have any need for the breadcrumb stuff, we can remove that line:

{% raw %}{% block breadcrumbs %}{% endblock %}{% endraw %}

While your at it, you may want to make a few other adjustments to the markup to fit in with your preferred style.6

Users can now log in, but they need to be able to log out. We just need to do the same thing as we did for logging in. In urls.py add the following:

(r'^accounts/logout/', 'django.views.auth.login.logout'),

Then, make the same changes to registration/logged_out.html as you did to registration/login.html. While you're at it, you may want to adjust the url to 'log back in' so it points to /accounts/login/.

The only thing left is to add a login/out link to our templates, add edit links to the index page (which only display for logged in users) and we have a much more useful wiki. It's even starting to look like something I would use for my own personal site. Especially if I used Markdown instead of Paul's WikiLinks. As I've noted previously, I could even combine the two.

Feel free to use the code I have provided here. As with Paul's, consider it licenced under the MIT Licence. And look for Part 2 were I go over a number of little things that really clean it up and make it easy to use.


  1. One could always include a link to a login page in their custom error page. You do have custom error pages don't you? Whether such is left to the http server, of handled by Django shouldn't matter. With a few modifications to your server settings (perhaps in Apache's .htaccess file) or to my code this should be easy enough, but is left as an exersize for the reader. Perhaps I'll include that in part 2.
  2. My understanding is that if one desires to customize or alter the Admin templates for use with the Admin app, the same practice would be followed and all edits would take place on this copy. As an aside, you may also note that 404 and 405 error templates are available here and can be easily adapted to work with the previous footnote.
  3. I should mention that I have been testing this all along by using the admin app to log in and out from another browser tab. As both tabs use the same session data, it works fine, but is not really an ideal situation. Besides, you may not want to give page editors access to the admin interface, which you use to add and delete users. This way, they can still log in without ever seeing the admin interface. Just make sure they have no admin privliges when you create their account. Remember, our little wiki app only checks if they are logged in, but never checks what permissions they have, so you could give them no permisions but they can still edit wikipages as long as they can log in.
  4. If you havn't done so already, create a superuser from the command line by doing:
    python manage.py createsuperuser
    

    You will then be prompted for a username, email address and password.

  5. You may actually want to leave the admin styling in tact, as the admin interface will use these altered templates over the defaults we copied them from. If you follow the last few steps here, the login and logout pages for the admin will match the styling of the wiki no matter what app in the project you may be using the admin interface for. I'm not aware of, not did I look for an easy workaround. Consider yourself warned.
  6. For example, I edited all the templates to use a title block so that the page title and/or the action (edit, login) was displayed at the top of the browser window/tab.