Django Subversion Repository Structure

The last couple of days I have been racking my brain to come up with an appropriate Subversion repository structure for my django development. My requirements are simple really:
  1. A single repository must contain all of my django projects. This is required since I am hosting my development on google code, and they only provide you with one repository..
  2. Each django application should be "versioned" independently. This allows me to release new versions of applications independently from django projects. It also allows different django projects to use various versions of the same application.
  3. Each django project should be "versioned" independently.
  4. The structure should support both branches and tags. Tags are releases of either applications or projects, while branches are bigger developments that could span a couple of tags or releases.
  5. It must be possible to patch tags or releases easily.
  6. Django projects should be able to reference specific versions of the django applications.
These requirements have led me to a structure which looks like this:

/
  /django-app
    /app_1
      /trunk/app_1
      /tags
        /1.0/app_1
        /2.0/app_1
      /branches
        /awsome_new_feature/app_1
  /django-projects
    /project_1
      /trunk
        /project_1
          /apps
      /tags
        /1.0
          /project_1
        /1.1/project_1      /branches
        /client_color_test/app_1

It is quite easy to see that requirements 1 to 4 are easily satisfied by this structure. Requirement 5 is also met; as long as all patches on a tag or release are done in another tag or release that is related to the original tag, by way of the version number. So if a patch needs to be done to version 1.0 of an application the procedure would be to create a tag from the version 1.0 tag and name it something like version 1.1. Then apply any changes to the version 1.1 tag and release a new package from the version 1.1 tag. This way the original version 1.0 tag or release stays untouched.

Requirement 6 is met but not in the structure itself, but by employing a very nifty feature in Subversion called, externals. (By the way, the repeated project and application names (project1/trunk/application1 and project_1/tags/1.0/project_1) in the repository tree was introduced to make the use of externals more transparent, as can be see below.) The external property gets added to the project directory under the /trunk of the project. In my case I add an external that looks a bit like the following (it assumes that you are in the django-projects/project_1/trunk directory):

svn propget svn:externals project_1 ^/django-apps/app_1/trunk apps
svn propget svn:externals project_1 ^/django-apps/app_1/tags/1.0 apps
The first example add the externals property to my project_1 directory linking my project_1 to the trunk of app_1. When a checkout is done the app_1/trunk contents will be checked out into the project_1/apps directory. The second example will link the release version 1.0 of the same application rather than the trunk version of the application to project_1.

One of the problems with this scheme is that if you reference the trunk of an application and create a release, then the release will also reference the trunk. However, this is easily managed by always developing against release versions of applications. Obviously this is not always possible and in these cases vigilant management of the projects will be required to ensure that a project release doesn't accidentally reference incorrect versions of applications. I can see the possibility of writing some code to validate these rules.

Thats it for now. I am quite excited to start using this structure.

Adding CKEditor to Django Admin

This article will show how to enhance the django admin site's textareas by replacing them with the CKEditor control.

Install CKEditor

Download the CKEditor here. Place the CKEditor source in such a spot that the static web server can serve it. In my case I have created a media directory under the document root of the web static web server and then placed the CKEditor files in a directory ckeditor. Thus I can access the ckeditor.js like this http://localhost/media/ckeditor/ckeditor.js.

Update the MEDIA_URL = 'http://localhost/media/'  in the settings.py of your django project.

My sample model is very simple and looks like this:
from django.db import models

class SampleModel(models.Model):
  title = models.CharField(max_length=50)
  text = models.TextField()

  def __unicode__(self):
    return self.title

The magic happens in the custom model admin form. The for for my sample model looks like this:

from sampleapp.models import SampleModel
from django.contrib import admin
from django import forms
from django.db import models

class SampleModelAdmin(admin.ModelAdmin):
  formfield_overrides = { models.TextField: {'widget': forms.Textarea(attrs={'class':'ckeditor'})}, }

  class Media:
    js = ('ckeditor/ckeditor.js',) # The , at the end of this list IS important.
       
admin.site.register(SampleModel,SampleModelAdmin)
The important things to notice is that I add class attribute to the textarea widget. When ckeditor then satrt up it knows to replace any textareas with the ckeditor code.

The last thing to notice is the the media location is relative to the MEDIA_URL since there is no preceding /.

That's it. 


Her is the sample application: django-ckeditor.zip. Or you can get is directly from the subversion repository:

svn checkout http://johanscode.googlecode.com/svn/devblog/django/ckeditor johanscode-read-only

Johan's Dev Blog Template

The template system at blogger.com (where this blog is being hosted) is quite nice and it allowed me to modify the look and feel of my blog within a couple of minutes to be close to what I wanted. I have uploaded the template that I am using here (look for devblog.css) if anybody is interested.

Easy LAMP Setup

This is the easiest way to install a LAMP stack on an Ubuntu machine:

sudo apt-get install apache2 php5-mysql libapache2-mod-php5 mysql-server

After the installation has finished remember to restart the Apache server.

sudo /etc/init.d/apache2 restart
Connect and enjoy.