<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Brandon Konkle's Blog - Latest Posts</title><link>http://brandonkonkle.com/blog/</link><description>The latest entries on Brandon Konkle's blog.</description><language>en-us</language><lastBuildDate>Fri, 15 Jul 2011 12:07:41 -0500</lastBuildDate><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/bkonkle-latest-posts" /><feedburner:info uri="bkonkle-latest-posts" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><geo:lat>33.020118</geo:lat><geo:long>-97.007718</geo:long><feedburner:emailServiceId>bkonkle-latest-posts</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item><title>4 Day Work Week</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/M-sOLooLrP4/</link><description>&lt;p&gt;Since I began working with Lincoln Loop, I’ve been absolutely loving the flexibility.  What has surprised me, though, is how hard it has been to consistently put in 40 hours a week.  There were several weeks when it was even difficult for me to get to 32 hours, which is my absolute minimum.  I decided that it was time to take advantage of this newfound flexibility, and try a new work schedule.  So, six weeks ago I shifted to a 4 day work week.  So far, it has been great! There are definitely positives and negatives about the arrangement, but overall it’s been very good for me and it’s made it much easier for me to hit my 40 hour goal each week.&lt;/p&gt;
&lt;p&gt;I work Monday to Thursday, 8 hours during the day, and 2 hours in the evening.  Splitting the time up that way helps me avoid getting to burnt out each day, and gives me a chance to spend some time with my wife and son before the little guy goes to bed.  I usually jump back on the computer by 8, and finish up for the evening by 10.  This leaves me a couple of hours for some reading (or Minecraft) before bed.&lt;/p&gt;
&lt;p&gt;On Fridays my family and I usually spend part of the day out and about, enjoying the opportunity to get away from the house and be together.  This is especially important to us since we are home all the time.  I work from home, and Barbara stays home and cares for Elliot.  The time we spend out on Friday as a family is great bonding time and it fights off any traces of cabin fever.&lt;/p&gt;
&lt;p&gt;One of the most effective things about it, though, is the motivation.  I get a direct correlation between working hard and putting my hours in, and getting an extra day to relax.  I know that at any moment my 4 day week could become a 5 day week if I don't focus on my work and get my time in.  This personal reward makes a great motivation to be consistent and not give in to laziness or procrastination.  For this reason, I think it’s important for me that the day off be at the end of the week.  I’m working towards a reward each week, not struggling to catch up.&lt;/p&gt;
&lt;p&gt;The downside is that it has made it more difficult to spend time with friends during the week.  I’m always working on weeknights, so we’ve had to turn down invitations to get together on several occasions.  To overcome this I’m starting to plan for one free night each week, often working 3 hours during two other nights to compensate.&lt;/p&gt;
&lt;p&gt;I definitely feel that this new schedule has made me more productive and consistent, and I’d certainly recommend it to those who have the opportunity.  I’m definitely glad to be away from the rigid corporate world and free to pursue the style of work that fits me best!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=M-sOLooLrP4:o1ItaRhE8W0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=M-sOLooLrP4:o1ItaRhE8W0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=M-sOLooLrP4:o1ItaRhE8W0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=M-sOLooLrP4:o1ItaRhE8W0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=M-sOLooLrP4:o1ItaRhE8W0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=M-sOLooLrP4:o1ItaRhE8W0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=M-sOLooLrP4:o1ItaRhE8W0:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=M-sOLooLrP4:o1ItaRhE8W0:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/M-sOLooLrP4" height="1" width="1"/&gt;</description><pubDate>Fri, 15 Jul 2011 12:07:41 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2011/jul/15/4-day-work-week/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2011/jul/15/4-day-work-week/</feedburner:origLink></item><item><title>uWSGI and Django Secure Requests</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/xWLGwKh0x68/</link><description>&lt;p&gt;A colleague and I ran into a frustrating situation this evening when transitioning a site from Apache and mod_wsgi to uWSGI.  Django's &lt;code&gt;request.is_secure()&lt;/code&gt; wasn't working correctly.  After much Googling and shouting at the SSH console, we finally discovered the UWSGI_SCHEME uWSGI parameter.&lt;/p&gt;
&lt;p&gt;To fix the issue, we edited /etc/nginx/uwsgi_params (which originally comes from nginx/uwsgi_params in the uWSGI tarball) and added an additional param at the bottom:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;uwsgi_param&lt;/span&gt;  &lt;span class="n"&gt;UWSGI_SCHEME&lt;/span&gt;   &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This prompts uWSGI to set wsgi.url_scheme to the appropriate value, which Django then detects in the &lt;a href="http://github.com/django/django/blob/master/django/core/handlers/wsgi.py#L132-134"&gt;wsgi handler&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'd really love to see this in the documentation on the uWSGI site, or (even better) included by default in the uwsgi_params in the tarball.  In the meantime, though, it's a relatively quick and easy fix.  Enjoy!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=xWLGwKh0x68:CqeLQ_vlZTw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=xWLGwKh0x68:CqeLQ_vlZTw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=xWLGwKh0x68:CqeLQ_vlZTw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=xWLGwKh0x68:CqeLQ_vlZTw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=xWLGwKh0x68:CqeLQ_vlZTw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=xWLGwKh0x68:CqeLQ_vlZTw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=xWLGwKh0x68:CqeLQ_vlZTw:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=xWLGwKh0x68:CqeLQ_vlZTw:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/xWLGwKh0x68" height="1" width="1"/&gt;</description><pubDate>Fri, 29 Oct 2010 00:27:09 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/oct/29/uwsgi-and-django-secure-requests/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/oct/29/uwsgi-and-django-secure-requests/</feedburner:origLink></item><item><title>Postgres 9 Streaming Replication and Django-Balancer</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/yILKD5xVKhc/</link><description>&lt;p&gt;Over the past couple of weeks I've had the opportunity to dig in to Postgres 9's streaming replication.  What I found was a relatively easy way to configure a system for basic replication with very impressive speed.  Postgres's streaming replication is an enhancement to the log-shipping warm standby that was available in previous versions.  Instead of replaying write-ahead log (WAL) files as they become available on the slave's filesystem, the slave nodes connect directly to the master and stream the log as it generated.  This results in extremely fast replication, typically under a second.&lt;/p&gt;
&lt;p&gt;It can't match the cascading features of Slony, but if you're looking for a simple structure with a master database feeding one or more read-only slaves then Postgres 9 is a great choice.&lt;/p&gt;
&lt;p&gt;Here's how I got everything working on Ubuntu 10.04.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; I've changed the NFS details below to squash the user and group IDs and set the anonymous access user and group to the slave server's user and group IDs.  This resolves issues where the postgres use has a different ID on the slave server than the master server, so files received from master are owned by the wrong user.&lt;/p&gt;
&lt;h2&gt;Postgres 9&lt;/h2&gt;
&lt;p&gt;Postgres 9 isn't included in the Lucid repos (or Maverick, for that matter), so you'll need to use a PPA.  I used Martin Pitt's.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install python-software-properties
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo add-apt-repository ppa:pitti/postgresql
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get update
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install postgresql postgresql-server-dev-9.0 postgresql-contrib-9.0
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you're using GeoDjango, you'll need to re-compile the requirements from source after 9.0 is installed.  Make sure to use Postgis 1.5.2, which is the first release to support Postgres 9.&lt;/p&gt;
&lt;p&gt;Because the default encoding is ASCII, you'll need to drop the cluster, and re-initialize it as UTF8.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo -u postgres pg_dropcluster --stop 9.0 main
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo -u postgres pg_createcluster --start -e UTF-8 9.0 main
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;At this point, you can restore any database backups needed.  If you'd like, you can begin accepting live queries - the base backup needed below can run while the database is still available.&lt;/p&gt;
&lt;h2&gt;NFS Sharing&lt;/h2&gt;
&lt;p&gt;I'm using log-shipping and streaming replication together, just in case something goes wrong with the direct connection.  I use NFS to share a folder on the slave node and mount it on the master, so that the master can send WAL files to the slave.&lt;/p&gt;
&lt;h3&gt;On the Slave Server&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install nfs-kernel-server nfs-common portmap
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Note: If you're on Rackspace, you'll have trouble with nfs-kernel-server.  See my post &lt;a href="http://forrst.com/posts/Enable_NFS_on_Rackspace_Cloud_Servers-72h"&gt;here&lt;/a&gt; for details on how to work around it.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo dpkg-reconfigure portmap
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Select 'no'.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo restart portmap
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo emacs /etc/exports
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Find out the user and group IDs for postgres.  You'll use them in the next step.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;id -u postgres
104
&lt;span class="nv"&gt;$ &lt;/span&gt;id -g postgres
108
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Add a line like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;/path/for/backups   10.0.1.101/32(rw,async,no_subtree_check,all_squash,anonuid=104,anongid=108)
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Replace the CIDR notation with your own.  Use a &lt;a href="http://www.subnet-calculator.com/cidr.php"&gt;CIDR calculator&lt;/a&gt; if needed.  Replace the anonuid and anongid with the appropriate values for your server.&lt;/p&gt;
&lt;p&gt;Then, restart the server and export your shares.  Make sure the path exists on the filesystem before doing so, and adjust the permissions as needed so that the path can be written to.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo /etc/init.d/nfs-kernel-server restart
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo exportfs -a
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;On the Master Server&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install nfs-common portmap
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo emacs /etc/fstab
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Add a line like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;slavehostname:/path/for/backups /path/for/mount nfs rsize=8192,wsize=8192,timeo=14,intr 0 0
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then mount it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo mount /path/for/mount
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Test the NFS connection by touching a new file on the primary machine, and making sure it can be seen on the standby machine.&lt;/p&gt;
&lt;h2&gt;Replication&lt;/h2&gt;
&lt;h3&gt;On the Master Server&lt;/h3&gt;
&lt;p&gt;Edit &lt;em&gt;postgresql.conf&lt;/em&gt; to enable WAL archiving:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;wal_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hot_standby&lt;/span&gt;

&lt;span class="n"&gt;archive_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;

&lt;span class="n"&gt;archive_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;cp -i %p /path/for/mount/%f &amp;lt;/dev/null&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;max_wal_senders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Adjust &lt;code&gt;max_wal_senders&lt;/code&gt; to the number of slave servers you plan on using.&lt;/p&gt;
&lt;p&gt;Create a superuser for streaming replication:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;myuser&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;SUPERUSER&lt;/span&gt; &lt;span class="k"&gt;ENCRYPTED&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mypassword&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Modify &lt;em&gt;pg_hba.conf&lt;/em&gt; to allow the replication user to connect:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;host    replication     myuser     10.0.1.102/32             md5
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You'll need to restart Postgres at this point.&lt;/p&gt;
&lt;h3&gt;Base Backup&lt;/h3&gt;
&lt;p&gt;Next, a base backup needs to be created.  This must be a filesystem-level backup, not a logical backup like the &lt;em&gt;pg_dump&lt;/em&gt; command creates.  On the master server, run the &lt;em&gt;pg_start_backup&lt;/em&gt; command:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pg_start_backup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;base_backup&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then, tar and bzip the data directory:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/lib/postgresql/9.0/
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo tar -cjf ~/postgres-data.tar.bz2 main
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will take awhile.  Once it's done, run the &lt;em&gt;pg_stop_backup&lt;/em&gt; command:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pg_stop_backup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then, copy the archive to the standby server.  Make sure postgresql is not running on the standby, and unzip the archive:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/lib/postgresql/9.0/

&lt;span class="c"&gt;# Make SURE you&amp;#39;re on the standby server here&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo rm -rf main
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo tar -xjf ~/postgres-data.tar.bz2
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo chown -R postgres:postgres main
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;On the Slave Server&lt;/h3&gt;
&lt;p&gt;Edit &lt;em&gt;postgresql.conf&lt;/em&gt; to enable hot standby:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;hot_standby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Create a &lt;em&gt;recovery.conf&lt;/em&gt; in &lt;em&gt;/var/lib/postgresql/9.0/main/&lt;/em&gt; similar to this::&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;standby_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;on&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;primary_conninfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;host=10.0.1.101 port=5432 user=myuser password=mypassword&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;restore_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;cp /path/for/backups/%f %p&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;trigger_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;/path/for/backups/trigger_file&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;archive_cleanup_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;pg_archivecleanup /path/for/backups %r&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The postgresql-contrib-9.0 package doesn't link pg_archivecleanup into the
path.  You can do so manually:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo ln -s /usr/lib/postgresql/9.0/bin/pg_archivecleanup /usr/bin/
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Smoke Test&lt;/h2&gt;
&lt;p&gt;To make sure everything is working properly, first check and make sure wal sender and receiver processes are live on the master and slave servers.  On the primary, run &lt;code&gt;ps -ef | grep "wal sender"&lt;/code&gt; and make sure you see a wal sender process.  On the standby run &lt;code&gt;ps -ef | grep "wal receiver"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, create a test table on the primary:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Testing 1 2 3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;On the standby, query the table:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You should see the following output:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;     test      
---------------
 Testing 1 2 3
(1 row)
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can then delete your test table:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Load Balancing&lt;/h2&gt;
&lt;p&gt;For load balancing, I decided to leverage the excellent multidb capability introduced in Django 1.2.  To do this, I created the &lt;a href="http://github.com/bkonkle/django-balancer"&gt;django-balancer&lt;/a&gt; project.  It builds on the example of database routers in the Django docs, a &lt;a href="http://eli.thegreenplace.net/2010/01/22/weighted-random-generation-in-python/"&gt;post&lt;/a&gt; on Eli Bendersky's blog, and Jeff Balogh's &lt;a href="http://github.com/jbalogh/django-multidb-router/"&gt;django-multidb-router&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;For my project, I decided on the WeightedMasterSlaveRouter.  I want to include my master database in the read pool, but to a lesser extent than my slave database&lt;/p&gt;
&lt;p&gt;I borrowed master-pinning from Jeff Balogh's project for django-balancer, but I ended up not needing it in my project.  In my tests, I never had an issue with the databases being out of sync.  The replication was really that fast!  Even in situations where a user just created an object and was being rerouted to a rendered view of that object, the replication was speedy enough to keep up and prevent a 404.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Install with pip:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip install django-balancer
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then, add the desired router to your DATABASE_ROUTERS setting.  Also add any
settings required by that router.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;DATABASE_ROUTERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;balancer.routers.WeightedMasterSlaveRouter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;DATABASE_POOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;#39;slave1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;MASTER_DATABASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;default&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Make sure to configure your DATABASES setting to add the new slave database.&lt;/p&gt;
&lt;p&gt;And with that, you should be all set!&lt;/p&gt;
&lt;h3&gt;Create Your Own&lt;/h3&gt;
&lt;p&gt;You can also use the classes and mixins inside the django-balancer project to create your own routers.  If you come up with one that you'd like to share, fork me on Github.  I'd be glad to pull it into the project.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=yILKD5xVKhc:hdEx8WxyFR0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=yILKD5xVKhc:hdEx8WxyFR0:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=yILKD5xVKhc:hdEx8WxyFR0:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=yILKD5xVKhc:hdEx8WxyFR0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=yILKD5xVKhc:hdEx8WxyFR0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=yILKD5xVKhc:hdEx8WxyFR0:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=yILKD5xVKhc:hdEx8WxyFR0:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=yILKD5xVKhc:hdEx8WxyFR0:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/yILKD5xVKhc" height="1" width="1"/&gt;</description><pubDate>Wed, 20 Oct 2010 23:26:03 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/oct/20/postgres-9-streaming-replication-and-django-balanc/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/oct/20/postgres-9-streaming-replication-and-django-balanc/</feedburner:origLink></item><item><title>Django Admin Customization Examples</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/ZkRA3FxI2V0/</link><description>&lt;p&gt;I've worked on a couple of projects recently that required some customization of the Django admin.  One of the things that I love about Django's built-in admin is that it is created with a very extensible class-based structure.  Here are some of the ways I was able to customize the admin to add functionality.&lt;/p&gt;
&lt;h2&gt;Adding Buttons to the Change View&lt;/h2&gt;
&lt;p&gt;One of the major features needed for a project was the ability to manage a workflow for a model.  The model progressed through different status stages, and the admin team needed to be able to make decisions based on that current status.  To handle this, I added some new buttons to the model's change view, and used my ModelAdmin and ModelForm subclasses to move the model around in the workflow accordingly.&lt;/p&gt;
&lt;h3&gt;Customizing the Template&lt;/h3&gt;
&lt;p&gt;In order to differentiate the workflow management options from the standard Admin delete &amp;amp; save buttons, I added a row of buttons directly above the standard delete &amp;amp; save row.  I created a file in my templates directory with the path &lt;code&gt;admin/myapp/mymodel/change_form.html&lt;/code&gt; that looked something like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;admin/change_form.html&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;after_related_objects&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;original.pk&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit-row&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;deletelink-box&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Manage this request&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;original.requested&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Approve&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;approve&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Deny&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;deny&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;original.submitted&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Accept&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;accept&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Return&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;return&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;original.submitted&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;original.assigned&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Cancel&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;cancel&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;original.accepted&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Complete&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;complete&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;original.cancelled&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Reopen&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;reopen&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;original&lt;/code&gt; context variable refers to the object being created or changed.  I checked for a primary key because I only wanted the extra buttons to show up on edits.  The &lt;code&gt;.requested&lt;/code&gt;, &lt;code&gt;.submitted&lt;/code&gt;, etc. attributes are properties on my model that check a status field.&lt;/p&gt;
&lt;h3&gt;Handling the Extra Submit Options&lt;/h3&gt;
&lt;p&gt;To handle these extra submit options, I created a ModelForm subclass.  On my ModelAdmin, I set &lt;code&gt;form = MyAdminForm&lt;/code&gt; to point to my new ModelForm.  On the form, I implemented the &lt;code&gt;clean&lt;/code&gt; method to look for the workflow actions:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cleaned_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyAdminForm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;approve&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# Handle the approval logic here&lt;/span&gt;
        &lt;span class="c"&gt;# ...&lt;/span&gt;
        &lt;span class="c"&gt;# If there&amp;#39;s a problem with the action, you can raise a&lt;/span&gt;
        &lt;span class="c"&gt;# forms.ValidationError(&amp;quot;Your message here.&amp;quot;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;deny&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# Handle the denial logic here&lt;/span&gt;
        &lt;span class="c"&gt;# ...&lt;/span&gt;

    &lt;span class="c"&gt;# Etc...&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cleaned_data&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In order to give the user effective feedback that their action was success, I wanted to customize the response message given to them after they click the custom button.  To do that, I went back to my ModelAdmin subclass and overrode the &lt;code&gt;response_change&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;response_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;approve&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The &lt;/span&gt;&lt;span class="si"&gt;%(name)s&lt;/span&gt;&lt;span class="s"&gt; &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(obj)s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; was approved.&amp;#39;&lt;/span&gt;  &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;obj&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;../&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;deny&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The &lt;/span&gt;&lt;span class="si"&gt;%(name)s&lt;/span&gt;&lt;span class="s"&gt; &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(obj)s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; was denied.&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;obj&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;../&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Etc...&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;reopen&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;The &lt;/span&gt;&lt;span class="si"&gt;%(name)s&lt;/span&gt;&lt;span class="s"&gt; &amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(obj)s&lt;/span&gt;&lt;span class="s"&gt;&amp;quot; was reopened. &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;&amp;#39;name: obj._meta.verbose_name, &amp;#39;&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;: obj&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;You may manage it below.&amp;#39;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REQUEST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_popup&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;?_popup=1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyModelAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Adding Modules to the App Index&lt;/h2&gt;
&lt;p&gt;In order to create a dashboard for the workflow-based app, I wanted to add some modules to the app index that would act as a queue for items in a certain status.  I decided to handle this through template tags:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nd"&gt;@register.inclusion_tag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;admin/myapp/_recent_requests.html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;recent_requests&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;requests&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;-requested&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I added an &lt;em&gt;admin/myapp/app_index.html&lt;/em&gt; template that looked like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;admin/app_index.html&amp;quot;&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;load&lt;/span&gt; &lt;span class="nv"&gt;myapp_admin_tags&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;sidebar&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;content-related&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;module&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Shortcuts&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;url&lt;/span&gt; &lt;span class="nv"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;myapp_mymodel_changelist&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;&lt;span class="s"&gt;?ot=desc&amp;amp;is_closed__exact=0&amp;amp;o=3&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          Open requests
        &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Etc... --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;recent_submissions&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;content-secondary&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;recent_requests&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Creating a Site-Specific Admin&lt;/h2&gt;
&lt;p&gt;On one project with several sub-sites, I needed to create a site-specific admin so that administrators of one site could use the Django admin without interfering with content from the other sites.  To do this, I created a secondary admin site.&lt;/p&gt;
&lt;h3&gt;Addind an Additional Admin Site&lt;/h3&gt;
&lt;p&gt;I wanted users of the site-specific admin to be a member of a specific group.  With this in mind, I created a subclass of AdminSite with an overridden &lt;code&gt;has_permission&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.admin.sites&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AdminSite&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LocalAdminSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AdminSite&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;has_permission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Checks to make sure that the user is authenticated and is either a&lt;/span&gt;
&lt;span class="sd"&gt;        staff member or a member of the &amp;#39;Content editor&amp;#39; group.&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Group&lt;/span&gt;
        &lt;span class="n"&gt;ce_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;Content editor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_authenticated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_staff&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;ce_group&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;local_admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalAdminSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;local&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I created an app within my project called &lt;em&gt;admin&lt;/em&gt; and added the above code to &lt;em&gt;admin/sites.py&lt;/em&gt;. Then, to use my site, I could simply do this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;myproject.admin.sites&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;local_admin&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Custom Autodiscovery&lt;/h3&gt;
&lt;p&gt;I wanted to be able to add a &lt;em&gt;local_admin.py&lt;/em&gt; file in each app that should be available in the site-specific admin.  To do this, I took the admin.autodiscover() method and slightly modified it:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;autodiscover_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Auto-discover the specified module in INSTALLED_APPS and fail silently&lt;/span&gt;
&lt;span class="sd"&gt;    when not present. This forces an import on them to register anything they&lt;/span&gt;
&lt;span class="sd"&gt;    may want.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;copy&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.contrib.admin.sites&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.importlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;import_module&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils.module_loading&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;module_has_submodule&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;import_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;# Attempt to import the app&amp;#39;s admin module.&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;before_import_registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_registry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;import_module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c"&gt;# Reset the model registry to the state before the last import as&lt;/span&gt;
            &lt;span class="c"&gt;# this import will have to reoccur on the next request and this&lt;/span&gt;
            &lt;span class="c"&gt;# could raise NotRegistered and AlreadyRegistered exceptions&lt;/span&gt;
            &lt;span class="c"&gt;# (see #8245).&lt;/span&gt;
            &lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;before_import_registry&lt;/span&gt;

            &lt;span class="c"&gt;# Decide whether to bubble up this error. If the app just&lt;/span&gt;
            &lt;span class="c"&gt;# doesn&amp;#39;t have an admin module, we can ignore the error&lt;/span&gt;
            &lt;span class="c"&gt;# attempting to import it, otherwise we want it to bubble up.&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;module_has_submodule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That way I can use &lt;code&gt;autodiscover_module('local_admin')&lt;/code&gt; to automatically register all of my local admin modules.  I stuck it in &lt;code&gt;urls.py&lt;/code&gt; - not the most semantically correct location, but it's convenient.&lt;/p&gt;
&lt;h3&gt;Excluding the Sites Attribute&lt;/h3&gt;
&lt;p&gt;To make things nice and DRY, I wanted to use the existing admin.py definitions, but limit them to the current site and strip the &lt;em&gt;sites&lt;/em&gt; attribute from them.  To do this, I got a little crazy and wrote a function to automatically exclude them.  I am absolutely certain that this could be refactored to be more elegant, I just haven't had the time to do it yet.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exclude_sites&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;    Remove occurrences of site and sites from admin fields.&lt;/span&gt;

&lt;span class="sd"&gt;    Returns: fields, fieldsets, list_display, list_filter&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;sites&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;site&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;new_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;new_fieldsets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;new_list_display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;new_list_filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c"&gt;# Take sites out of the fields&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exclusion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exclusion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;new_fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclusion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Take sites out of the fieldsets&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fieldset&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fieldset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fieldset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exclusion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exclusion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclusion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;new_fieldsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;fields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)}))&lt;/span&gt;

    &lt;span class="c"&gt;# Take sites out of the list_display&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_list_display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_display&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exclusion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exclusion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_list_display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;new_list_display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclusion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;# Take sites out of the list_filter&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_list_filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__class__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exclusion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exclusion&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_list_filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;new_list_filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclusion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_fields&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_fieldsets&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_list_display&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_list_filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To make use of this, I created a base class that I could then subclass in all of my &lt;em&gt;local_admin.py&lt;/em&gt; modules.  I also overrode the queryset in this base class to use the &lt;code&gt;local&lt;/code&gt; manager I assigned to each model.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LocalModelAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Runs the fieldsets, list_displays, and list_filters through the&lt;/span&gt;
&lt;span class="sd"&gt;        exclude_sites function, which returns the attributes without the&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;#39;site&amp;#39; or &amp;#39;sites&amp;#39; field.&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LocalModelAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldsets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_display&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; \
            &lt;span class="n"&gt;exclude_sites&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;queryset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="sd"&gt;        Returns a queryset with results filtered by the current site. Requires&lt;/span&gt;
&lt;span class="sd"&gt;        a manager called &amp;#39;local&amp;#39; to be assigned to the model that does the&lt;/span&gt;
&lt;span class="sd"&gt;        filtering.&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="n"&gt;qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_query_set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;ordering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ordering&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ordering&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ordering&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then, in my &lt;em&gt;local_admin.py&lt;/em&gt; files I used multiple inheritance to inherit functionality both from this base class and the original &lt;em&gt;admin.py&lt;/em&gt; module definition.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;myproject.admin.sites&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;local_admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;myproject.myapp.admin&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ItemAdmin&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LocalItemAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LocalModelAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="n"&gt;local_admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LocalItemAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Adjusting the Queryset Used in Related Fields&lt;/h3&gt;
&lt;p&gt;Related fields still show results from other sites, however.  To fix this, I extended the &lt;code&gt;formfield_for_dbfield&lt;/code&gt; method on my LocalModelAdmin subclass.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LocalItemAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LocalModelAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemAdmin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;my_field&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;queryset&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LocalItemAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formfield_for_dbfield&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;db_field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;local_admin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LocalItemAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=ZkRA3FxI2V0:C7WJ9Bs14-k:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=ZkRA3FxI2V0:C7WJ9Bs14-k:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=ZkRA3FxI2V0:C7WJ9Bs14-k:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=ZkRA3FxI2V0:C7WJ9Bs14-k:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=ZkRA3FxI2V0:C7WJ9Bs14-k:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=ZkRA3FxI2V0:C7WJ9Bs14-k:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=ZkRA3FxI2V0:C7WJ9Bs14-k:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=ZkRA3FxI2V0:C7WJ9Bs14-k:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/ZkRA3FxI2V0" height="1" width="1"/&gt;</description><pubDate>Mon, 04 Oct 2010 22:50:03 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/oct/4/django-admin-customization-examples/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/oct/4/django-admin-customization-examples/</feedburner:origLink></item><item><title>Class-based Views with Django-Baseviews</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/Y18mdAA2JpE/</link><description>&lt;p&gt;I've always loved the concept of class-based views because views often become repetitive.  There are a lot of common operations in a view that work best when they are defined once and reused.  Class inheritance, in my opinion, is the best way to keep views DRY and allow you to focus on what makes each view different.&lt;/p&gt;
&lt;p&gt;At first, I used to break out pieces of functionality into their own functions in the views.py file.  This worked okay, but I had to pass a lot of data around in the arguments and I had to do a lot of imports if I had functions from other app's views that I wanted to use in this app.  This quickly became tedious.&lt;/p&gt;
&lt;p&gt;After seeing &lt;a href="http://haystacksearch.org/"&gt;django-haystack&lt;/a&gt; use class-based views and really loving the way I could customize their behavior for my own project, I decided to start building some base views of my own to build on.  While working on new Pegasus News apps, I started building up a set of class-based views that helped me stop repeating everything that the views had in common.  Once the base views were in place, the process of creating new views for my apps because a little quicker and more organized.&lt;/p&gt;
&lt;p&gt;I've received permission to open-source the app, so it's now &lt;a href="http://github.com/bkonkle/django-baseviews"&gt;out on Github&lt;/a&gt;.  I've already received a few contributions (from Justin Quick, Jannis Leidel, and Chuck Harmston - thanks guys!) and I'd be happy to receive more.  I've been using the project in production on Pegasus News since June, and it has worked well so far.&lt;/p&gt;
&lt;h2&gt;How I'm using django-baseviews&lt;/h2&gt;
&lt;p&gt;For the apps I'm using &lt;em&gt;baseviews&lt;/em&gt; with on Pegasus, I start out with a &lt;em&gt;BasicView&lt;/em&gt; for the index.  Then, for any views that deal with a specific model instance I create a base class for the app that takes the slug or id from the url pattern, grabs the object, and adds it to the class instance.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ItemView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BasicView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_object_or_404&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ItemView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I then subclass the item-specific view as the base for any views that deal with a specific item.  This gives me the self.item attribute on my subclasses without having to repeat the &lt;code&gt;get_object_or_404&lt;/code&gt; call for every view.  This is very a basic example, and it's just the start of what you can do.&lt;/p&gt;
&lt;p&gt;For a real world example - in one of my apps, I had a view that simply displayed all instances of a model.  Later on after the app was launched, I found out that I needed to create a page that showed all the instances of that model within a specific category.  All I had to do was move the queryset creation out into its own method, create a quick subclass of my original view, and override the queryset method on the subclass to filter by the requested category.&lt;/p&gt;
&lt;h2&gt;Plans for continued development&lt;/h2&gt;
&lt;p&gt;After watching Ben Firshman's &lt;a href="http://blip.tv/file/4109272/"&gt;Alternative Views&lt;/a&gt; talk from Djangocon US, I've learned about the concept of mixins - using Python's multi-inheritance to add small pieces of functionality.  For example, I could create a mixin that renders everything to JSON, or one to render in XML, or one for YAML.  Then, when subclassing FormView I could add the JSON mixin to the front of the new class's bases::&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ItemFormView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RenderJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FormView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Assuming that all RenderJson does is override the render method, this would allow the RenderJson class to handle rendering and FormView to handle all the other methods.&lt;/p&gt;
&lt;p&gt;I'd also like to take a look at Jacobian's &lt;a href="http://github.com/jacobian/django/tree/class-based-generic-views/django/views/generic2"&gt;class-based generic views&lt;/a&gt; and do something similar for &lt;em&gt;baseviews&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I'm going to keep building up &lt;em&gt;baseviews&lt;/em&gt; as I use it for other projects, and I encourage you to contribute any view classes to &lt;em&gt;baseviews&lt;/em&gt; that you think would help speed up development for others.  Once I get enough view classes, I'll start to split &lt;code&gt;views.py&lt;/code&gt; up into different files based on the type of views contained.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The image at the top of this post is from &lt;a href="http://www.nataliedee.com/"&gt;Natalie Dee&lt;/a&gt;, a funny and exceedingly irreverent web comic.&lt;/em&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=Y18mdAA2JpE:l97YORaio1I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=Y18mdAA2JpE:l97YORaio1I:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=Y18mdAA2JpE:l97YORaio1I:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=Y18mdAA2JpE:l97YORaio1I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=Y18mdAA2JpE:l97YORaio1I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=Y18mdAA2JpE:l97YORaio1I:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=Y18mdAA2JpE:l97YORaio1I:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=Y18mdAA2JpE:l97YORaio1I:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/Y18mdAA2JpE" height="1" width="1"/&gt;</description><pubDate>Tue, 28 Sep 2010 23:56:48 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/sep/28/class-based-views-django-baseviews/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/sep/28/class-based-views-django-baseviews/</feedburner:origLink></item><item><title>Django on uWSGI and Nginx</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/GnFct_gR_M8/</link><description>&lt;p&gt;I recently moved &lt;a href="http://www.pegasusnews.com"&gt;Pegasus News&lt;/a&gt; from Perlbal, Lighttpd, and Apache to Nginx and uWSGI.  We balance the traffic between 3 physical servers, and the systems were struggling under the load even after weeks of Apache conf tweaking.  We began having issues with excessively slow page loads, request timeouts, and intermittent errors with OGR transformations.&lt;/p&gt;
&lt;p&gt;I decided to move us to a lighter application server so that we could get the most out of our system resources, and after a lot of research and testing I chose &lt;a href="http://projects.unbit.it/uwsgi/"&gt;uWSGI&lt;/a&gt;.  While I was at it, I decided to replace Perlbal and Lighttpd with &lt;a href="http://wiki.nginx.org/"&gt;Nginx&lt;/a&gt; because of its great configuration syntax and excellent performance.  I also upgraded us to Ubuntu 10.04 and Postgres 8.4.&lt;/p&gt;
&lt;p&gt;The result was a resounding success! Memory usage and CPU load on each of the web nodes dropped dramatically.  Swap usage dropped to almost nothing.  The overall responsiveness of the site improved noticeably, and the timeout errors and OGR failures disappeared entirely.&lt;/p&gt;
&lt;p&gt;If you'd like to give this stack a try, read on for an overview of the setup on Ubuntu 10.04.  I'm using the standard Ubuntu source package for Nginx, but modifying it slightly and them installing it with &lt;code&gt;dpkg-buildpackage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This post is just about the setup relevant to Nginx and uWSGI.  If you need a more complete server setup guide, try my &lt;a href="http://brandonkonkle.com/blog/2010/jun/25/provisioning-new-ubuntu-server-django/"&gt;Provisioning a new server&lt;/a&gt; post.&lt;/p&gt;
&lt;h2&gt;uWSGI&lt;/h2&gt;
&lt;p&gt;Before we build Nginx, uWSGI needs to be compiled so that its module can be included in the Nginx build.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install build-essential python-dev libxml2-dev
&lt;span class="nv"&gt;$ &lt;/span&gt;wget http://projects.unbit.it/downloads/uwsgi-0.9.5.4.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;tar -xzf uwsgi-0.9.5.4.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;uwsgi-0.9.5.4
&lt;span class="nv"&gt;$ &lt;/span&gt;make -f Makefile.Py26
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Copy the executable to the local sbin directory.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo cp uwsgi /usr/local/sbin
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Also, copy the default uwsgi settings to the /etc/nginx directory.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo mkdir /etc/nginx
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo cp nginx/uwsgi_params /etc/nginx
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Nginx&lt;/h2&gt;
&lt;p&gt;We need to slightly modify the nginx package from Ubuntu to add uWSGI (and, optionally, SSL).&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install libssl-dev
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get build-dep nginx
&lt;span class="nv"&gt;$ &lt;/span&gt;apt-get &lt;span class="nb"&gt;source &lt;/span&gt;nginx
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;nginx-0.7*
&lt;span class="nv"&gt;$ &lt;/span&gt;emacs debian/rules
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Add the following lines to the end of the configure options. Make sure to include backslashes so that all the options are interpreted as being on one line.  If you don't need SSL, ignore that line.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;    --with-http_ssl_module \
    --add-module=$(CURDIR)/../uwsgi-0.9.5.4/nginx
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Build, install, and hold the packages.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dpkg-buildpackage
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo dpkg -i nginx*.deb
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;nginx hold&amp;quot;&lt;/span&gt; | sudo dpkg --set-selections
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;nginx-dbg hold&amp;quot;&lt;/span&gt; | sudo dpkg --set-selections
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Starting with nginx-0.8.41, you can add something like &lt;code&gt;--http-uwsgi-temp-path=/var/lib/nginx/uwsgi&lt;/code&gt; to the &lt;em&gt;debian/rules&lt;/em&gt; so that the temp files are kept in the right place.  Until then (probably a later version of Ubuntu), you'll need to create a &lt;em&gt;/usr/local/nginx/uwsgi_temp&lt;/em&gt; folder to use for the temp files.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo mkdir -p /usr/local/nginx/uwsgi_temp
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Supervisor&lt;/h2&gt;
&lt;p&gt;To manage the uWSGI processes, I use Supervisor.  In Ubuntu 10.04, you can simply install it with apt-get.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install supervisor
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;h3&gt;Supervisor&lt;/h3&gt;
&lt;p&gt;To configure uWSGI, I use command-line options in my Supervisor config.  The example below is similar to what I use in production for Pegasus, but you'll want to take a look at the &lt;a href="http://projects.unbit.it/uwsgi/wiki/Doc"&gt;uWSGI docs&lt;/a&gt; and tweak for your situation.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;[program:myapp]
command=/usr/local/sbin/uwsgi
  --home /home/myuser/.virtualenvs/myapp/
  --module myapp.deploy.wsgi
  --socket 10.1.2.3:10000
  --pythonpath /sites/myapp.com/code/myapp
  --processes 5
  --master
  --harakiri 120
  --max-requests 5000
