Posts Tagged ‘python’

How to Install MySQL with Fabric

Saturday, May 22nd, 2010

I just want to share a small fabfile snipplet that installs mysql-server package on a Debian machine if it’s not already installed. Actually it should have been quite straightforward; just issue an apt-get command, right? But during configuration it displays a curses dialog for MySQL root password. This of course blows your automated configuration plans. We will seed this value to debconf database to avoid this dialog.

First let’s have a look at the supporting code:

from __future__ import with_statement
from fabric.api import *
from fabric.utils import warn

def apt_get(*packages): sudo('apt-get -y --no-upgrade install %s' % ' '.join(packages), shell=False)

Our apt_get fabric command issues apt_get install that answers yes to all questions and does not upgrade if the requested package is already installed. Now let’s take a look at the main command that installs mysql-server:

def install_mysql():
    with settings(hide('warnings', 'stderr'), warn_only=True):
        result = sudo('dpkg-query --show mysql-server')
    if result.failed is False:
        warn('MySQL is already installed')
        return
    mysql_password = prompt('Please enter MySQL root password:')
    sudo('echo "mysql-server-5.0 mysql-server/root_password password ' \
                              '%s" | debconf-set-selections' % mysql_password)
    sudo('echo "mysql-server-5.0 mysql-server/root_password_again password ' \
                              '%s" | debconf-set-selections' % mysql_password)
    apt_get('mysql-server')

First we want to make sure mysql-server is not already installed. For this reason we issue dpkg-query --show mysql-server. Note that if this command fails then mysql-server is not installed and we can proceed otherwise we want to return. We hide the ugly error messages by running our dpkg-query command within a settings context:

with settings(hide('warnings', 'stderr'), warn_only=True):
    result = sudo('dpkg-query --show mysql-server')
if result.failed is False:
    warn('MySQL is already installed')
    return

The rest of the code is pretty straightforward. We prompt to the user running fabric for the root password and seed its value twice into the debconf database.

sudo('echo "mysql-server-5.0 mysql-server/root_password password ' \
                            '%s" | debconf-set-selections' % mysql_password)
sudo('echo "mysql-server-5.0 mysql-server/root_password_again password ' \
                            '%s" | debconf-set-selections' % mysql_password)
apt_get('mysql-server')

Finally, having finished our little dance, we install mysql-server. I hope this helps some automated deployment believer out there.

Bookmark and Share

My Idea Of The Django Blogging App™

Wednesday, April 28th, 2010

I am not going to talk about yet another Django-based blogging engine in this post. There are a number of blogging apps which try to be like turn-key solutions, like a WordPress blog. I have skimmed through the code of many such apps, but haven’t used one yet. Some of them are really high quality apps. What I have in mind is somewhat different though. I would like an app that would allow me to build a blog that satisfies my projects specific requirements.

Let me reiterate the last sentence. Having a Django-based blog just because Django is fashinable is a little dumb in my opinion. If Django-based X blogging engine suits you better than anything else, use it. Why not? But my personal choice of blogging engine is WordPress1. The value of a Django blogging app, for me, is in adding a blog to a Django project. And different projects might have different requirements. So my idea of a Django blogging app is one that is highly configurable and highly extendable.

On the other hand I don’t need the convenience of clicking a checkbox on a polished UI. I can write a function. Or I don’t necessarily need it to, say, provide a navigation menu. There are apps that do that. Even if there wasn’t it shouldn’t be the blog app’s job. So I am not looking for an instant-blog. I have a Django app in my mind, nothing more.

What Should Be Left Out

Basically any feature that can be provided by another reusable app should be left out. Why should we re-implement something that is already done… and reviewed by others… and tested. Of course this doesn’t necessarily mean providing no convenience functions.

  • No admin. Because we already have one.
  • No theming. For the love of Flying Spaghetti Monster, you don’t need any theming other than what django.template offers. Pre-built themes are for turn-key solutions.
  • No comments or contact forms. (See django.contrib.comments and django-contact-form)
  • No official markup format (or formats). This can be handled in the templates without difficulty. But, maybe, pluggable content filters is a good idea. I haven’t made up my mind on this one entirely. It won’t use any markup format by default, that is for sure.

What Should Be Included

