Django UI in 2005 vs Django UI in 2014

I stumbled across my favourite ever tech talk, the legendary Snakes and Rubies event where Django met Rails, back in 2005. One thing caught my eye while re-watching every minute of the 3-hour event: the Django Web site and the Admin Site app look almost exactly the same today than they did 9 years ago.

Screen capture image from Djangoproject.com as it was in 2005

Still image of Djangoproject.com from the Snakes and Rubies video, 2005.

 

Screen Shot 2014-09-17 at 22.50.28

Djangoproject.com, 2014.

 

Still image from Snakes and Rubies video

Still image of Django Admin Site from the Snakes and Rubies video, 2005.

 

Screen Shot 2014-09-17 at 22.58.44

A screencapture of one Django Admin app (in Finnish — it still talks also Welsh, among others), 2014.

 

I don’t want to comment this in any other way but to notice that it’s quite amazing that Djangoproject.com still looks so good and I actually like using it every single day — it works really well. Way better than most newer homepages of other open source projects.

Granted, the Admin app looks a bit more dated, but not too much. And for its purpose, it too works still really well.

Wilson Miner is one kick-ass designer.

And Wilson, it’s not just a Web site. 🙂

(This post was originally published on my other blog called Spinning Code.)

Why I Love Python And Hate Java

Java and Python processes

These backgroundtasks running Java processes — after tuning for minimun memory consumption — eat 20 times more memory than my whole Python Web app. Nice.

(This post was originally published on my other blog called Spinning Code.)

My Stumblings With Logstash And Kibana

I’ve started a project of building a Web toolkit for DJs and in the process I’m constantly learning more about running a Web server [^1]. Being a huge data nerd one of the first things I wanted to install on the Linode VPS were Logstash and Kibana. Lots of data. Lots of pretty graphs. Drool.

[^1]: I’m a language geek. And, being a nerd and pythonist, I’m stealing my writing style from Django. How nerdy is that! In a related note, English isn’t my native language (hello from Finland!) and I know that I’m not very good at writing English. But I try. And I am a language geek.

And it was supposed to be easy.

But based on 15 years of experience, it very seldomly is.

Logstash and Elasticsearch-backend installed nicely (on Ubuntu 14.04 Server) from a .deb package, Kibana was basically all-manual install from source, but it’s JavaScript and a NginX vhost is very simple to config, so it wasn’t so bad. First test inputs worked perfectly and the first little bits of data were already making the Kibana look cool. How nice!

But after adding some initial configuration files to gather logs from NginX, Apache and syslog, Logstash wouldn’t collect data when started from init script. I was dumb enough not to try running it with –verbose flag, so it was only after a couple of hours (yes) head scratching, an unhelpful visit to Logstash irc channel, and a post to google user-group and a very fast and helpful answer when I figured out that I needed to set up a SINCEDB_DIR environment variable for the script to know where to store its internal metadata. Somehow that seemed really weird problem to have for a program that had been installed as a service from a .deb package.

In an unrelated learning experience, I had hard time figuring out how the Logstash configuration files work. The documentation let me thinking that I was supposed to write one config file with input, filter and output section for every log I wanted to consume. But it turns out that while you can have multiple inputs and filters, you only should have only one common output section for all of the logfiles (if you want to collect all of the logs to one same place). I figured this out only after the data was showing multiplied in Kibana. So eventually I ended up collecting all of the configs in one big file with huge input section covering all of the logs I wanted to parse, huge filter section matching the inputs, and one small output section that forwarded everything to the Elasticsearch backend.

Kibana

But at the end of the day,  I got myself an awesome, beautiful frontend to all of the server logs. And yet another a reminder that I’m not a true nerd but a wannabe.

(This post was originally published on my other blog called Spinning Code.)

Redirect to Custom URL After Saving in Django Admin

Here’s a quick and dirty admin hack that can save you lots of time if you’re used to browsing between editing objects in admin and vieving the results on a live site.

Put following change_view-method in your admin.py and now you can make links to admin change page that return you back to where you came from:

 class BlogEntryAdmin(admin.ModelAdmin):      ...      def change_view(self, request, object_id, form_url='', extra_context=None):         result = super(BlogEntryAdmin, self).change_view(request, object_id, form_url, extra_context)         if request.GET.get('return_to', False):                         result['Location'] = request.GET['return_to']         return result 

In your template you’d have something like:

 {% if request.user.is_staff %}     <a href="/admin/blog/blogentry/{{ entry.pk }}/?return_to={{ entry.get_absolute_url }}">Edit in admin</a> {% endif %} 

Of course, if you’re smart, you should convert any places that need this kind of functionality with frontend editing tools so you can forget going to the Admin in the first place. But meanwhile, those few lines of code may come in handy!

Convention Over Configuration: Django Settings

I’ve been working with Django for over five years. Even though I don’t consider myself as a programmer, one of the advantages I seem to have over less experienced Django developers is that I’ve developed (together with our team) strong conventions on how I work. This makes developing faster and generally more enjoyable.

Conventions are a tool for efficient workflow and to help read and understand code by other people better. Like so many tools, conventions too can be taken to extremes to the point when then they become magic (Rails, anyone), which in Python community is considered a bad practice.

The Python community has a name for code that feels right: that kind of code is pythonic. Good, pythonic conventions are logical and easy to remember but also explicit. In other words good conventions should make sense when you see them first time.

Django has many documented conventions (like that models should live in models.py and views in views.py) and even coding guidelines but there are many places that could use more stronger or better ones. This article is first in a series of discussing conventions related to Django. My goal is not to impose these opinnions to anyone but to share what I’ve learned while working with Django for over five years.

Conventions for Django settings

Settings are not the sharpest tool in the Django shed. This article is not about crying over the design details, but to make working with them easier.

