Web


16
Feb 12

HTML input placeholder handling with jQuery

There’s a million of small jQuery snippets which handle input placeholders (ie. the help/explanation text that’s shown until you actually write something in the field), and when HTML5 gets widespread, they’ll all be obsoleted.

But nevertheless, here’s another one.

this post is more for self reference, but if it’s useful to you, be my guest.


25
Sep 11

Quickstart your Django project in 60 seconds

Sane-default boilerplate to avoid repeating yourself each time you start on a new Django project.

Django comes with a lot of batteries included, but it takes some time to set them up. And usually, the initial few steps are always the same. We can refactor these into a boilerplate empty Django project, and use it as a base for new projects, instead of starting from scratch each time.

That’s what I did with DJ Skeletor. It’s an empty, relocatable (ie. uses no hardcoded paths) Django project, set up to my liking and with a few Django apps that I use in virtually all projects (impatient? jump directly to the examples).

Django settings

Default Django settings organisation is rather simplistic – it’s just one file. But as soon as you deploy the project to somewhere else than the computer you’re writing it on, you’re going to have at least two sets of settings: development and production.

There are several ways to handle this in Django. I prefer the following:

  • Settings module is split into several submodules and lives in settings/ directory.
  • Settings that are the same for both development and production live in settings.base module.
  • Development-specific settings are provided by settings/dev.py, and production-specific settings are in settings/prod.py; a symbolic link settings/local.py points to the one that should be used in a specific environment.
  • The settings module imports settings.base and settings.local

This allows me to have all of the settings code (even per-environment settings) in the git repository. By using the symlink (instead of host name or IP address as some do), I can activate the variant I need without regards to the rest of the environment. This allows me to eg. have several variants of the same project running on the same machine.

Database

A test SQLite database is set up for the development environment. The database file is configured to be test.db in the project root by default.

South

South is an awesome Django app for handling database schema migrations (ie. table/field changes when you modify your models). If you’re using a database with Django, you want to use South as well.

Django Debug Toolbar

Django Debug Toolbar is very handy for inspecting what happens when you request a page from Django. It lists things such as SQL queries executed (including how long did they take and why they were executed), signals, logging, exception, request params, etc. I use it all the time for finding and fixing suboptimal database queries.

The app usually defines a white-list of client IPs for which to be shown. As I’m not on a static IP, I find it more useful to have the toolbar show all the time when in development environment, and never when in production.

Sentry

Sentry helps with exception logging and viewing for your Django project. It can handle multiple Django installs where the logs can be managed from a single place, or it can be used on a per-project basis. The latter is how it’s preconfigured in DJ Skeletor.

Besides logging the exceptions, Sentry can also catch normal logs you create with Python’s logging system. This is also preconfigured in DJ Skeletor.

Example

Enough talk, let’s see some action. First, we’ll create a virtual environment (you do use virtualenv, right? if not, you should) and install prerequisite packages:

    virtualenv --no-site-packages myenv
    cd myenv
    source bin/activate
    pip install django django-debug-toolbar south django-sentry

Next, we’ll clone the boilerplate project:

    git clone https://github.com/senko/dj-skeletor.git myproject
    cd myproject

Let’s activate the development environment and prepare the database:

    cd settings
    ln -s dev.py local.py
    cd ..
    python manage.py syncdb
    python manage.py migrate sentry

All done, let’s run it!

    python manage.py runserver

See? Piece of cake. With boring initial set-up out of our way, we can focus on building an awesome web site or app.

DJ Skeletor is open source and is available on GitHub. Feel free to use it or base your own boilerplate on it – if you do, please share your thoughts in the comments.

Bonus: HTML boilerplate

If you’re a programmer and couldn’t design if your life depended on it, it’s useful to have the user interface boilerplate handy as well.

If you need a clean, well designed (but definitely not unique or artistic) user interface for your HTML app, I heartily recommend Bootstrap, created and open sourced by the fine folks at Twitter.

If you do need to make a proper, unique design, again no need to start from zero: use HTML5 boilerplate which takes care of a myriad little gotchas for you; and there’s a mobile option as well.


