Apr 3 2012

Dear Netflix…

Oh Netflix, I am so conflicted about you.

Your online streaming is really convenient.  I love being able to watch series from beginning to end.  And it is reasonably priced for me.

Sometimes your streaming selection could be better.  (Not to be confused with Netflix red™.)

Your DVD service helps to fill many of those streaming gaps.

But your DVD service is overpriced.

Your dashboard is well designed.  I love the interface.  

Your queue management is horrible.  Why when I’ve watched something does its status in my queue seem pretty much unchanged?

Your suggested content is great.

You need to handle your browser support better.  It is ridiculous that the Firefox 11 update breaks your instant streaming and requires me to install an add-on that spoofs the Firefox 10 user agent string (User Agent Switcher for those having the same issue).  AND your browser compatibility information is incorrect as it lists “Firefox 3 or higher”, when clearly it should say “Firefox 3-10” given your browser support mechanism. Here is my Firefox 10 user agent string

“Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0) Gecko/20100101 Firefox/10.0”

and here is my Firefox 11 user agent string

“Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko/20100101 Firefox/11.0”.  

You are seriously telling me that you can’t programmatically support that update?  *I* can support that programmatically.  Even if you can’t, you weren’t paying attention to when the Firefox 11 update was released so that you could manually update your allowed user agent list?  After Internet Explorer, Firefox has the 2nd largest share of the browser market.  Why wouldn’t you stay on top of supporting that?  And by supporting I mean simply updating your list of allowed user agents.  You could complete that code change in less time than it would take to create the ticket to track the issue.

You have THE BEST server error experience that I have ever seen (and fortunately for you, I have only seen it once – good thing I’m quick with my Print Screen key). *Brilliant* idea to provide a 100 or so streaming selections that so users could still watch something.

So that’s about 5 greens and 4 reds… I guess I’m sticking with you for now.  Please do something about your User Agent support.  That is probably the most annoying issue.  When I want to kick back and watch something to relax and forget about your other foibles, that browser support issue is REALLY (and ironically) frustrating.  FYI, here is the RapidRelease calendar for Firefox.  Please study it.  New releases about every 6 weeks.  That’s a lot of recurring opportunity to lose otherwise [very? mostly? somewhat?] happy customers.

Well, I’m off to find that Arthur and the Invisibles DVD that we’ve had sitting around since October 26th and still haven’t watched (no lie, and don’t laugh).

Sincerely,

Another (happy?) customer


Mar 1 2012

How to add a UUID field in Django using Django Extensions and how to make it a read-only admin field

I’m using Django Extensions which is a great collection of extensions and utilities for the Django framework.  For a project I’m using it to generate and store UUID fields in several models.  I found an easy way to add that UUID field to my admin as read-only.

A sample model using Django Extensions’ UUID field (I chose to use a version 4 UUID):

 Python |  copy code |? 
1
from django.db import models
2
 
3
from django_extensions.db.fields import UUIDField
4
 
5
class SomeClass(models.Model):
6
    uuid = UUIDField(version=4)
7
    # ...other fields...

Sample admin.py class to show UUID as a read-only field:

 Python |  copy code |? 
1
from myapp.models import SomeClass
2
 
3
from django.contrib import admin
4
 
5
class SomeClassAdmin(admin.ModelAdmin):
6
    readonly_fields = ('uuid',)
7
 
8
admin.site.register(SomeClass, SomeClassAdmin)

That’s it!  Very easy to implement a UUID and make it visible as read-only in the Django admin.  Note that you need at least Django 1.2. More here.


Feb 18 2012

Tips for creating 404 ‘page not found’ and 500 ‘server error’ templates in Django, plus configuring email alerts

One of the steps that you have to take [at least] before deploying a Django project to production is to create templates for 404 (page not found) and 500 (server error) errors.   You can also setup some error and broken link reporting.  Here are a few tips:

Where the 404 and 500 templates live

The 404.html and 500.html templates live in the root of your templates directory (TEMPLATE_DIRS setting).

Creating the 404 template

The 404.html template is shown when a page is not found.  It is often a good idea to maintain the overall look and feel of the site even if a piece of content is not found.  As it is good practice to use template inheritance with Django’s templating system, create a 404.html template that extends your base template.  Here is an example:

 HTML |  copy code |? 
01
{% extends "base.html" %}
02
{% load i18n %}
03
 
04
{% block title %}Page not found{% endblock %}
05
 
06
{% block content %}
07
<h1>Page not found</h1>
08
 
09
<p>Sorry, but the requested page could not be found.</p>
10
{% endblock %}

Testing the 404 template

It is very easy to test your 404.html template.  Simply change DEBUG to False in settings.py and try visiting a non-existent URL.  Your 404.html page not found template should be served.

Creating the 500 template

The 500.html template is shown when there is some kind of catastrophic server error.  Since you cannot be sure what the root cause of the issue is, you cannot rely upon the entire Django framework being available and should therefore keep it decoupled from everything.  A simple HTML file is a good choice.  Here is an example:

 HTML |  copy code |? 
01
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
02
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
03
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
04
 
05
<head>
06
 <title>Page unavailable</title>
07
</head>
08
 
09
<body>
10
 <h1>Page unavailable</h1>
11
 
12
 <p>Sorry, but the requested page is unavailable due to a server hiccup.</p>
13
 
14
 <p>Our engineers have been notified, so check back later.</p>
15
</body>
16
 
17
</html>

Testing the 500 template

Testing the 500.html template is also easy.  If a 404 (page not found) error occurs and Django cannot find a 404.html template, it will return a 500 error and therefore the 500.html template.  Just like testing the 404.html template, be sure that DEBUG is set to False in settings.py.  Temporarily rename your 404.html file to something like 404.html.bak.  Try visiting a non-existent URL and you should be served the 500.html server error template.

Configuring error alert emails

Django can email a list of appropriate folks whenever an unhandled exception occurs, including a trace, variables, and settings.  First, in settings.py, add the appropriate names and email addresses to ADMINS, such as

 Python |  copy code |? 
1
ADMINS = (
2
    # ('Your Name', 'your_email@example.com'),
3
    ('John Doe', 'johndoe@gmail.com'),
4
    ('Jane Doe', 'janedoe@gmail.com'),
5
)

Second, configure Django to send email.  This isn’t hard if you look at the documentation.  In a nutshell, you need to configure the settings EMAIL_HOST in settings.py and possibly EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, EMAIL_PORT, or EMAIL_USE_TLS depending upon how you are sending your emails.  I’m running hMailServer on my Windows development machine and so I only set EMAIL_HOST to localhost and did not have to configure those other options.  I’ve configured hMailServer to relay through Google’s SMTP server using my gmail credentials.  (hMailServer offers some nice abilities to monitor SMTP queues and logging to debug email issues.)  One last gotchya: you will likely have to set SERVER_EMAIL to something other than the default root@localhost, as some SMTP servers block this, which gmail does.  It took me a little while to figure out why my emails weren’t being sent, so if you don’t see them come out the other side, make sure SERVER_EMAIL is set to something like django@myhost.com.

You can easily test this to make sure the emails are being sent by following the steps to test your 500.html server error template described above.  Out the other end you should receive an email with an error TemplateDoesNotExist: 404.html.

Configuring broken link alert emails

As with errors, Django can email a list of appropriate folks whenever a 404 page not found error is raised with a non-empty referrer.  If you want email sent for these broken links you need to make sure that CommonMiddleware is installed (it is by default).  Then edit your settings.py file and add the appropriate names and emails to MANAGERS.  By default, MANAGERS it set to ADMINS, which is probably fine for most of us. You then need to set SEND_BROKEN_LINK_EMAILS to True.

 Python |  copy code |? 
1
# We want to be notified of 404s via email to the MANAGERS
2
SEND_BROKEN_LINK_EMAILS = True
3
 
4
MANAGERS = ADMINS


Feb 17 2012

A better way to set your Django template directory setting — dynamically

When using Django you must specify where the framework can find your templates by setting the TEMPLATE_DIRS setting in settings.py.  Typically I just set it to a static path, but recently came across a great tip about how to set it dynamically (thanks to The Definitive Guide to Django).

In settings.py, I would usually specify something like this:

 Python |  copy code |? 
1
TEMPLATE_DIRS = (
2
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
3
    # Always use forward slashes, even on Windows.
4
    # Don't forget to use absolute paths, not relative paths.
5
    "C:/MyDjango/myproject/mytemplates"
6
)

But there is a much better way to specific this such that it is dynamic:

 Python |  copy code |? 
1
import os.path
2
 
3
TEMPLATE_DIRS = (
4
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
5
    # Always use forward slashes, even on Windows.
6
    # Don't forget to use absolute paths, not relative paths.
7
    os.path.join(os.path.dirname(__file__), 'mytemplates').replace('\\','/'),
8
)

This solution uses os.path.dirname(__file__), which gets the name of the directory that the current file resides in using __file__, which references the current Python module which the code lives in (settings.py).  The appropriate sub-directory mytemplates is appended using os.path.join. Finally, the .replace('\\','/') replaces any pesky backslashes with the required forward slashes.

