Django, Haystack and Elasticsearch – Part 1

I’m wrapping up a little side project at the moment (more on that very soon) which required full-text search, autocomplete, and a few other bits of search related functionality.

After some research I landed upon the combination of Elasticsearch and the awesome Django application Haystack.

First step was to get Elasticsearch up and running locally on OS X…

1) Download latest zip from http://www.elasticsearch.org/overview/elkdownloads/. A good spot is:

/opt/elasticsearch-1.1.x

2) Create the following directories:

/opt/elasticsearch-1.1.x/data
/opt/elasticsearch-1.1.x/work
/opt/elasticsearch-1.1.x/logs

3) Add the following to your .profile (allows you to run Elasticsearch from the command prompt without the full path):

4) Update the following values in the Elasticsearch config file:

5) Ensure all requirements are installed (django-haystack, pyelasticsearch, requests, simplejson):

6) You should now be able to start Elasticsearch:

7) Add Haystack to your Django config:

8) After you’ve added your search indexes, you can use manage.py to rebuild the search index:

$ python manage.py rebuild_index

Django Template Tag for Truncating Characters at a Word Boundary

The default truncatechars template tag truncates a string if it is longer than the specified number of characters but does so exactly at the character count, irrespective of whether it’s the middle of a word or not.

Here’s a smarter version that clips the text at the word boundary:

Populate and Validate a Django ModelForm using API Data with the Generic CreateView

I ran into a slightly tricky use case the other day wherein I needed to grab some data from an API, validate it against a Django ModelForm, and only show the form to the user if there are errors during the validation.

If the form is shown, the error messages from the validation should appear along with a pre-populated form (using the data from the API). Otherwise, the ModelForm should submit and be saved immediately, without any user interaction.

After digging through Django’s code it became clear that to accomplish this, I needed to override a couple methods of the generic class-based CreateView.

Here’s the code for the view…

As you can see, the first thing I’m doing is overriding get_form_kwargs so that it can take a new argument – data. This allows me to pass in the data pulled from the API to the form. Since I want the form to be saved immediately, without any user interaction, I set both the initial and data properties to the passed in data.

The get method is now a simple exercise in pinging the API for data, placing it into a dictionary the form can use, and calling form.is_valid() to see if the data passes the validation.

If it doesn’t, the form is rendered (complete with validation errors and pre-populated data) and the post method handles when the form is submitted by the user.

Connecting to a Local Django Server from VMware Fusion on OS X

Even though we’d all like to believe that IE is dead and there’s no need to test it any more, that simply isn’t the case yet. But debugging via a remote server is a pain when you can easily use a local Windows VM running on VMware Fusion.

The following will get you up and running with a local Django server (running on port 8000) and any Windows OS. Note that I’m using the latest VMware Fusion (version 6) but the same steps will work with previous versions as well.

First, shutdown the VM (power it off completely) and then open it’s settings. Click Add Device, select Network Adapter and click Add.

Now you’ll see 2 network adapters for the VM. Click the new one to edit it.

Switch the connection type to Private to my Mac.

Next, grab the IP address your Mac is using for this private network. Open a terminal window and run “ifconfig vmnet1”.

$ ifconfig vmnet1
vmnet1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 00:50:56:c0:00:01
inet 172.16.85.1 netmask 0xffffff00 broadcast 172.16.85.255

The last line is the important one. Make note of the IP address next to inet, in this case mine is 172.16.85.1. Because we made a new private network, this IP should hopefully stay the same and you won’t have to worry about messing with the configuration again down the road.

Next, start your Django server using the IP address 0.0.0.0:

$ python manage.py runserver 0.0.0.0:8000

Now start the VM back up and open up IE or your browser of choice. Go to http://172.16.85.1:8000 (make sure to substitute the IP address you made note of above). Voilà, you’re browsing your local Django server from Windows.

If you’d like to use something more memorable for the host name, go ahead and edit the Windows host file (it can be found here – C:\Windows\system32\drivers\etc\hosts). Add the new host to the list of entries:

127.0.0.1      localhost
172.16.85.1    mysite.local

I found that xxxx.local worked reliably while other host names were hit and miss.

Finally, flush the DNS on your VM for the host changes to take effect:

ipconfig /flushdns

You can now reach your Django site at http://mysite.local:8000.

Django 1.7, Signals & AppConfig

The alpha of Django 1.7 was released recently and I’ve started to experiment with the new applications feature.

Applications include some combination of models, views, templates, template tags, static files, URLs, middleware, etc. They’re generally wired into projects with the INSTALLED_APPS setting and optionally with other mechanisms such as URLconfs, the MIDDLEWARE_CLASSES setting, or template inheritance.

The power of this feature is that it allows you to create an application configuration class which has a ready method, allowing you to perform initialization tasks such as registering signals when the application first loads.

To get started with applications, set the default_app_config variable in your application’s __init__.py:

Now create a new file called apps.py in the root of your application and create a class that inherits from AppConfig:

Within this class you can define a variety of things including the application’s name, verbose name and the ready method. In the example above, I’m importing the signals submodule that contains the signal receivers (in Django 1.6 and below, signal registration usually happened in the models module).

Finally, here’s an example signal and handler from my signals submodule: