New Django site: polurls.com

polurls | the political blog aggregatorTwo of my great passions (OK…obsessions) are web tech and politics. It was just a matter of time before I mashed them together.

Recently, I’ve been diving head first into Django, looking to complement my Java-based toolset — honed via ParentShack.com and Sharenik.com — with some Python/Django ones.

Last week I launched polurls.com, a political blog aggregator which is not only my first live Django site but also the spawn of my politics and tech love. Think of it as the popurls of politics. A site which aggregates left-leaning political blogs on polurls.com/blue, right-leaning blogs on polurls.com/red and the whole spectrum of political blogs on polurls.com/purple.

My hope is that by showing conservative, progressive and centrist blogs side-by-side that polurls visitors will get a truly balanced take on the latest political news.

I’ve already found the site to be a very quick and interesting way to scan the latest political news. I’m eating my own dog food and loving the taste!

I’d love feedback either in the comments or on twitter (@mfournier or @polurls).

Specify a custom manager for the Django admin interface

As I was running through the weblog example in James Bennett’s excellent “Practical Django Projects (2nd Ed)” book I ran across a problem. In the book, we are asked to create a custom manager for the Entry model. Instead of pulling all entries, the custom manager only pull the entries that have been marked as LIVE (and ignores the ones that are marked HIDDEN or DRAFT).

This custom manager is set as the _default_manager (by defining it first in the Entry class), which is fine, except for the fact that the Django admin interface “defaults” to using the default manager of a class. In the admin, I want to see and edit ALL objects, not just the live ones.

To further complicate things, we create a custom tag that takes any type of content and displays the most recent elements of it. This tag is model agnostic and so uses the _default_manager of the model that it is looking at. So, for the Entry model, the default manager needs to remain the custom one which shows only LIVE results.

Luckily, there is a fairly simple way to tell the admin interface to not use the default manager. Simply change your ModelAdmin class in you admin.py file from something like this:

class EntryAdmin(admin.ModelAdmin):
    prepopulated_fields = { 'slug': ['title'] }

To something like this:

class EntryAdmin(admin.ModelAdmin):
     prepopulated_fields = { 'slug': ['title'] }
     def queryset(self, request):
         return Entry.objects

And make sure that your Entry model has its managers defined thusly:

# Give the Entry model two managers. NOTE: the first one is the default!
live = LiveEntryManager()   # _default_manager #
objects = models.Manager()

Now everywhere that you use Entry.live.all() or Entry._default_manager.all() You’ll pull only the LIVE results while the admin interface will show all of the LIVE, DRAFT and HIDDEN results.

(Let me know in the comments any unspeakable horrors this solution might stir up ;)

Make Firefox look (and act) like Google Chrome

Chrome logoI love almost everything about Google’s Chrome browser: the startup speed, the clean and simple interface, the javascript processing speed, the extensions (especially not having to reboot when you add extensions!) and the tabs-as-processes.

What I don’t love, however, is the lack of a built-in master password mechanism for hiding and protecting all of my most sensitive site credentials. A quick visit to “Options > Personal Stuff > Show saved passwords” and you’ll see all the typical passwords I use at my favorite sites.

I do use LastPass, but my Firefox experience tells me that relying on extensions to provide the required core functionality can result in frustration around browser update time. Granted, I’m using a bunch of Firefox extensions to mimic Chrome, but these are just for cosmetics. If any of them go at my next update, it’s not the end of my browsing world.

My secondary concerns with Chrome are its more bloated RAM use and its lack of a good LeechBlock alternative for keeping me on task. I could probably live with these, however, but I can’t live with the master password oversight.

So until Chrome has a master password, I’ve customized Firefox to look and act like it:

FF as Chrome


  1. Get and install the “Chromifox Basic” theme for Firefox:
    https://addons.mozilla.org/en-US/firefox/addon/8782
  2. Get and install the “Omnibar” extension:
    https://addons.mozilla.org/en-US/firefox/addon/8823
  3. Get and install the “Tiny Menu” extension:
    https://addons.mozilla.org/en-US/firefox/addon/1455
  4. Get and install the “Toolbar Buttons” extension:
    https://addons.mozilla.org/en-US/firefox/addon/2377
  5. Restart Firefox
  6. Right click on your Firefox menubar and click “customize”. Move the newly minimized menu to the far right, add toolbar buttons for “Toggle the Bookmark Toolbar” | “Open Add-ons Manager” | “Print this page” and move the new “omnibar” to the right of the forward/back/reload buttons
  7. Optionally, remove the excessive location bar icons (RSS, bookmark star) by creating a userChrome.css file (located here in my Windows 7 install: C:\Users\Mitch\AppData\Roaming\Mozilla\Firefox\Profiles\vggf8vt5.default\chrome) and adding the following:
    /* Remove the Bookmark star from the location bar */
    #star-button {
    display: none !important; } 
    
    /* Remove the feed button from the location bar */
    #feed-button {
    display: none !important;}
  8. Restart Firefox again

Voila! Many of the benefits of Chrome, all in the feature-rich (aka: master password containing) container of Firefox!

Errata: Practical Django Projects 2nd Edition (PDF)

UPDATE! Even better than listing out the individual errors, Phil Gyford has posted his working code for the examples in James Bennet’sPractical Django Projects 2nd Edition“. You can find it on bitbucket here. Thanks Phil!


Since a quick Google search failed to turn up these e-book errate for James Bennets informative “Practical Django Projects 2nd Edition”, I’ll compile my own list. Hopefully my frustration in overcoming these errors will save you from the same.

Chapter 4, page 66:

(r'^weblog/(?P<year>\d{4})/(?P<month>\w{3})/(?P<day>\d{2})/(P?<slug>[-\w]+)/$',
'coltrane.views.entry_detail'),

should be:

(r'^weblog/(?P<year>\d{4})/(?P<month>\w{3})/(?P<day>\d{2})/(?P<slug>[-\w]+)/$',
'coltrane.views.entry_detail'),

(note the “P?” vs. “?P” before <slug>)

Chapter 4, page 70

Author should mention that the following must be added to the top of urls.py once you switch to generic views:

from coltrane.models import Entry

Chapter 4, page 71 and 73

Each of the four urlpatterns which include:

weblog/(?P<year>\d{4}/

Should actually be:

weblog/(?P<year>\d{4})/

(note the “)” after the {4})



Follow

Get every new post delivered to your Inbox.