A couple notes:

  1. Be sure to include the trailing comma at the end of the line, as you are able to specify more than one template directory and Django will not be happy without the comma.
  2. You must import os.path to have access to the appropriate Python functions used to assemble the path.

The flexibility of this solution should be immediately apparent, allowing for easily moving your project around, including to production, without having to update your template directory.


Jan 19 2012

Wondering why SOPA and PIPA are so bad? Very interesting video explains it well (and with a great throwback to Missile Command)!

Very interesting video that explains why PIPA and SOPA are bad!

PROTECT IP / SOPA Breaks The Internet from Fight for the Future on Vimeo.


Dec 6 2011

How to Properly Set a Default Value for a DateTimeField in Django

It is pretty common to have database fields to track the created and updated dates for objects in your model.  But how do you make sure that every time a new object is created they receive the current date and time?  That’s easy: just set the default option for your DateTimeField, such as

created_date = models.DateTimeField('date created', default=datetime.now())

That’s exactly what I’ve done and while developing with Django’s internal development server it works great!  Then I deployed my project to production which uses Apache with mod_wsgi and I noticed that the timestamps were wrong.  They seemed to coincide with when I had restarted Apache.  I googled things like “django datetime incorrect” and found other people with the same problem.  Some people resorted to overriding the save method, while others created database triggers to update with the current date and time on insert.  I didn’t think I should have to override the save method for something like this and I certainly didn’t want to start creating database triggers (I use Django to keep all of my logic in one place and abstract away my database!).

After doing some more research, it turns out that while I had read the manual, I didn’t understand the manual.  Referring to the Django docs for the default field option for models

The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created.

you can see that you can either pass a value or callable object.  I thought that I was telling Django to evaluate the current time every time a new object was created, but I wasn’t.  What instead was happening was datetime.now() was being evaluated when the class was first instantiated (when the web server was restarted!) and that value was being reused every time a new object was created.  I needed to instead pass a callable object that would return the current date and time whenever a new object was created.

I updated my code from this

created_date = models.DateTimeField('date created', default=datetime.now())

to this

created_date = models.DateTimeField('date created', default=datetime.now)

Note that datetime.now() is evaluated only once when the class is instantiated and datetime.now is a callable object that is evaluated each time a new object is created.  Including the parenthesis executes the function and passes the result while excluding the parenthesis passes the function itself.  This was a good lesson to learn and will definitely help me with my future Django coding.  Hopefully this tidbit helps someone else as well.


Nov 16 2011

Managing expectations for browser and device support

I’m currently reading Andy Clarke’s Hardboiled Web Design, which is very interesting.  I particularly like his suggested language around how to word contracts (and expectations) when it comes to browser and device support.

 “The landscape of web browsers and devices changes regularly and our approach is to look forward, not back. With that in mind we will test all our markup and CSS in current versions of all major desktop browsers to ensure that we make the most from them. Users of older or less capable browsers or devices will experience a design that is appropriate to the capabilities of their software. For people using Microsoft Internet Explorer 6, this means a universal, typographically focussed [sic] design but no layout.”

I think it really makes clear how you can have your clients’ best interests at heart.  Not only are you helping them get more value out of their money and your time by not having you hack around for too long, but you are also demonstrating your forward thinking approach to meeting their current and future needs.


Oct 26 2011

Opera even downloads and installs the fastest of all browsers!

OperaThe last time that I used Opera was back in the early 2000s while still at WPI.  I remember how it seemed to popularize the notion of tabbed browsing.  Before that I was using Netscape.  My past few years of web browsing have mostly occurred on Firefox and more recently Chrome.  However, being a fine upstanding citizen of the web community, I try to do my part with cross-browser/device testing.  So, between yesterday and today I’ve been installing the various web browsers onto a new laptop and decided that I’d give Opera another look-see.

Here are my experiences downloading and installing these various browsers: Continue reading


Aug 9 2011

3D Webpage Visualization

A co-worker mentioned a plugin for Firefox called Tilt, which allows you to rotate any webpage in 3 dimensions.  This allows you to easily visualize the Z-direction for your various elements.  I can definitely see the benefits of using this when designing your non-run-of-the-mill site.  At a minimum, it is neat to play with.


Aug 9 2011

How to Enable iframes for WordPress Posts

For my last post I wanted to embed a Vimeo video, but by default WordPress strips out iframes when you switch from the HTML to Visual editor.  This can pose a problem when posting videos from Vimeo or YouTube, Google Maps, or other content.  After some reading I found and tried the solution here and it seems to work well!