directory=/sites/myapp.com/code/myapp
environment=DJANGO_SETTINGS_MODULE=&amp;#39;myapp.settings&amp;#39;
user=www-data
autostart=true
autorestart=true
stdout_logfile=/sites/myapp.com/logs/uwsgi.log
redirect_stderr=true
stopsignal=QUIT
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The module I specify in the &lt;code&gt;--module&lt;/code&gt; option simply contains:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;django.core.handlers.wsgi&lt;/span&gt;

&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;django&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wsgi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The IP address in the &lt;code&gt;--socket&lt;/code&gt; option is the server's IP address on the interface reserved for local network traffic.  On Rackspace, eth1 is your local interface.  Use the &lt;code&gt;ifconfig&lt;/code&gt; command to find your IP address on the local interface.  If you're just using uWSGI on localhost, you can use something like &lt;code&gt;--socket /sites/myapp.com/var/run/myapp.sock&lt;/code&gt; instead to avoid the overhead of the full TCP stack.&lt;/p&gt;
&lt;p&gt;My value for &lt;code&gt;--harakiri&lt;/code&gt; is rather high, and my value for &lt;code&gt;--max-requests&lt;/code&gt; is rather low.  You may not even need either of these options, but I'm using them to solve some problems specific to Pegasus.&lt;/p&gt;
&lt;h3&gt;Nginx&lt;/h3&gt;
&lt;p&gt;For the site-specific Nginx config, I'm using something like this:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="n"&gt;10.1.2.3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="n"&gt;10.1.2.4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="n"&gt;10.1.2.5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;myapp.com&lt;/span&gt; &lt;span class="s"&gt;www.myapp.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="s"&gt;/sites/myapp.com/logs/nginx-access.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;error_log&lt;/span&gt; &lt;span class="s"&gt;/sites/myapp.com/logs/nginx-error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/sites/myapp.com/public&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="s"&gt;/sites/myapp.com/ssl/myapp.crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="s"&gt;/sites/myapp.com/ssl/myapp.key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# This checks for a file called simply &amp;quot;downtime&amp;quot; in the public&lt;/span&gt;
        &lt;span class="c1"&gt;# directory, and puts up the downtime page if it exists.&lt;/span&gt;
        &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;(-f&lt;/span&gt; &lt;span class="s"&gt;/sites/myapp.com/public/downtime)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kn"&gt;uwsgi_pass&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;uwsgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/media&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# This makes static media available at the /media/ url.  The&lt;/span&gt;
        &lt;span class="c1"&gt;# media will continue to be available during site downtime,&lt;/span&gt;
        &lt;span class="c1"&gt;# allowing you to use styles and images in your maintenance page.&lt;/span&gt;
        &lt;span class="kn"&gt;alias&lt;/span&gt; &lt;span class="s"&gt;/sites/myapp.com/public/media&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;error_page&lt;/span&gt; &lt;span class="mi"&gt;502&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt; &lt;span class="s"&gt;@maintenance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;@maintenance&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;# In this example, there&amp;#39;s a directory in the site media files&lt;/span&gt;
        &lt;span class="c1"&gt;# called &amp;quot;downtime&amp;quot; that contains a &amp;quot;maintenance.html&amp;quot; file and&lt;/span&gt;
        &lt;span class="c1"&gt;# any styles and images needed for the maintenance page.&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/sites/myapp.com/public/media/downtime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;^(.*)&lt;/span&gt;$ &lt;span class="s"&gt;/maintenance.html&lt;/span&gt; &lt;span class="s"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;If you're just using uWSGI on localhost, then skip the &lt;code&gt;upstream&lt;/code&gt; section and use something like &lt;code&gt;uwsgi_pass unix:///sites/myapp.com/var/run/myapp.sock;&lt;/code&gt; in the root location definition instead.&lt;/p&gt;
