Posts for the 'django' Category

  1. Django 1.2 release

    It was just announced that Django 1.2 will be released on May 17th, which is in 3 days time.

    Been waiting a while for this one to go to production release so I am really glad. The new messages framework looks much better, and I also love the date i18n features – something that really helps on a project at Cantemo where we have to deal with ISO formatted datetimes.

    Now the users will be able to choose how their dates are displayed on individual systems.

    By timc3 on the
    May 15th, 2010
  2. Python plugin systems

    There is a lot of interesting information about creating plugin architectures using Python all over the web, but its in fairly disparate places. This is an overview of the documentation that I found as of April 2010.

    Firstly Dr André Roberge has some very interesting posts, as well as a talk at PyCon 2009 on Blip.tv entitled Plugins and monkeypatching: increasing flexibility, dealing with inflexibility. He is also the author of Crunchy which uses a plugin system. He goes on to write about his experiences in creating a plugin architecture on his blog, over 6 parts starting here on part 1.

    Marty Alchin, the author of Pro Django has an interesting couple of pages in that same book about creating a simple plugin system, and the snippet of code is on djangosnippets.com. He also has an excellent blog post about implementing a plugin architecture, it also talks about Django but is fairly non-specific.

    William E. Hart over 2009-2010 has also been researching Python Plugin Frameworks, his overview is on his blog and he is also the author of the PyUtilib Component Architecture. This was very recently released and so hasn’t gathered much momentum as of this post but looks very promising indeed.

    Zope. The big one is the Zope Component Architecture. I must admit, that looking into it I think its overkill, I don’t like the configuration utility or the overhead it introduces – just to use it requires a lot of extra Zope modules. However google for it if you are interested in learning further.

    Yapsy. Yapsy is interesting, looks extremely lightweight, has been around for a few years, is currently on version 1.7 and serves as a good starting point. It has no external dependancies.

    SprinklesPy. SprinklesPy is also very lightweight but I am not sure that development is active on it (2006 was the last mention of it being used that I found), or that it has anyone in the community using it (a benefit I believe if you are implementing a system that you are not creating yourself.)

    Trac is the one component architecture that frequently gets mentioned, and it has a very decent user base. However, ripping the plugin system out might be more work than is reasonable.

    Examples of Plugin use in the wild:

    Other interesting links:

    It is clear to me that there is no correct way of implementing plugins and you have to pick based upon the needs of your project, but this should serve as a good starting point. I will add and update this post as my knowledge grows.

    By timc3 on the
    April 22nd, 2010
  3. Django on RedHat or CentOS

    I am normally using Debian, Ubuntu or even Suse for deploying Django, but a recent customer needed to deploy on Red Hat Enterprise Server 5.

    We decided beforehand to test deployment on RHEL5 and also on CentOS 5.4 and so these instructions should work for both environments. NginX will be used as the webserver.

    The first thing I like to do is to upgrade the repository and add EPEL. This wasn’t needed I found on the RedHat box I was using, but was needed on CentOS but your mileage might vary. It should be noted that we made the system as up to date as possible with patches and yum updates.

    I won’t be covering the database setup, it is assumed that this is already setup and done. I also won’t be covering the actually placing of the Django application anywhere.

    cd ~
    rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-3.noarch.rpm
    yum repolist
    yum update
    yum search zlib
    yum install zlib-devel*
    yum install gcc make

    Python 2.4 ships with both RHEL5 and CentOS 5 and is needed for the packaging environment YUM so I would advise not to replace it but just compile and install 2.5.5 (or 2.6) next to it.

    mkdir src
    cd src
    wget http://www.python.org/ftp/python/2.5.5/Python-2.5.5.tgz
    tar fxz Python-2.5.5.tgz
    cd Python-2.5.5
    ./configure
    make
    make install
    cd ~/src
    wget http://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c11-py2.5.egg#md5=64c94f3bf7a72a13ec83e0b24f2749b2
    sh setuptools-0.6c11-py2.5.egg

    Now we need to build and install NginX. I choose this primarily because of my experience with it, (I have used Apache2 a lot but its archaic ) and because of its speed.

    yum install pcre-devel.i386 openssl-devel.i386
    wget http://nginx.org/download/nginx-0.7.65.tar.gz
    tar fxz nginx-0.7.65.tar.gz
    ./configure --sbin-path=/usr/local/sbin --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_ssl_module --with-http_flv_module --with-cc-opt="-I /usr/include/pcre"
    make
    make install
    vim /etc/init.d/nginx
    sudo chmod +x /etc/init.d/nginx
    /sbin/chkconfig nginx on
    /sbin/chkconfig --list nginx

    There is a decent RedHat init.d script here: http://wiki.nginx.org/RedHatNginxInitScript, but remember that I have changed the installation location above so you will need to change the location in that script of nginx from /usr/sbin/nginx to /usr/local/sbin/nginx

    You will also need a nginx.conf configuration script for /etc/nginx/ or you could roll your own using sites-available, sites-enabled pattern. I will attach the script soon.

    Now with Python 2.5 installed we can install Django and other requirements:

    easy_install-2.5 Django
    easy_install-2.5 django-nose
    easy_install-2.5 http://dist.repoze.org/PIL-1.1.6.tar.gz
    easy_install-2.5 flup

    I am using easy_install here as that is the most widely used tool, but pip with a requirements file is a better solution.

    Now we need a database adapter. I am using PostgreSQL 8.4.2, if you are using MySQL, Oracle or some other database you will need to follow the recommended route for the Python adapter.

    yum search psycopg2
    yum install python-psycopg2.i386 (This will install for the old Python)
    yum install postgresql-devel
    cd ~/src
    wget http://initd.org/pub/software/psycopg/psycopg2-2.0.13.tar.gz
    tar xvfz psycopg2-2.0.13.tar.gz
    cd psycopg2-2.0.13
    vim setup.cfg

    Change the location of pg_config:

       pg_config=/usr/bin/pg_config

    And now install:

    python2.5 setup.py build
    python2.5 setup.py install

    Then you need to install your application. This is really specific to your needs. I choose to install the files in /opt/cantemo/application-name as that is specific to our solutions, but RedHat style might mean that you wish to install somewhere else.

    Lastly you will want to have your application start easily and also make sure that it starts if the machine ever reboots. To do this I am going to install the start-stop-daemon that is used on Debian based Linux distros on Redhat. I read some threads that it will but there is no ETA on that.

    You should download the initd script for Django from http://code.djangoproject.com/wiki/InitdScriptForLinux. Change the RUN_AS user to the user on RedHat that you are going to use for running Nginx. By default this is apache.

    cd ~/src
    wget http://developer.axis.com/download/distribution/apps-sys-utils-start-stop-daemon-IR1_9_18-2.tar.gz
    tar -xzf apps-sys-utils-start-stop-daemon-IR1_9_18-2.tar.gz
    cd ~/src/apps/sys-utils/start-stop-daemon-IR1_9_18-2
    gcc start-stop-daemon.c -o start-stop-daemon
    cp start-stop-daemon /usr/local/sbin/
    chmod +x /usr/local/sbin/start-stop-daemon
    vim /etc/init.id/fastcgi
    chmod +x /etc/init.d/fastcgi
    /etc/init.d/fastcgi start
    ps ax
    /etc/init.d/fastcgi stop
    /sbin/chkconfig --add fastcgi
    /sbin/chkconfig --list fastcgi

    Lastly make sure that your application directories have the correct user permissions but you should be able to start everything by issuing the following commands:

    /etc/init.d/nginx start
    /etc/init.d/fastcgi start

    Eric Florenzano posted a nice tutorial on deploying django using FastCGI so also check that out, but I didn’t have much luck with daemontools on CentOS, and didn’t bother trying on RedHat. I suggest instead using start-stop-daemon. Check it out for other hints and tips though, particularly on prefork vs. threaded, pip, and more nginx configuration tips.

    By timc3 on the
    March 26th, 2010
  4. Django staff member required

    Here is a simple decorator that isn’t mentioned properly in the Django documentation.

    @staff_member_required

    It basically checks to see if the user is logged in and has is_staff before allowing a user access to the view. Use like you would the normal
    @login_required decorator.

    from django.shortcuts import render_to_response
    from django.template.context import RequestContext
    from django.contrib.admin.views.decorators import staff_member_required


    @staff_member_required
    def my_view(request):
       
        return render_to_response('page.html',
                                  context_instance=RequestContext(request))
    By timc3 on the
    March 13th, 2010
  5. Extending Django’s user admin

    The built in admin pages that you get in Django can be useful, but they particularly become useful once you start to add a lot more functionality to them.

    For instance the Django’s User authentication system (which lives in django.contrib.auth ) is widely used, and quite often you need to extend the user’s profile by using AUTH_PROFILE_MODULE and a separate model. But having a separate Admin screen for this is kind of pointless.

    In the admin.py file for your model which provides extra information (in my example it is called UserProfile just do the following:

    from django.contrib import admin
    from django.contrib.auth.admin import UserAdmin
    from django.contrib.auth.models import User
    from models import UserProfile

    class UserProfileInline(admin.TabularInline):
        model = UserProfile
        fk_name = 'user'
        max_num = 1
       
    class CustomUserAdmin(UserAdmin):
        inlines = [UserProfileInline,]
        list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', 'is_active')
       
    admin.site.unregister(User)
    admin.site.register(User, CustomUserAdmin)

    Simple once you know how.

    By timc3 on the
    February 18th, 2010
  6. gcc and python

    Had an interesting little problem with gcc and python today on OS X 10.6.

    Basically I was trying to use graphviz and pygraphviz, and installing from source I got messages like this:

    gcc -arch ppc -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk -fno-strict-aliasing -Wno-long-double -no-cpp-precomp -mno-fused-madd -fno-common -dynamic -DNDEBUG -g -O3 -I/usr/local/include/graphviz -I/Library/Frameworks/Python.framework/Versions/2.5/include/python2.5 -c pygraphviz/graphviz_wrap.c -o build/temp.macosx-10.3-i386-2.5/pygraphviz/graphviz_wrap.o
    cc1: error: unrecognized command line option "-Wno-long-double"
    cc1: error: unrecognized command line option "-Wno-long-double"
    lipo: can't figure out the architecture type of: /var/tmp//ccD4Ow7T.out
    error: command '
    gcc' failed with exit status 1

    This pointed to two problems I found:

    1. MacOSX10.4u.sdk being used
    2. “-Wno-long-double” being passed to gcc

    Obviously I am on 10.6 and even though I do have that SDK installed it is the incorrect version. On some Xcode installations you won’t have it.

    The remedy was to change the following file:

    /Library/Frameworks/Python.framework/Versions/Current/lib/python2.5/config/Makefile

    After backing up change all the instances of MacOSX10.4u.sdk to MacOSX10.6.sdk and then remove the flag -Wno-long-double

    Then you should be able to compile from a normal python setup. For good measure I test easy_install as well and that worked smoothly..

    By timc3 on the
    February 18th, 2010
  7. Django settings in template

    So what do you do if you require a django setting in your templates, much like we have MEDIA_URL today?

    Well there are use cases for this, (if you are in doubt have a look at the original ticket for adding MEDIA_URL to django ).

    The easiest way that I have found so far is to write a context processor. For example in my settings I might have JAVASCRIPT_URL (which in my real life code changes depending on whether I am running in debug, test or from a CDN):

    JAVASCRIPT_URL = 'http://myjshost.com'

    Now from here I would like to make this available in my templates. Create a new python file that is somewhere on your python path (Under my project, I create a utils directory and then put a file context_processors.py in there. Don’t forget __init__.py should live in that directory as well).

    In the context_processors.py file simply put

    def javascript_url(request):
        from django.conf import settings
        return {'JAVASCRIPT_URL': settings.JAVASCRIPT_URL}

    In your settings.py file you might already have a reference to TEMPLATE_CONTEXT_PROCESSORS if not then add it like so:

    from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS
    TEMPLATE_CONTEXT_PROCESSORS += (
         'django.core.context_processors.request',
         'django.core.context_processors.i18n',
         'appname.utils.context_processors.javascript_url',
    )

    And thats about it. From there on in you will be able to use JAVASCRIPT_URL like MEDIA_URL:

    {{ JAVASCRIPT_URL }}
    By timc3 on the
    January 22nd, 2010
  8. Leading zeros in django

    I am always forgetting about the stringformatting tag in Django’s templating language. For instance for leading zeros:

    {{ variable|stringformat:"02d" }}

    This will always put a leading zero.

    By timc3 on the
    July 13th, 2009
  9. Django test fixtures and contenttypes

    Just came across an interesting problem with the contenttypes contributed application and the test framework.

    If you are using fixtures in the test frame work you might find that the database gets out of date as you are working on it, particularly if you are creating new models all the time.

    To get around this you need to regenerate the contenttypes database. If you depend on this, the following steps could give you problems so make sure that you back up your data (dumpdata) beforehand.

    So first off drop the database:

    python manage.py reset contenttypes

    If the database has foreign key dependancies on contenttypes you will not be able to reset it like that. Use a database administration tool and drop cascade on the django_content_type table instead.

    Then sync the database to create a new contenttypes table:

    python manage.py syncdb

    Now you are ready to dumpdata into a fixture:

    python manage.py dumpdata contenttypes > fixtures/testdata.json
    By timc3 on the
    May 29th, 2009
  10. django one form, two models

    This post is a work in progress is now working I am glad to say. I have been working on a django site which needs two models updated for one post. It is actually using models very close to that on django-forums and I have created a forms.py file:

    class ThreadForm(forms.ModelForm):
        class Meta:
            model = Thread
            exclude = ('forum', 'sticky', 'closed', 'posts', 'views', 'latest_post_time')
         
        def clean_title(self):
            title = self.cleaned_data['title']
            if not alnum_re.search(title):
                raise forms.ValidationError(ugettext("Titles can only contain letters, numbers and underscores"))

            if len(title) < 1:
                raise forms.ValidationError(ugettext("Please enter a title"))
            return title

           
    class PostForm(forms.ModelForm):
        class Meta:
            model = Post
            exclude = ('thread', 'author', 'time', 'related_item')
           
        def clean_body(self):
            body = self.cleaned_data['body']
            if len(body) < 1:
                raise forms.ValidationError(ugettext("Please enter some body text"))
            return body

    And then in my views (after importing in the correct models):

    @login_required
    def groupnewthread(request, slug):
        thegroup = get_object_or_404(GroupsOfUser, slug=slug)
        if request.method == 'POST':
            f = request.POST.copy()
            tdata = {
                'title': f['title'],
            }
            pdata = {
                'body': f['body'],
            }
            t = ThreadForm(tdata)
            p = PostForm(pdata)
            if t.is_valid():
                newthread = t.save(commit=False)
                newthread.forum = thegroup
               
               
                if p.is_valid():
                    newthread.save()
                    newpost = p.save(commit=False)
                    newpost.thread = newthread
                    newpost.author = request.user
                    newpost.save()
             
                    strmessage = 'has created a thread <a href="%s">%s</a>' % (newthread.get_absolute_url(), newthread.title)
                    usm = UserStatus(user = newpost.author, message = strmessage)
                    usm.save()
             
                    return HttpResponseRedirect(reverse('groupdetail', args=[thegroup.slug]))
           
        else:

            t = ThreadForm()
            p = PostForm()
           
        objContext = RequestContext(request, {'threadform': t, 'postform': p})
        return render_to_response('groups/group_thread_add.html', objContext)

    Now the bit that is in progress is the returning part of dealing with the form data being bound, but I am going to write a custom handler. This is obviously going to be much easier to handle than an update, which I will have to deal with at a point.

    The form HTML looks like this btw:

    {% extends "base.html" %}
    {% load i18n %}
    {% block title %}
        {% trans 'Add a new group thread' %}
    {% endblock %}
    {% block body %}
        <h1>{% trans 'Create a new group thread' %}</h1>
    <div class="column span-14">
        {% if t.errors %}
        <h3>{% blocktrans count t.errors|length as count %}Please correct the following error:{% plural %}Please correct the following errors:{% endblocktrans %}</h3>
        {% endif %}
        {% if p.errors %}
        <h3>{% blocktrans count p.errors|length as count %}Please correct the following error:{% plural %}Please correct the following errors:{% endblocktrans %}</h3>
        {% endif %}
        <table>
        <form method="post" action=".">
            {{ threadform.as_table }}
            {{ postform.as_table }}
        <tr><td></td><td><input type="submit" value="{% trans "Update" %}"/></td></tr>
        </form>
        </table>
    </div>
    {% endblock %}
    By timc3 on the
    June 21st, 2008