Fun with Pagination – Back and Ahead by 5

Yesterday I wanted to beef up a ListView-based page with additional navigation controls.  Specifically, I decided to add buttons to jump back 5 and ahead 5 pages in the list.  With a little bit of class extension, this was easy to do.


from django.core.paginator import Page, Paginator

class PageFive(Page):class PageFive(Page):

    ''' Include info for next 5 and previous 5 pages '''

    def has_next_five(self): 
        return self.number < self.paginator.num_pages - 5 

    def has_previous_five(self): 
        return self.number > 6

    def next_five_page_number(self): 
        return self.paginator.validate_number(self.number + 5)

    def previous_five_page_number(self): 
        return self.paginator.validate_number(self.number - 5)

class PaginatorFive(Paginator):

 ''' Uses the PageFive class to report info for next and previous 5 pages
     Set pageinator_class in ListView to use '''

    def _get_page(self, *args, **kwargs): 
        Return an instance of a single page using the PageFive object 

        return PageFive(*args, **kwargs)

First step was to extend the Pageclass to add additional methods.  Mimicking the existing methods for next and previous pages, these four new functions return information on the previous five and next five pages of the list.

To use my new PageFiveclass, I also extended the Paginator class.  Lucky for me, the authors have included the _get_page()method as a hook to modify the Pageclass used.  Simple to override.

Next, I have to tell ListViewto call my redefined paginator.  The paginator_classvariable is used to make that change:

class MyListView(ListView):
    model = MyModel
    paginate_by = 10
    paginator_class = PaginatorFive

Finally, I can use the new methods of the PageFiveclass in the template:

<!-- PaginatorFive navigation buttons -->
 {% if is_paginated %}
   <ul class="pagination pagination-centered">
     {% if page_obj.has_previous %}
       <li><a href="?page=1">First</a></li>
       {% if page_obj.has_previous_five %}
         <li><a href="?page={{ page_obj.previous_five_page_number }}">Back 5</a></li>
       {% endif %}
       <li><a href="?page={{ page_obj.previous_page_number }}">Prev</a></li>
    {% endif %}

    <li class="active" ><a href="?page={{page_obj.number}}">{{page_obj.number}} of{{ page_obj.paginator.num_pages }}</a></li>

    {% if page_obj.has_next %}
       <li><a href="?page={{ page_obj.next_page_number }}">Next</a></li>
       {% if page_obj.has_next_five %}
         <li><a href="?page={{ page_obj.next_five_page_number }}">Ahead 5</a></li>
       {% endif %}
       <li><a href="?page={{ page_obj.paginator.num_pages }}">Last</a></li>
     {% endif %}
 {% endif %}

This was a fun little exercise.


(Sorry for the word wrap in the code.  Copy to your editor for a better view)

Django, Gunicorn, Nginx, & HTTPS

Django, gunicorn, nginx, and https logosI can find many examples of using Django, Gunicorn, and Nginx for an application, and also ways to implement HTTPS on Nginx, but not all together.  This post will document my final configurations and explain my decisions.

First, the code:


worker_processes  auto;