&lt;h2&gt;Firing it up&lt;/h2&gt;
&lt;p&gt;Restart Supervisor with &lt;code&gt;sudo /etc/init.d/supervisor restart&lt;/code&gt;, and it should
reload the config and bring the uWSGI processes up.  Restart Nginx with &lt;code&gt;sudo /etc/init.d/nginx restart&lt;/code&gt;, and you should now be able to reach your site.  If not, take a look at the logs and start troubleshooting.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=GnFct_gR_M8:CRjUp6aoyHw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=GnFct_gR_M8:CRjUp6aoyHw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=GnFct_gR_M8:CRjUp6aoyHw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=GnFct_gR_M8:CRjUp6aoyHw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=GnFct_gR_M8:CRjUp6aoyHw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=GnFct_gR_M8:CRjUp6aoyHw:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=GnFct_gR_M8:CRjUp6aoyHw:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=GnFct_gR_M8:CRjUp6aoyHw:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/GnFct_gR_M8" height="1" width="1"/&gt;</description><pubDate>Tue, 14 Sep 2010 00:19:31 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/sep/14/django-uwsgi-and-nginx/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/sep/14/django-uwsgi-and-nginx/</feedburner:origLink></item><item><title>Setting up a template_postgis on Lucid</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/KGDixnL95VU/</link><description>&lt;p&gt;I wasn't able to find instructions in the Django docs for setting up a template_postgis database with postgis-1.5 and Postgres 8.4 on Ubuntu Lucid (10.04).  Below is what worked for me.&lt;/p&gt;
&lt;h2&gt;GeoDjango installed via Ubuntu 10.04 packages&lt;/h2&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nv"&gt;POSTGIS_SQL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;pg_config --sharedir&lt;span class="sb"&gt;`&lt;/span&gt;/contrib
createdb -E UTF8 template_postgis &lt;span class="c"&gt;# Create the template spatial database.&lt;/span&gt;
createlang -d template_postgis plpgsql &lt;span class="c"&gt;# Adding PLPGSQL language support.&lt;/span&gt;
psql -d postgres -c &lt;span class="s2"&gt;&amp;quot;UPDATE pg_database SET datistemplate=&amp;#39;true&amp;#39; WHERE datname=&amp;#39;template_postgis&amp;#39;;&amp;quot;&lt;/span&gt;
psql -d template_postgis -f &lt;span class="nv"&gt;$POSTGIS_SQL_PATH&lt;/span&gt;/postgis.sql &lt;span class="c"&gt;# Loading the PostGIS SQL routines&lt;/span&gt;
psql -d template_postgis -f &lt;span class="nv"&gt;$POSTGIS_SQL_PATH&lt;/span&gt;/spatial_ref_sys.sql
psql -d template_postgis -c &lt;span class="s2"&gt;&amp;quot;GRANT ALL ON geometry_columns TO PUBLIC;&amp;quot;&lt;/span&gt; &lt;span class="c"&gt;# Enabling users to alter spatial tables.&lt;/span&gt;
psql -d template_postgis -c &lt;span class="s2"&gt;&amp;quot;GRANT ALL ON spatial_ref_sys TO PUBLIC;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h2&gt;GeoDjango built from source&lt;/h2&gt;
&lt;p&gt;Use the above script, but change POSTGRIS_SQL_PATH to:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;POSTGIS_SQL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;pg_config --sharedir&lt;span class="sb"&gt;`&lt;/span&gt;/contrib/postgis-1.5
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Make sure to run &lt;code&gt;sudo ldconfig&lt;/code&gt; after compiling everything, or else postgis won't be able to find geos.  You'll see errors like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;psql:/usr/share/postgresql/8.4/contrib/postgis-1.5/postgis.sql:59: ERROR:  could not load library &amp;quot;/usr/lib/postgresql/8.4/lib/postgis-1.5.so&amp;quot;: libgeos_c.so.1: cannot open shared object file: No such file or directory
&lt;/pre&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=KGDixnL95VU:bSybn4pJwJ4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=KGDixnL95VU:bSybn4pJwJ4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=KGDixnL95VU:bSybn4pJwJ4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=KGDixnL95VU:bSybn4pJwJ4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=KGDixnL95VU:bSybn4pJwJ4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=KGDixnL95VU:bSybn4pJwJ4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=KGDixnL95VU:bSybn4pJwJ4:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=KGDixnL95VU:bSybn4pJwJ4:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/KGDixnL95VU" height="1" width="1"/&gt;</description><pubDate>Mon, 19 Jul 2010 15:59:18 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/jul/19/setting-template_postgis-lucid/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/jul/19/setting-template_postgis-lucid/</feedburner:origLink></item><item><title>Bitten by Python&amp;#39;s Pass-By-Reference</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/5RM3tFaYhiw/</link><description>&lt;p&gt;I just got bitten by an interesting bug related to Python's pass-by-reference feature.  I had a class method (my_method) with a keyword argument (my_kwarg) that defaulted to {}.  I thought that each time my_method was called, if my_kwarg wasn't passed then it would default to an empty dict.&lt;/p&gt;
&lt;p&gt;In reality, when the class was read by Python it evaluated the {} and made the default value a reference to the dict in memory.  So each time my_method was called without passing my_kwarg, it was defaulting to that memory reference which contained data from a previous use.  Here's an example class:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_kwarg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{}):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;my_kwarg&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And here's an iPython shell session illustrating what I mean:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;first_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;kwarg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_method&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;kwarg&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;kwarg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;kwarg&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;kwarg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_method&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;kwarg&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;second_class&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;other_kwarg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;second_class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_method&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;other_kwarg&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The fact that the kwarg wasn't defaulting to a fresh dict each time led to a lot of confusion before I realized what was happening!  There are a couple of ways to solve the issue, but the easiest is probably this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;my_kwarg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;my_kwarg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_kwarg&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;my_kwarg&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=5RM3tFaYhiw:FoHgW2wUuPc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=5RM3tFaYhiw:FoHgW2wUuPc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=5RM3tFaYhiw:FoHgW2wUuPc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=5RM3tFaYhiw:FoHgW2wUuPc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=5RM3tFaYhiw:FoHgW2wUuPc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=5RM3tFaYhiw:FoHgW2wUuPc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=5RM3tFaYhiw:FoHgW2wUuPc:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=5RM3tFaYhiw:FoHgW2wUuPc:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/5RM3tFaYhiw" height="1" width="1"/&gt;</description><pubDate>Wed, 14 Jul 2010 17:19:41 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/jul/14/pass-by-reference/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/jul/14/pass-by-reference/</feedburner:origLink></item><item><title>Provisioning a new Ubuntu server for Django</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/eKazmgmbORo/</link><description>&lt;p&gt;I've been a long-time satisfied user of &lt;a href="http://www.webfaction.com/"&gt;Webfaction&lt;/a&gt;, but recently I've had a strong urge to move to VPS hosting so that I can have greater control over the environment.  After some research, I went with Rackspace Cloud because of the incredibly cheap low-end options.  My site doesn't use a huge amount of bandwidth, so Rackspace looks to be the most feature-packed and still cost-effective option.&lt;/p&gt;
&lt;p&gt;A friend of mine, &lt;a href="http://kevin-whitaker.net"&gt;Kevin Whitaker&lt;/a&gt;, recently posted &lt;a href="http://blog.kevin-whitaker.net/post/725558757/running-django-with-postgres-nginx-and-fastcgi-on"&gt;a great article&lt;/a&gt; about getting up and running with Django in a server environment for testing or production.  He used Ubuntu, Postgres, Nginx, and FastCGI to make up his stack.  I've never set up Nginx before, so his post was a great help in getting Nginx configured.  My stack is slightly different, however, since I prefer to use Gunicorn instead of FastCGI and I use supervisord to manage my processes.  I also use virtualenv to manage dependencies like Django itself and psycopg2.&lt;/p&gt;
&lt;p&gt;I'm including my install notes below so that I can provide a complete picture of one way of provisioning a new server from build to deploy.  My site is relatively low-traffic, so I'm not going to go into postgres tweaking, connection pooling, caching, etc.  I'll save those topics for another post.&lt;/p&gt;
&lt;p&gt;These notes were made on a Rackspace Cloud instance, but they should apply to a variety of other VPS hosts with only minor modifications.  If you've got tips on things that need to be changed for other providers, let me know and I'll edit the post.  I want this to stay up to date for awhile.&lt;/p&gt;
&lt;h2&gt;After the build is complete&lt;/h2&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;My first step was to log into my new instance using SSH.  You should get an email from Rackspace with the IP address and root password.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh root@123.45.67.890

root@123.45.67.890&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;s password:

Welcome to Ubuntu!
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I then installed my preferred editor - obviously an optional step.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;apt-get install emacs23-nox
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;emacs
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I used visudo to set up sudo access the way I like it.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;visudo
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Under this line:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;Defaults        env_reset
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I added the following lines to preserve my editor and Django environment settings:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;Defaults        env_keep += &amp;quot;EDITOR VISUAL&amp;quot;
Defaults        env_keep += &amp;quot;DJANGO_SETTINGS_MODULE PYTHONPATH&amp;quot;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I changed this line:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;%sudo   ALL=(ALL) ALL
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;%admin  ALL=(ALL) ALL
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This allows users with the group &lt;code&gt;admin&lt;/code&gt; to use sudo for all commands.&lt;/p&gt;
&lt;p&gt;Then I added my first user and group.  I typically prefer to use a separate user account for each developer or administrator that will be accessing the box, and add them to appropriate groups for access.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;adduser myuser
&lt;span class="nv"&gt;$ &lt;/span&gt;addgroup admin
&lt;span class="nv"&gt;$ &lt;/span&gt;adduser myuser admin
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I switched to my new user, and added my public key so that I can SSH without requiring a password.  If you're not familiar with this, the &lt;a href="http://help.github.com/mac-key-setup/"&gt;guide at Github&lt;/a&gt; is quick and to the point.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;su - myuser
&lt;span class="nv"&gt;$ &lt;/span&gt;mkdir .ssh
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; .ssh
&lt;span class="nv"&gt;$ &lt;/span&gt;wget http://mydomain.com/path/to/my/pubkey/id_rsa.pub
&lt;span class="nv"&gt;$ &lt;/span&gt;mv id_rsa.pub authorized_keys
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then, I logged out and made sure I can SSH in using the new user with no problems.  Once that's done, I locked down the root account for security (probably because I'm a long time Ubuntu user and it just feels wrong any other way).&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo passwd -l root
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I also make my editor choice permanent.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo update-alternatives --config editor
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Nginx&lt;/h3&gt;
&lt;p&gt;With that taken care of, I was ready to install Nginx.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install nginx
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo /etc/init.d/nginx start
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo emacs /etc/nginx/nginx.conf
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I used &lt;a href="http://articles.slicehost.com/2008/5/15/ubuntu-hardy-nginx-configuration"&gt;this Slicehost article&lt;/a&gt; to learn about the basic nginx conf options.&lt;/p&gt;
&lt;h3&gt;Postgres&lt;/h3&gt;
&lt;p&gt;Then I installed postgres.  I first changed the password on the postgres user
for security.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install postgresql
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo passwd postgres
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo -u postgres psql

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \password postgres&lt;/span&gt;
&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \q&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I set up a new user to use for my site.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo -u postgres createuser myproject
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo -u postgres psql

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \password myproject&lt;/span&gt;
&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \q&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I use a Unix domain socket for my postgres connection, so I have to edit pg_hba.conf to allow md5 login from domain sockets.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo emacs /etc/postgresql/8.4/main/pg_hba.conf
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I change the lines that say:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;# &amp;quot;local&amp;quot; is for Unix domain socket connections only                                                
local   all         all                               ident
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;To:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;# &amp;quot;local&amp;quot; is for Unix domain socket connections only                                                
local   all         all                               md5
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo /etc/init.d/postgresql-8.4 restart
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;(Substitute your favorite VCS here)&lt;/h3&gt;
&lt;p&gt;Now for my VCS.  I use the Git PPA.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install python-software-properties
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo add-apt-repository ppa:git-core/ppa
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get update
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install git-core
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Setting up a Django site&lt;/h2&gt;
&lt;h3&gt;Setting up the structure&lt;/h3&gt;
&lt;p&gt;First, I pulled over my git repositories.  I use the &lt;code&gt;/code&lt;/code&gt; directory for hosting the master copies of my repositories to use as a hub.  I keep the live checkouts in &lt;code&gt;/sites/mydomain.com/code/&lt;/code&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo mkdir /code
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo chown myuser:admin /code
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /code
&lt;span class="nv"&gt;$ &lt;/span&gt;git clone --mirror olddomain.com:/code/repository_name.git

&lt;span class="nv"&gt;$ &lt;/span&gt;sudo mkdir /sites
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo chown myuser:admin /sites
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /sites
&lt;span class="nv"&gt;$ &lt;/span&gt;mkdir -p mydomain.com/&lt;span class="o"&gt;{&lt;/span&gt;code,public,logs,backup&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;mydomain.com
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo chown :www-data logs public
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo chmod g+w logs public
&lt;span class="nv"&gt;$ &lt;/span&gt;git clone /code/repository_name.git code/

Initialized empty Git repository in /sites/mydomain.com/code/.git/
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Media&lt;/h3&gt;
&lt;p&gt;I set up the media and made sure www-data could access it and write to the root directory.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;public
&lt;span class="nv"&gt;$ &lt;/span&gt;ln -s ../code/projectname/media
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo chown :www-data media
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo chmod g+w media
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I synced the media from my old server, so that user-created media wouldn't be lost.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rsync -avz --progress old.domain.com:~/path/to/media/ media/
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Restoring the database&lt;/h3&gt;
&lt;p&gt;The next step was backing up and restoring the database.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh old.domain.com
&lt;span class="nv"&gt;$ &lt;/span&gt;pg_dump dbname | bzip2 &amp;gt; dbname.&lt;span class="sb"&gt;`&lt;/span&gt;date +%Y%m%dT%H%M%S&lt;span class="sb"&gt;`&lt;/span&gt;.sql.bz2
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ssh new.domain.com
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /sites/mydomain.com/backup/
&lt;span class="nv"&gt;$ &lt;/span&gt;scp old.domain.com:~/dnmame.&amp;lt;timestamp&amp;gt;.sql.bz2 ./
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo su postgres
&lt;span class="nv"&gt;$ &lt;/span&gt;createdb dbname
&lt;span class="nv"&gt;$ &lt;/span&gt;bzcat dnmame.&amp;lt;timestamp&amp;gt;.sql.bz2 | psql dbname
&lt;span class="nv"&gt;$ &lt;/span&gt;psql dbname

&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# GRANT ALL ON DATABASE dbname TO myproject;&lt;/span&gt;
&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \q&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Installing dependencies&lt;/h3&gt;
&lt;p&gt;I then created my virtualenv and installed my dependencies.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install build-essential python-dev libpq-dev
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install python-setuptools
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo easy_install -U pip
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo pip install virtualenv
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo pip install virtualenvwrapper
&lt;span class="nv"&gt;$ &lt;/span&gt;mkdir /sites/.virtualenvs
&lt;span class="nv"&gt;$ &lt;/span&gt;emacs ~/.bashrc
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;At the bottom of .bashrc:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;WORKON_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/sites/.virtualenvs
&lt;span class="nb"&gt;source&lt;/span&gt; /usr/local/bin/virtualenvwrapper.sh
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then, back in the shell:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;. ~/.bashrc
&lt;span class="nv"&gt;$ &lt;/span&gt;mkvirtualenv projectname
&lt;span class="nv"&gt;$ &lt;/span&gt;workon projectname
&lt;span class="nv"&gt;$ &lt;/span&gt;pip install -r /sites/mydomain.com/code/requirements.txt
&lt;span class="nv"&gt;$ &lt;/span&gt;deactivate
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;PIL is a tough one.  I usually just install it globally.  Since I install my virtualenv's with --no-site-packages, I need to symlink PIL into the site-packages.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install python-imaging
&lt;span class="nv"&gt;$ &lt;/span&gt;workon projectname
&lt;span class="nv"&gt;$ &lt;/span&gt;cdsitepackages
&lt;span class="nv"&gt;$ &lt;/span&gt;ln -s /usr/lib/python2.6/dist-packages/PIL
&lt;span class="nv"&gt;$ &lt;/span&gt;ln -s /usr/lib/python2.6/dist-packages/PIL.pth
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then, I edited my project's settings.py to reflect the new environment setup.&lt;/p&gt;
&lt;p&gt;Finally, I added ntpdate to keep my server-s clock in sync.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo dpkg-reconfigure tzdata
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo apt-get install ntpdate
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo crontab -e

30 23 * * * /usr/sbin/ntpdate ntp.ubuntu.com &amp;gt; /dev/null
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Nginx&lt;/h3&gt;
&lt;p&gt;I added an nginx.conf to my source control.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;emacs /sites/mydomain.com/code/deploy/nginx.conf
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The Nginx conf:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;www.mydomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;rewrite&lt;/span&gt; &lt;span class="s"&gt;^/(.*)&lt;/span&gt; &lt;span class="s"&gt;http://mydomain.com/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="s"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;mydomain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="s"&gt;/sites/mydomain.com/logs/access.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;error_log&lt;/span&gt; &lt;span class="s"&gt;/sites/mydomain.com/logs/error.log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/media&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/sites/mydomain.com/public&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:29000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I then symlinked it into &lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt; and &lt;code&gt;sites-enabled/&lt;/code&gt;.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo ln -s /sites/mydomain.com/code/deploy/nginx.conf /etc/nginx/sites-available/mydomain.com
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo ln -s /etc/nginx/sites-available/mydomain.com /etc/nginx/sites-enabled/mydomain.com
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo /etc/init.d/nginx restart
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Gunicorn&lt;/h3&gt;
&lt;p&gt;Gunicorn should be installed in your virtualenv as part of your requirements.txt.  If you're not using virtualenv and pip, or zc.buildout, then you should definitely read up on them.  They are a vital part of any serious Django stack.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;emacs /sites/mydomain.com/code/deploy/gunicorn.conf.py
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I already had a simple Gunicorn conf in my source control, which I simply modified for the new environment.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="na"&gt;bind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;127.0.0.1:29000&amp;quot;&lt;/span&gt;
&lt;span class="na"&gt;logfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/sites/mydomain.com/logs/gunicorn.log&amp;quot;&lt;/span&gt;
&lt;span class="na"&gt;workers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Supervisord&lt;/h3&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo pip install supervisor
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo emacs /etc/supervisord.conf
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here's a basic config file:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;[unix_http_server]&lt;/span&gt;
&lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/tmp/supervisor.sock   ; (the path to the socket file)&lt;/span&gt;

&lt;span class="k"&gt;[supervisord]&lt;/span&gt;
&lt;span class="na"&gt;logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/log/supervisord.log ; (main log file;default $CWD/supervisord.log)&lt;/span&gt;
&lt;span class="na"&gt;logfile_maxbytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;50MB       ; (max main logfile bytes b4 rotation;default 50MB)&lt;/span&gt;
&lt;span class="na"&gt;logfile_backups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10          ; (num of main logfile rotation backups;default 10)&lt;/span&gt;
&lt;span class="na"&gt;loglevel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;info               ; (log level;default info; others: debug,warn,trace)&lt;/span&gt;
&lt;span class="na"&gt;pidfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)&lt;/span&gt;
&lt;span class="na"&gt;nodaemon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false              ; (start in foreground if true;default false)&lt;/span&gt;
&lt;span class="na"&gt;minfds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1024                 ; (min. avail startup file descriptors;default 1024)&lt;/span&gt;
&lt;span class="na"&gt;minprocs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;200                ; (min. avail process descriptors;default 200)&lt;/span&gt;

&lt;span class="k"&gt;[rpcinterface:supervisor]&lt;/span&gt;
&lt;span class="na"&gt;supervisor.rpcinterface_factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;supervisor.rpcinterface:make_main_rpcinterface&lt;/span&gt;

&lt;span class="k"&gt;[supervisorctl]&lt;/span&gt;
&lt;span class="na"&gt;serverurl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket&lt;/span&gt;

&lt;span class="k"&gt;[program:myproject]&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/sites/.virtualenvs/myproject/bin/gunicorn_django -c deploy/gunicorn.conf.py&lt;/span&gt;
&lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/sites/mydomain.com/code&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;www-data&lt;/span&gt;
&lt;span class="na"&gt;autostart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;autorestart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;stdout_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/sites/mydomain.com/logs/supervisord.log&lt;/span&gt;
&lt;span class="na"&gt;redirect_stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I created an init.d script:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo emacs /etc/init.d/supervisord
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here's what I used:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Supervisord auto-start&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# description: Auto-starts supervisord&lt;/span&gt;
&lt;span class="c"&gt;# processname: supervisord&lt;/span&gt;
&lt;span class="c"&gt;# pidfile: /var/run/supervisord.pid&lt;/span&gt;

&lt;span class="nv"&gt;SUPERVISORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/supervisord
&lt;span class="nv"&gt;SUPERVISORCTL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/bin/supervisorctl

&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; in
start&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;quot;Starting supervisord: &amp;quot;&lt;/span&gt;
        &lt;span class="nv"&gt;$SUPERVISORD&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt;
        ;;
stop&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;quot;Stopping supervisord: &amp;quot;&lt;/span&gt;
        &lt;span class="nv"&gt;$SUPERVISORCTL&lt;/span&gt; shutdown
        &lt;span class="nb"&gt;echo&lt;/span&gt;
        ;;
restart&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;quot;Stopping supervisord: &amp;quot;&lt;/span&gt;
        &lt;span class="nv"&gt;$SUPERVISORCTL&lt;/span&gt; shutdown
        &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;span class="nb"&gt;        echo&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;quot;Starting supervisord: &amp;quot;&lt;/span&gt;
        &lt;span class="nv"&gt;$SUPERVISORD&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt;
        ;;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Then I finished up the init.d script.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo chmod +x /etc/init.d/supervisord
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo update-rc.d supervisord defaults
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo /etc/init.d/supervisord start
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If all goes well, you can check the status on your site.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo supervisorctl status

myproject                    RUNNING    pid 11616, uptime 0:00:03
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You can also grep for the gunicorn processes.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ps -ef | grep gunicorn
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;Testing&lt;/h3&gt;
&lt;p&gt;To test this new setup, you can add domain name overrides to the /etc/hosts file on your local machine.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Do this on your local machine - not your server&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo emacs /etc/hosts

123.45.67.890 mydomain.com
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now you can go to your browser and access &lt;code&gt;mydomain.com&lt;/code&gt;.  You should see your site.&lt;/p&gt;
&lt;p&gt;To restart your site, use:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sudo supervisorctl restart myproject
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Make sure to change your /etc/hosts back!&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=eKazmgmbORo:-oM8R5z7JhE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=eKazmgmbORo:-oM8R5z7JhE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=eKazmgmbORo:-oM8R5z7JhE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=eKazmgmbORo:-oM8R5z7JhE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=eKazmgmbORo:-oM8R5z7JhE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=eKazmgmbORo:-oM8R5z7JhE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=eKazmgmbORo:-oM8R5z7JhE:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=eKazmgmbORo:-oM8R5z7JhE:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/eKazmgmbORo" height="1" width="1"/&gt;</description><pubDate>Fri, 25 Jun 2010 02:29:58 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/jun/25/provisioning-new-ubuntu-server-django/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/jun/25/provisioning-new-ubuntu-server-django/</feedburner:origLink></item><item><title>Using virtualenvwrapper to start processes and swap config files</title><link>http://feedproxy.google.com/~r/bkonkle-latest-posts/~3/gVircC6fvBg/</link><description>&lt;p&gt;I do my Django development work locally on OS X, so I have several different daemons installed on my machine.  I used to keep Postgres, MySQL, lighttpd, memcached, and more set up to autostart and run continuously, but I didn't like the burden on performance (real or imagined).  Also, I switch between projects frequently and I often need to switch config files based on the project I'm about to work on.&lt;/p&gt;
&lt;p&gt;My solution is to use the &lt;a href="http://www.doughellmann.com/docs/virtualenvwrapper/scripts.html#scripts"&gt;end-user customization&lt;/a&gt; hooks that Doug Hellmann's excellent &lt;a href="http://www.doughellmann.com/docs/virtualenvwrapper/"&gt;virtualenvwrapper&lt;/a&gt; provides.  If you're not familiar with virtualenvwrapper, it makes it easy to manage your virtualenvs in one place and switch between them quickly and conveniently.  The project provides several scripts that are meant to be added to by the end-user to customize behavior.&lt;/p&gt;
&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;p&gt;For my configuration files, I create multiple versions with the changes needed for each project, and I then create a symlink to the version I want at the moment.  For example, with lighttpd I'll create an &lt;code&gt;/etc/lighttpd/project-a.conf&lt;/code&gt; and an &lt;code&gt;/etc/lighttpd/project-b.conf&lt;/code&gt;, and then create a symlink called &lt;code&gt;/etc/lighttpd/lighttpd.conf&lt;/code&gt; that points to the one I need.&lt;/p&gt;
&lt;p&gt;In OS X, I use launchd .plist files that have the OnDemand option set to True.  I've posted the .plists I'm using currently to &lt;a href="http://gist.github.com/448895"&gt;Github&lt;/a&gt;.  Put these in your &lt;code&gt;/Library/LaunchDaemon/&lt;/code&gt; folder and then load them using the command:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;sudo launchctl load /Library/LaunchDaemon/&lt;span class="o"&gt;[&lt;/span&gt;plist_filename&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This registers the processes with OS X's launchd system.  Afterwards, you can start and stop them using the commands:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;sudo launchctl start &lt;span class="o"&gt;[&lt;/span&gt;label&lt;span class="o"&gt;]&lt;/span&gt;
sudo launchctl stop &lt;span class="o"&gt;[&lt;/span&gt;label&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Note that, when starting and stopping, you don't have to use the path to the .plist file.  Just use the daemon's label, which is typically the filename without the path or the &lt;code&gt;.plist&lt;/code&gt; part.&lt;/p&gt;
&lt;h2&gt;Env-specific virtualenvwrapper scripts&lt;/h2&gt;
&lt;p&gt;I use the project-specific &lt;code&gt;postactivate&lt;/code&gt; and &lt;code&gt;postdeactivate&lt;/code&gt; scripts to swap in the right config and start the daemons that I need for a particular project.  You'll find these in the &lt;code&gt;$WORKON_HOME/[project_name]/bin&lt;/code&gt; directory.  If your virtualenv is a little old, those scripts may not already be present.  As long as you're up-to-date with a recent version of virtualenvwrapper, you can simply add those scripts to your virtualenv's &lt;code&gt;bin&lt;/code&gt; directory yourself.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;$WORKON_HOME/project-a/bin/postactivate&lt;/code&gt;, I use a script similar to this:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# This hook is run after this virtualenv is activated.&lt;/span&gt;

&lt;span class="c"&gt;# Postgres&lt;/span&gt;
sudo launchctl start org.postgresql.postmaster

&lt;span class="c"&gt;# Memcached&lt;/span&gt;
sudo launchctl start com.danga.memcached

&lt;span class="c"&gt;# Lighttpd&lt;/span&gt;
sudo mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.bak
sudo ln -s /etc/lighttpd/project-a.conf /etc/lighttpd/lighttpd.conf
sudo launchctl start net.lighttpd

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/code/webapps/project-a-dir/project-a
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;I like having the &lt;code&gt;cd&lt;/code&gt; command there so that I can immediately run &lt;code&gt;manage.py&lt;/code&gt; commands.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;$WORKON_HOME/project-a/bin/postdeactivate&lt;/code&gt;, I use:&lt;/p&gt;
&lt;table class="codehilitetable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# This hook is run after this virtualenv is deactivated.&lt;/span&gt;

&lt;span class="c"&gt;# Postgres&lt;/span&gt;
sudo launchctl stop org.postgresql.postmaster

&lt;span class="c"&gt;# Memcached&lt;/span&gt;
sudo launchctl stop com.danga.memcached

&lt;span class="c"&gt;# Lighttpd&lt;/span&gt;
sudo launchctl stop net.lighttpd
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;So now, when I run &lt;code&gt;workon project-a&lt;/code&gt;, postgres, memcached, and lighttpd all
come to life.  When I run &lt;code&gt;workon project-b&lt;/code&gt;, the &lt;code&gt;postdeactivate&lt;/code&gt; script for project-a runs, and then the &lt;code&gt;postactivate&lt;/code&gt; script for project-b runs.  This effectively switches my lighty conf and restarts the process so it's immediately ready for me.  When I &lt;code&gt;deactivate&lt;/code&gt;, the processes shut down and my computer can take advantage of the free memory.&lt;/p&gt;
&lt;h2&gt;General virtualenvwrapper scripts&lt;/h2&gt;
&lt;p&gt;Virtualenvwrapper also provides global hooks directly in $WORKON_HOME.  &lt;a href="http://twitter.com/brosner"&gt;Brian Rosner&lt;/a&gt; suggested making the config switching generic so you wouldn't have to create scripts for each environment.  I haven't tried this yet, but it definitely wouldn't be hard to do.  The environment variable &lt;code&gt;$VIRTUAL_ENV&lt;/code&gt; is available within &lt;code&gt;postactivate&lt;/code&gt; scripts, so &lt;code&gt;$WORKON_HOME/postactivate&lt;/code&gt; could include a block that looks something like this:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -f /etc/lighttpd/&lt;span class="nv"&gt;$VIRTUAL_ENV&lt;/span&gt;.conf &lt;span class="o"&gt;]&lt;/span&gt;; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="k"&gt;    &lt;/span&gt;sudo ln -s /etc/lighttpd/&lt;span class="nv"&gt;$VIRTUAL_ENV&lt;/span&gt;.conf /etc/lighttpd/lighttpd.conf
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;All you would have to do is name your config files after your environments, and they would automatically be symlinked into place when you activate the environment.&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=gVircC6fvBg:PRMvz3MMPuo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=gVircC6fvBg:PRMvz3MMPuo:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=gVircC6fvBg:PRMvz3MMPuo:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=gVircC6fvBg:PRMvz3MMPuo:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=gVircC6fvBg:PRMvz3MMPuo:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=gVircC6fvBg:PRMvz3MMPuo:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?a=gVircC6fvBg:PRMvz3MMPuo:dZlCUnDz3GY"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bkonkle-latest-posts?i=gVircC6fvBg:PRMvz3MMPuo:dZlCUnDz3GY" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bkonkle-latest-posts/~4/gVircC6fvBg" height="1" width="1"/&gt;</description><pubDate>Tue, 22 Jun 2010 22:45:31 -0500</pubDate><guid isPermaLink="false">http://brandonkonkle.com/blog/2010/jun/22/using-virtualenvwrapper-start-processes-and-swap-c/</guid><feedburner:origLink>http://brandonkonkle.com/blog/2010/jun/22/using-virtualenvwrapper-start-processes-and-swap-c/</feedburner:origLink></item></channel></rss>