Remember, every project has a different set of needed features for its blog. Some need catagories, some need tags and some others need both. But it would end up as a disaster if we implemented each one of those features into a single app. Instead I think it should consist of many small apps that work together. But I wouldn’t want to end up having huge spaghetti of apps that all depend on one another, like Pinax does. A minimal amount of core apps2 and then everything else should be optional. By optional I mean you don’t have to install packages you won’t need.

I think the components (apps) should be activated via adding to INSTALLED_APPS and configured with settings. I can’t think of any parameter that needs to be changed dynamically, so why not use the established way of doing configuration in Django.

Two must have features for such a blogging app are previews and scheduled publishing. It is possible that you sometimes write a post quickly and publish it immediately. But I suppose nobody will say they don’t care about these two features.

Built-in feeds and sitemaps are also nice to have.

Multiple instances of this blogging app running on the same project? À la admin. I can’t make my mind on this one. Sure it would be a nice feature. But it could complicate the code. Peehaps too much for a not so common case.

What do you think about the general idea? Are there any other must-have features? Would you be willing to learn a new app when you are already comfortable with another blogging app?


1: Even though it’s written in the abomination called PHP. But since there are plugins for everything I don’t have to touch the code.

2: One sounds like a good number, if possible.

Bookmark and Share

What’s New in django-formfieldset 1.1

Saturday, March 20th, 2010

I have just released 1.1 version of django-formfieldset. I has been almost a year since version 1.0. Here is a summary of changes for this version:

New Example Project

There is a new and improved example project. It is designed to be some sort of documentation at the same time. When you run the example project and visit different pages you will see, for each examle, Python code, template code, text of rendered result and finally the result embedded.

If you have Pygments installed all the code will be nicely highlighted.

Fieldset & FieldsetMixin Improvements

Fieldset definitions are validated now. An exception will be raised if all of your fields are not included exactly once.

Template strings that are used by as_table, as_p and as_ul methods are now class attributes. You can simply override them instead of writing your own as_* method.

FieldsetMixin provides a fieldset_dict attribute. This dictionary has slugified fieldset names as keys and Fieldset instances as values. Your fieldset declarations can still be accessed from fielsets attribute.

Rendering Improvements

There are two rendering related improvements: individual fieldset rendering and renderform template filter.

Fieldset objects have as_table, as_p, as_ul methods just like forms. Errors from hidden fields are handled correctly, but you still need to call non_field_errors() to get the top level errors. Also it is template author’s responsibility to make sure all the fieldsets are rendered.

If as_* methods are not enough for you, with renderform filter you can render your forms or Fieldsets through a custom template. It works like this:

{{ form.fieldset_dict.mytitlerenderform:"myapp/mytitle_fieldset.html" }}

If you call it without an argument formfieldset/form.html template will be used.

Bookmark and Share

Developing Reusable Django Apps: Signals

Thursday, March 4th, 2010

I wrote “signals provide a great way to propagate the events generated from your app” earlier. I think reusable apps should avoid hardcoding any kind of event handling and send signals instead. App consumers might prefer an email over an on-screen notification. They may even choose to ignore the event silently. A reusable app should give this choice to the consumer.

Taking advantage of signals doesn’t necessarily mean providing no sane defaults. You can send signals and provide default event handling. Here is a couple of ideas how this can be done:

  • Your app can check if there are any listeners and connect the default handlers if there is none.
  • You can ship an auxiliary app that connects default handlers when added to INSTALLED_APPS.

I personally prefer the second approach since it’s simpler and more explicit. I’m sure there are other ways to implement default handlers for signals.

Dispatch_uid

Don’t forget to assign a unique dispatch_uid for each connect() call. Otherwise your handler can get connected twice. I would also suggest you to use both module path and your handler function’s name in your dispatch_uid:

"%s.%s" % (os.path.splitext(__file__)[0].replace(os.sep, '.')[1:],
           handler_name)

Now I should take my own advice and replace hardcoded User.message_set.create()s with signals in django-simple-friends.

Bookmark and Share

Web Site Performance Optimizations

Monday, February 22nd, 2010

Recently I have done some optimizations to make telvee a little faster using django_compressor and making sprites for background images. Good news is substantial changes to development environment and the design wasn’t required. I’ll get into details below. But first I’d like to write about the theory a little bit.

I follow (and read with great interest) Steve Souders’s blog High Performance Web Sites. I must admit I was sceptical about it at first; spriting images, individual different loading behaviours of browsers… I thought they were premature optimizations. But I realized I was wrong as I continued to read. Steve Souders is an expert on high performance web sites and what he preaches are realistic techniques, backed by test results most of the time. If you are not following, I suggest you add it to your RSS reader.