21
Sep 11

If your server never reboots …

… you never verify that it’ll boot correctly.

The Linode box hosting my VPS had a scheduled maintenance downtime for a couple of minutes yesterday, so they shutdown my VPS and rebooted it afterwards. This was a first downtime since I set it up around 7 months ago. I saw in the VPS management console that the server was correctly rebooted, so I went to sleep, not a care in the world.

BUT

  • Nginx didn’t find an (ephermal, removed on reboot) cache directory and failed to start altogether.
  • MongoDB found a stale lock file and refused to start until it’s removed.
  • I ran a couple of things in screen (!). Which didn’t start, obviously.

Oops

Why?

Let’s practice root cause analysis with five why’s, shall we?

  • Why didn’t NginX start? It used a directory I had manually created in /tmp at some point.
  • Why was there a stale MongoDB lock file? No idea – it was stopped with SIGTERM – possibly the current command lasted so long the reboot procedure decided to send KILL as well.
  • Why did I run things from screen? Because it was a quick hack, and the server never needed to shut down or rebooted.
  • Why didn’t I immediately notice something is wrong? I didn’t have Pingdom set up properly and never received the notification.
  • Why didn’t I ever try rebooting (or crashing) the server, to see if it would go back up properly? I didn’t want to create “unneccessary downtime”.

Lessons learned

  • Test what happens when you crash the server. A bit of downtime (at the least-inconvenient moment) is worth it to prevent a longer bit of downtime at the most-inconvenient moment. Test that monitoring gives you the notifications, as well.
  • Ensure things are properly automated, even for quick hacks.

Let this post remind me to not cut these corners again. And you, dear reader, check whether you’ve cut them as well.


21
Jun 11

Sparrw looking for a new home

Image credit: rspb.org.uk

I’ve decided to find Sparrw, my Twitter backup / search service, a new home and good hands that will have the time and energy to maintain and improve it.

I’ve built Sparrw a couple of years ago, and since then it got around 600 users, and has processed over 3 million tweets. Recently it has been hitting some resource limits on my VPS, and I have decided that I don’t really have the time and interest in maintaining and improving it. Instead of shutting it down, I’d prefer to find a new home for it.

Sparrw is built in Python using Tornado Web framework, and uses MongoDB for storage.

I’d be giving away the domain (sparrw.com), the code, and the database content to the new owner. The owner would be required to keep the database contents private, and to continue providing the service for some reasonable amount of time.

If interested, mail me (senko@ this domain), or contact @senkorasic on Twitter.


18
Jun 11

A Django setup using Nginx and Gunicorn

This is a howto on setting up Django on a Linux (Ubuntu) system using Nginx as a reverse proxy and Gunicorn as a Django service.

Django, Gunicorn, Nginx

The conventional way to run Django in production these days is using Apache2 and mod_wsgi. While there’s nothing wrong with that approach, I prefer Nginx. I also like to be able to control Django server separately from the web server.

There are several production-ready servers for Django. The best seem to be Gunicorn and uWSGI, and Gunicorn seems the best supported and most active project.

When running Django server separately from the web server, we need a way to start, stop and restart the Django server. A popular way for doing it in Django world is Supervisor, altough, for Ubuntu users, Upstart might be less hassle.

You probably already have a Django project you want to deploy, but for completenes’ sake, the steps here will use an empty toy “Hello World” Django project:

Preparation

First things first – you are using virtualenv, right? If not, you should.

  virtualenv --no-site-packages test
  cd test
  source bin/activate
  pip install gunicorn django
  django-admin.py startproject hello
  cd hello
  # to test the base setup works
  python manage.py runserver 0.0.0.0:8000

Gunicorn

Testing Django with Gunicorn is as simple as:

  gunicorn_django -b 0.0.0.0:8000