events {
    worker_connections  1024;

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    ## Rewrite http to https

    server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name _;
        return 301 https://$host$request_uri;

    ## Use https

    server {
        add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; ";

        listen              443 ssl;
        ssl_certificate     <path-to-certificate>;
        ssl_certificate_key <path-to-private-key>;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;

        location = /favicon.ico { access_log off; log_not_found off; }

        location / {
            proxy_set_header Host               $host;
            proxy_set_header X-Real-IP          $remote_addr;
            proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Host   $host:443;
            proxy_set_header X-Forwarded-Server $host;
            proxy_set_header X-Forwarded-Port   443;
            proxy_set_header X-Forwarded-Proto  https;
            proxy_read_timeout 300s; 
            proxy_pass http://localhost:8000;

##bind = ":8000"
workers = 3
timeout = 300
proc_name = "dupe"
user = "<your user>"
group = "<your group>"
raw_env = []
capture_output = True
loglevel = "debug"
errorlog = "<path-and-name-for-log-file>"


Description=gunicorn daemon

User=<your user>
Group=<your group>
ExecStart=<virtualenv.path>/bin/gunicorn -c <application.path>/  --bind <project>.wsgi:application



nginx.conf holds all of the interesting stuff

The first server section grabs any http traffic coming in on port 80 and redirects it to the https URL on port 443.

Handling https traffic was trickier. We need to pass all of the header settings from the incoming request on to gunicorn/Django at localhost. All of the proxy-set attribute values in the location section came from a little bit of trial and error.

The setting for proxy-read-timeout is admittedly high – five minutes! This application has one longer running transaction that was exceeding the 60 second default. This value needs to match the timeout setting in the gunicorn settings. I’ll probably lower it to a more respectable two minutes before hitting production.

Finally, astute readers will notice that there is no code to handle requests for static resources. This is because I am using Whitenoise to route those calls through Django. This works only because my app just has eight users and Django can handle the load. Anything that is public facing should use Nginx to serve static files directly (or look into using a CDN, etc.)

Not much in

I’m using 3 workers – plenty for my expected load. The timeout is set to 300 seconds, which is the same found in nginx.conf above. These should match.

The bind setting is commented and moved to gunicorn.service so that I can use this file for more than one instance of the application. A second service file can specify a unique port for its copy of gunicorn.

gunicorn.service – Instance specific settings

Since my app is stored in a git repo (on a company server, not github), I don’t want to include any security information like the SECRET_KEY in a versioned file. Therefore, this guy is not included there – although an example file is.

The Environment variables set here are read by Django at startup.

The ExecStart line starts by running the version of gunicorn found in the virtualenv for this instance. Setting up a virtualenv is beyond this scope of this post, but I hope that if you’ve gotten this far you know all about them.

Note that the bind parameter includes the port number used for this instance of gunicorn as mentioned above.

Questions? Comments?

I hope this helps someone setting up a Django app. Please drop a note in the comments with your experience. I’d also invite questions, as well as comments on how I can improve my configuration. Thanks all!

Django and LDAP

LDAP GraphicA project at work requires that I authenticate users using an LDAP service. Who knew it would be so easy?!?

I’m using Django 1.9.2, but I imagine that this will work with other versions +/- a few releases. Python version is 3.5.


The library that does the magic is django-auth-ldap. For Python 3.x, pyldap is required, while python-ldap is used for Python 2.x. I also had to install some system libraries: libsasl2-dev, python-dev, libldap2-dev, and libssl-dev (on Ubuntu).


The AUTHENTICATION_BACKENDS setting is not defined in the default Django file using v1.9.2. However, the code I use below expects the variable to exist. Therefore, I added it in with the default setting. You may have other backends already in use.



This code lives at the end of Use your own LDAP URI, ou (Organizational Unit), and dc (Domain Component).

#   LDAP Settings

AUTHENTICATION_BACKENDS += ('django_auth_ldap.backend.LDAPBackend',) 

AUTH_LDAP_SERVER_URI = "ldaps://your.ldap.server"

AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=users,dc=example,dc=com"

The first line adds an additional authentication backend to the tuple. Next, we define the path to the server. Note that I’ve used a secure protocol (ldaps). This is essential to keep the credentials passed to the server encrypted. The third line sets up a direct bind with the supplied user.

In place of the direct bind method, the documentation also suggests a user search configuration. I was able to get this to work as well, but it requires that your password be included in (or you are performing some trickery to get the password). My LDAP admin is not fond of this method, but will create an application-only account if needed. Here is the user search configuration:

import ldap
from django_auth_ldap.config import LDAPSearch

AUTH_LDAP_BIND_DN = "<user>"
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
    ldap.SCOPE_SUBTREE, "(uid=%(user)s)")

I’m sorry to say that I’m not an LDAP expert at all, so I can’t supply much advice if this doesn’t work for you. Buy your LDAP admin a lunch sometime and ask your questions while waiting for the food.

Best of luck!!

Sunfounder Project Super Starter Kit for Raspberry Pi Model B+

Sunfounder B+ Kit
I ordered this long-named kit as a practice tool so that I could learn more about the Raspberry Pi. My quick review: the documentation sparse, the wiring diagrams mostly correct, and the instructions – especially the programming sections – are incomplete. These factors work together to force me to experiment and research in order to make these blinky light projects work. Learn by doing, I guess.

Anyway, almost all of the code is written in C, but you all know that I’m a Python guy. So I’ve been rewriting each project in Python as I work through the list. I may never get through them all (interest is waning), but it has been fun to learn the Python interfaces.

My work is available on Github for your perusal. Feel free to comment.

This is How I Work

Lifehacker publishes a series of interviews called How I Work where they interview tech notables and other interesting people to find out what their daily work routine is like. Now, I am under no illusion of being either notable or interesting, but I saw this as a writing exercise and an opportunity to document this point in my life.

If you find this exercise appealing enough to write your own interview, please leave a link in the comments below.


Traverse City, Michigan

Current Gig:

Director of Administrative Systems for Northwestern Michigan College – In a nutshell, I lead the team that supports the various administrative software systems in use at the college. Banner from Ellucian is in the center, with interfaces to smaller programs.

One word that best describes how you work:


Current mobile device:

An HTC One that is a hand-me-down from my wife.

I’ve worked with many different handhelds over the years. First was a Palm Pilot – I was an expert with Graffiti! Next I had a Blackberry pager model for a while, followed later by a Blackberry Phone.

It was tough to imagine that I would be able to give up the thumb keyboard, but I made the jump to an iPhone with the model 3S. On a two-year contract, the first year was a positive experience, while the second was quite awful. Apple’s software had moved on, leaving my hardware to struggle against the load.

Since then I have used a number of Android devices, and I see no reason to switch.

Current computer:

Like most offices, my employer runs a Windows shop. My machine is whatever Dell desktop was in vogue two years ago, and I bumped it up to 8GB of RAM last year. I have the option of using Linux with Windows in a VM, but just haven’t been fired up enough to take the time to reinstall everything. Maybe that will happen with the next hardware refresh.

At home I was a long time Linux user – Ubuntu is my flavor of choice, but last year I gifted myself a MacBook Pro 13” (late 2013) as a reward for finding my current job. Like most developers, I take full advantage of the Unix underpinnings of Mac OS X. Plus, it looks so cool when I’m sitting at a small table in the back of the hipster coffee shop downtown.

There are still a couple of aging laptops laying around that get occasional use, and they all run older versions of Ubuntu. (These are the only machines with optical drives. Where else can I rip a CD?)

UPDATE: Shortly after posting this article I took the plunge and updated my work machine to Ubuntu 14.04 LTS. There is a Windows VM, but I rarely use it. Big thanks to Wine for allowing me to run my favorite Oracle tool, PL/SQL Developer, on Linux.

What apps/software/tools can’t you live without? Why?

I used to be a Google Reader disciple, and like many I was lost when the service was shuttered. Feedly now fills the void pretty well. However, there are fewer sources using RSS in a way that makes a feed reader the best way to gather information.

Pocket has become an important part of my workflow. I can share a link from just about any Android app, and a simple right click in a browser can also send an article there. I enjoy the offline nature of the Android app so that I can read articles regardless of the connectivity available.

My go-to editor is Sublime Text. I spent many years as an Eclipse devote, but I have come to appreciate the command line life. However, I’m not religious about it; use whatever you want.

What’s your workspace setup like?

At work my Dell workstation is hooked to a pair of 20” monitors and a wireless keyboard/mouse set. One of the monitors spends much of its time swiveled around so that I can share the view with a guest. The wireless mouse is useful in this situation.

At home I have a desk with a large monitor and USB keyboard and mouse. However, I spend more time in my easy chair than at the desk. Laptops are the best!

What’s your best time-saving shortcut/life hack?

I prefer to use a paper notebook (right now an unruled Moleskine which I can never find on the website) for note taking in meetings. Pencil to paper can reinforce thoughts and help my brain work on things when I’m nowhere near any technology. It’s also easy to integrate diagrams and decorate with doodles. These notes are mostly just for me, but I have scanned pages and shared a few times.

At meetings, I am often the only person not using the tablet/keyboard combo. I find they spend too much time checking email during the meeting instead of paying attention.

What’s your favorite to-do list manager?

I sometimes write a list in my notebook, but most of my assignments live as starred emails in Gmail. I can view it anywhere, and it is always up-to-date.

Besides your phone and computer, what gadget can’t you live without and why?

I love my current headphones, the Plantronics BackBeat GO 2. These wireless Bluetooth earbuds can connect to both of my computers and my phone, so I’m never tethered to anything. They have a good sound with plenty of isolation. Only down side is the battery life – I recharge once during the day as well as overnight.

What everyday thing are you better at than everyone else? What’s your secret?

My number one goal at work (and in life I guess) is to work towards compromise, and I have developed strong techniques to guide a conversation towards a common understanding of a problem and the possible solutions. I may not be the best at it, but I’m no slouch.

What do you listen to while you work?

Music is pretty important to my ability to concentrate. I am a constant user of Google Play Music with most of my collection uploaded to their servers.

I like all kinds of rock music, and lyrics don’t bother me in the least. I tend to upload the entire discography of an artist and listen from A to Z or chronologically. (This week I’m introducing myself to The Flaming Lips.)

What are you currently reading?

I haven’t read a book in a long time. Long form articles on the web are as close as I get. Medium provides so many good reads, and The New Yorker and Slate post insightful political and social pieces.

Are you more of an introvert or an extrovert?

I wish this question wasn’t part of the How I Work series. I am neither – or both – whatever the situation calls for.

What’s your sleep routine like?

I head to bed early – maybe 9:30, but I’ll read in bed for an hour or so. I’m usually up by 6:00 or 6:30 every day.

Fill in the blank: I’d love to see _________ answer these same questions.

I wonder how professional musicians organize their lives. How does one channel creativity into the cycle of write, record, perform? So many are becoming their own managers, booking agents, etc., and it must be difficult to keep the creative and business sides both moving forward. Maybe Jack Conte.

What’s the best advice you’ve ever received?

When I was 39, an elected official I was working with, himself nearing 70, said I was just a pup. I like to maintain that mindset – learning for the future.

Is there anything else you’d like to add that might be interesting to readers/fans?

I would be surprised to learn that I have readers or fans.

Django Crispy Forms

Post Crispy Critters cereal boxI’ve been looking at Django Crispy Forms in a new project I’ve been working on. Still getting my head around what it can do, but I can already see some advantages.

However, I have hit a couple of problems with version 1.4.0 that I took time to fix.

  1. I’m writing my code to work with all of the currently supported versions of Django – 1.4, 1.6, 1.7 – with 1.5 thrown in for good measure. I noticed the tests failing with v1.4.16. Turns out a comparison of version numbers in the tests was ranking 1.4.16 between 1.4.1 and 1.4.2. I modified my fork of the code to use the StrictVersion class for all version number checks, and that fixed the test issues.
  2. Along with multiple Django versions, I’m also working to be compatibile with a number of Python versions – 2.6, 2.7, 3.2, 3.3, 3.4. Once again, crispy_forms had a problem. Well, not really a problem. The package is not advertised to work with Python 3.2. Luckily, it’s an easy fix to handle the unicode literal changes that were reversed in v3.3. The key is from __future__ import unicode_literals at the top of each code file, and removing u from each literal string. u'some text' becomes 'some text'.

I submitted the first fix as a pull request on the dev branch. Hopefully it will be accepted and merged. Python 3.2 compatibility has already been submitted as the subject of two different pull requests, so my code remains local.

These fixes have been applied to v1.4.0 in my fork of the package, and all are free to use this version until these issues are handled in the main repository. The github location is:

See the strictversion branch.

Or, one can include it in a requirements file for pip as follows:

-e git://

It’s fun to contribute to a community open source project. I’ll do more of this.

Looking for OSS or Pro Bono Django Work

I’m settling in to a new job in a new town, and life is pretty good. Unfortunately, my duties don’t include any chance to program in Django. Our main software vendor is moving from Oracle Forms to Groovy/Grails, so I’m sure I’ll be learning more about that platform.

However, I would still like to keep my Django skills sharp, so I am offering my services to any non-profit group that needs an extra Django hand. No salary is expected, just a chance to get involved.

An open source project would be a great place for me to assist, and would also be a learning experience for me. Is there a project out there that needs a contributor with Django knowledge?

A charity or other non-profit would be another option. Perhaps there is an association developing OSS software for non-profits to use. With my experience supporting fundraising and higher education, I feel I could make a meaningful contribution.

If you have an opportunity fitting the description above, or know someone who does, please send me a message I am ready to help!

CAS with Django 1.5

I’ve posted a couple of times before about using a Central Authentication Service with Django. As I update my applications to Django v1.5, it is time to revisit the subject.

In my Django v1.3 setup, the django-cas library has served me well. However, in this later version of the framework, there are changes that break the library. The maintainer of django-cas has not yet updated their code to work with 1.5 (although there are pull requests waiting that address the issue – stay tuned).

Kansas State WildcatLuckily, another educational institution has forked the library. Our friends at Kansas State University (Go Wildcats!) have released their maintained version of django-cas on github. They have fixed the issue with the get_host() call, plus added some proxy features. Note that the fix is in a branch and not yet in a released version – currently at v0.8.5. I have installed it in my first app to be converted, and it works fine.


As far as I can tell, this K-State fork is not hosted on PyPi at this time, so I use this line in my requirements file to get the library via pip:


This uses Subversion because that is what is available on the servers, but the git syntax will work just as well.


K-State has provided only minimal documentation, but they link back to the original CCPC installation guide. Below are my and code. (Note that USE_CAS is a variable I define that is set True or False based on the environment.)

## django_cas settings
    CAS_SERVER_URL = '### your server URL here ###'
    CAS_VERSION = '2'


## end django_cas settings

# django_cas urls
if settings.USE_CAS:
    urlpatterns = patterns('',
    ('^admin/logout/$', RedirectView.as_view(url='../../accounts/logout')), ## TODO: This could be better
    ('^admin/password_change/$',RedirectView.as_view(url='### URL to your password change service ###')),
    url(r'^accounts/login/$', 'cas.views.login',name='login'),
    url(r'^accounts/logout/$', 'cas.views.logout',name='logout'),
                       ) + urlpatterns
    urlpatterns = patterns('',
    url(r'^accounts/login/$', 'django.contrib.auth.views.login',{'template_name': 'login.html'},name='login'),
                       ) + urlpatterns       
# end django_cas urls

Biggest change is that the package name changed from django-cas to cas.

Best of luck with your CAS setup!

Factory Boy and Django: Software Updates

Turn of the Century boy in a factory

I’ve been using Factory Boy in all of my tests for a while, but I hadn’t upgraded the package since setting up my development machine in December of last year. This meant I have been happily chugging along with v1.20.

Today I started the task of upgrading my applications to Django v1.5, so I ran pip install --upgrade on my requirements file. Django moved up to v1.5 just fine, but Factory Boy also jumped up to v2.11. I hadn’t noticed that some new features and several deprecations were added in v1.3, and many changes happened for v2.0.


The first odd behavior I noticed was that the model instances created by the factories weren’t being saved, and I also had trouble adding data to ManyToMany relationships even after I explicitly saved them. Turns out there was a major change to the Factory class. Rather than use that generic class, we are now being directed to use DjangoModelFactory.

The error I was seeing was a recursion problem, so it took a while to track down.


Next, all of my Sequence calls were failing – telling me that I couldn’t use a string operation on a type of ‘int’. In this case the problem was that the default data type for a sequence was changed to ‘int’. Since ‘str’ was the former default (although not necessarily documented), my solution was to declare the type as ‘str’.

name = factory.Sequence(lambda n: 'Location ' + n, str)

I’m a big fan of this package for tests, and I am glad that it is a very active project. Read through the change log for more info on these and many other updates.

I promise to do a better job of keeping up with the changes.