Always use relative paths

One of the first hurdles one comes up with Django settings is that they aren’t very portable. The MEDIA_ROOT, STATIC_ROOT and TEMPLATE_DIRS settings are problematic because the given examples are absolute paths (like "/home/media/media.lawrence.com/media/"). Simply by defining your paths as relative you can free yourself from this and move your projects around without breaking your settings.

A good convention is to define a PROJECT_ROOT setting and make every path in your settings relative to that root. Django settings file is just an ordinary Python module (well, not really, but in this respect it is) so you can use Python to do this:

 import os  PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../')) MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media') 

Turn settings into a package

Second problem you almost immediately have is a problem of multiple settings files. You can make your life a lot easier by moving settings from ´settings.py´ into several different files in a directory called settings. (Remember that to python /path/to/your_project/settings.py and /path/to/your_project/settings/__init__.py are the same thing.)

Also, because there usually is at least two almost equal settings files (for development and production), it’s smart to differentiate common settings and the ones that change for every machine. For this, you can move common settings to common_settings.py and then just import that to your other settings files:

 from common_settings import * 

Now you can either add an empty __init__.py to the settings directory and refer to settings as DJANGO_SETTINGS_MODULE=myproject.settings.myenv or you can symlink the current environment settings as __init__.py.)

You could end up with a directory structure like this:

myproject/     manage.py     urls.py     settings/         __init__.py         common_settings.py         dev_adrian.py         dev_jacob.py         dev_simon.py         production.py         staging.py 

Now you have one file for common settings and one for each individual environment and they all can live together in version control.

Store sensitive data outside of settings files

Another problematic thing about Django settings is that you’re supposed to put things like passwords and API-keys in there. For development environment this seldomly is a problem but what about version control and different settings for staging and production environments?

At this point it is a no-brainer: just put all sensitive data to a file outside of version control and import that:

 from secrets import *

Now we don’t need to think about storing sensitive data in the version control and we just need to make sure that secrets.py is properly protected in the production environment.

Conclusion

Conventions are good and we need them. By using these few simple conventions you can make your Django projects more portable, secure and pythonic.

More important than the implemention details is that you follow some conventions. I’m a strong believer of customization and that you should make your own conventions that best fit you yourself or your team. (This, obviously, is not something you shouldn’t do when writing your first Django apps but instead when you have been working with Django a while so that you have a general feeling of what works for you and what doesn’t.)

Next in the series I’ll talk about arranging the overall project structure and the tools that help keeping your project evolving.

Meanwhile, any comments or tips that you’d like to share?

Controlling iTunes from Python

django-tunesBack in 2008 I stumbled on then new API on Mac OS X called Scripting Bridge that enables easy controlling of Cocoa apps (like iTunes) from scripting languages like Python.

I wrote simple proof-of-concept Django app that turned my iPod touch into a remote control. It was cool, even though it didn’t do very much. Now there’s of course native iOS apps for this but I was somewhat surprised that there are still not much code for controlling iTunes via Web. I misplaced my original code so I decided to write it again — just for fun.

So here’s a working class for controlling iTunes, in 58 lines of Python:

 # -*- coding: utf-8 -*- from Foundation import * from ScriptingBridge import *  class iTunes(object):     """     A helper class for interacting with iTunes on Mac OS X via Scripting     Bridge framework.      To use this, launch iTunes and make sure a playlist or an album is ready.      Usage:      >>> player = iTunes()     >>> player.status     'playing'     >>> player.current_track     u'Maison Rilax'     >>> player.current_album     u'Maison Rilax'     >>> player.current_artist     u'Lemonator'     >>> player.pause()     >>> player.status     'paused'     >>> player.play()     >>> player.next()     >>> player.current_track     u'Not Your Game'      """      def __init__(self):         self.app = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")      def _get_status(self):         if self.app.playerState() == 1800426320:             return "playing"         elif self.app.playerState() == 1800426352:             return "paused"         else:             return "unknown"     status = property(_get_status)      def _get_current_track(self):         return self.app.currentTrack().name()     current_track = property(_get_current_track)      def _get_current_artist(self):         return self.app.currentTrack().artist()     current_artist = property(_get_current_artist)      def _get_current_album(self):         return self.app.currentTrack().album()     current_album = property(_get_current_album)      def _set_volume(self, level):         """         level should be an integer between 0-100.         """         self.app.setSoundVolume_(level)      def _get_volume(self):         return self.app.soundVolume()     volume = property(_get_volume, _set_volume)      def pause(self):         self.app.pause()      def play(self):         # According to AppleScript documentatin there should be a .play()         # method, but apparently there isn't. So we fake it :)         if self.status == "paused":             self.app.playpause()      def next(self):         self.app.nextTrack()      def previous(self):         self.app.previousTrack() 

This could be easily refined into something potentially interesting like a native REST API for iTunes. The downside, of course, is that it only works on a Mac. (The code is available also on BitBucket.)

A somewhat related is a project called Mopidy, which is a Python powered MPD music server for the awesome Spotify music service (that is suposed to finally be launching in US very soon, I hear.). I’m not at all familiar with MPD but I think it wouldn’t be very difficult to build a Django frontend to a server like Mopidy to get a very slick Web-based interface to your music. Now that would be cool.

Time to Wake Up

It’s been too long since last entry in this blog. I’ve had lots of good and bad excuses, but I’ve also collected a healthy pile of ideas for future topics, like “how to install Django on Mac OS X with just one command” and “best practices for installing dev environment on Mac OS X Lion”.

Just to get started with installing Django on Mac OS X, it’s as easy as saying sudo easy_install django in the Terminal.app. But there’s more to it. We’ll get to that in another post.

And for the record, this blog was never dead, it just took a three year vacation.