Optimization Techniques

We can explore the techniques in two main categories:

  1. Techniques to reduce the data to be transferred.
    • Minifying: Minification is removing comments and unneeded whitespace in CSS and JavaScript files. Compilers like YUI Compressor and Closure modifies JavaScript code to compress even further, without any changes to the functionality.
    • Gzipping: Web browsers accept gzip encoded content for a long time. I have just compressed a 13 KB text file down to 4 KB. A two thirds compression ratio is not bad at all.
  2. Techniques to reduce the number of HTTP requests.
    • Combining: CSS and JavaScript files can be combined together into a single file and therefore a single HTTP request. Gzip may be more efficient on these files. In a similar way background images can be merged into a sprite and then reconstructed again using their coordinates on that sprite.
    • Data URI’s: Images (or other file types) can be embedded into CSS (or JavaScript or HTML) using data: URI‘s. Extra HTTP requests for those resources can be avoided this way.

You might think it’s fine to perform all these optimizations, but what happens when I want to make some changes to my combined, minified JavaScript file? Instead of applying these techniques blindly, it’s best to follow a sensible plan for implementing these optimizations:

  • First of all everything that can be automated should be automated. Regarding the example above script files should stay uncombined and uncompressed in the development environment and optimizations should be applied when the application is published. Taking it a step further we can have the application detect changes in those files and update optimized versions automatically. (django_compressor works this way)
  • I was worried that spriting would complicate managing the design. But I have seen, on the contrary; if images each sprite contain are choosen carefully it makes the process easier. Start combining images that belong to the same design element. Avoid complex arrangements, stick with horizontal or vertical stacking as much as possible. Don’t forget to leave transparent spaces between items and sprite border. Try to combine images that are loaded on the same page, avoid loading a sprite for only half of it’s elements. Don’t force yourself to combine all images, if you follow the guidelines I have mentione they won’t.
  • When performing optimizations don’t forget to use easily available tools. You can use YSlow for general analysis, SpriteMe for image combining tips, CompressorRater to compare different compilers’ performance on your scripts. I would like to note that Steve Souders is the developer of first two.

Telvee Results

I didn’t think about performance at all when I started developing telvee. Too many CSS files and too many images were being loaded. Here is what it looked like before optimizations:

 # of requestsload (KB)
Homepage25~85
Cup Detail48~80

Then I have installed and configured django_compressor. I used YUI Compressor for both JavaScript and CSS. I have created sprites and modified CSS files manually1. Then I deployed these changes and measured again:

 # of requestsload (KB)
Homepage12~70 (~160)
Cup Detail14~64

In the load column of Homepage, the number in parens is the actual load. But the design of homepage is changed with this upgrade and a new 90 KB image is being loaded now. So I have accepted 70 KB in my calculations. The result of optimizations are as follows:

 # of requestsload (KB)
Homepage52%17%
Cup Detail70%19%

Django_compressor and Data URI’s

Django_compressor, developed by Christian Metts, helps you to apply optmizations I have mentioned above easily to your Django projects. You can see my fork here where I have merged some other branches and added a little bit of code myself.

Using compressor.filters.datauri.CssDataUriFilter in data-uri branch of this repository, you can embed linked files within your CSS files. It will only embed files less than or equal to 1024 Bytes (1 KB) by default. You can change this limit by setting COMPRESS_DATA_URI_MIN_SIZE in your settings.py.

There are a couple of things to pay attention when you convert your references to data: URIs. Firstly file contents are base64 encoded which means approximately one third increase in size. It’s up to you to balance between increased bandwidth and reduced request counts2. Another thing to watch for is multiple references to the same file will end up embedding the same data many times. The solution to this problem is to reduce all references to one3 but this might break your CSS arrangement strategy.

Please test django_compressor’s data: URI support and tell me what you think. If you haven’t applied optimizations I mentioned above, you should. Thanks to django_compressor they are quite easy to implement on Django projects.


1: I would like to add automatic sprite building/linking support to django_compressor sometime.

2: With Today’s modern connections 1~2 KB increase is a good price for 1 less HTTP request..

3: http://meiert.com/en/blog/20090401/why-css-needs-no-variables/

Bookmark and Share