For production, we might want a bit more options, and we want to make sure the server is executing in the correct environment. The easiest way is to create a shell script to set it all up:

  #!/bin/bash
  set -e
  LOGFILE=/var/log/gunicorn/hello.log
  LOGDIR=$(dirname $LOGFILE)
  NUM_WORKERS=3
  # user/group to run as
  USER=your_unix_user
  GROUP=your_unix_group
  cd /path/to/test/hello
  source ../bin/activate
  test -d $LOGDIR || mkdir -p $LOGDIR
  exec ../bin/gunicorn_django -w $NUM_WORKERS \
    --user=$USER --group=$GROUP --log-level=debug \
    --log-file=$LOGFILE 2>>$LOGFILE

The number of workers is number of worker processes that will serve requests. You can set it as low as 1 if you’re on a small VPS. A popular formula is 1 + 2 * number_of_cpus on the machine (the logic being, half of the processess will be waiting for I/O, such as database). YMMV.

Don’t forget to mark the script as executable (chmod ug+x script.sh). You can run it from the command line for testing. Note that Gunicorn by default uses 127.0.0.1:8000 address (the same as Django debug server), which is fine if Nginx is on the same machine – you usually don’t want to have it wide open to anyone, and instead let Nginx handle incoming connections.

If you want to run several Django servers on the same machine, just make sure each uses a different port number.

Supervisor

Supervisor has extensive documentation, and this blog post is big already, so I’ll just point you to the official docs. The config file for running our server (/etc/supervisor/cont.d/hello.conf on Debian/Ubuntu) should look like this:

  [program:hello]
  directory = /path/to/test/hello/
  user = your_unix_user
  command = /path/to/test/hello/script.sh
  stdout_logfile = /path/to/logfile.log
  stderr_logfile = /path/to/logfile.log

Test it with supervisorctl {start,status,stop} hello (as root).

Upstart

Ubuntu alternative is Upstart, which has a similar config file (/etc/init/hello.conf). An example:

  description "Test Django instance"
  start on runlevel [2345]
  stop on runlevel [06]
  respawn
  respawn limit 10 5
  exec /path/to/test/hello/script.sh

Test it with service hello {start,status,stop} (as root).

Update 2011-11-14:For completeness of the Upstart setup configuration one has to add a soft link in /etc/init.d for a file named hello to /lib/init/upstart-job. So the following instruction should be executed after the .conf file has been created in /etc/init:

  sudo ln -s /lib/init/upstart-job /etc/init.d/hello

Update 2011-11-14: Christophe Meessen found and fixed several errors in the procedures and config files, and also provided info about the extra Upstart configuration I missed. Thanks Christophe!

Nginx

If you don’t have it set up, you should also install Nginx. The install procedure varies from system to system. On Debian and Ubuntu systems, it’s as simple as apt-get install nginx, and other Linux distributions usually have equivalent commands.

Nginx is mostly a drop-in replacement for Apache for serving static files, though there are some things to set up if you need to run PHP code as well.

For our setup, we need Nginx to serve as the reverse proxy for the upstream server(s). To do so, we add a server section to the config file:

server {
    listen   80;
    server_name example.com;
    # no security problem here, since / is alway passed to upstream
    root /path/to/test/hello;
    # serve directly - analogous for static/staticfiles
    location /media/ {
        # if asset versioning is used
        if ($query_string) {
            expires max;
        }
    }
    location /admin/media/ {
        # this changes depending on your python version
        root /path/to/test/lib/python2.6/site-packages/django/contrib;
    }
    location / {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_connect_timeout 10;
        proxy_read_timeout 10;
        proxy_pass http://localhost:8000/;
    }
    # what to serve if upstream is not available or crashes
    error_page 500 502 503 504 /media/50x.html;
}

Ubuntu and Debian systems keep Nginx config files in same layout as for Apache, so the above cold be added to /etc/nginx/sites-available/hello (and enabled by symlinking from sites-enabled directory). Use nginx -t for config test and nginx -s reload to reload the configuration.

That’s it

And that’s it. The services are really quite simple to set up once you know what goes where, the setup is flexible and performant, and the server environments are isolated so it’s possible to host many different services with varying requirements on the same machine.

Have improvements on the above or your own helpful tips, or found an error in the post? Share in the comments.