Exploring the vastness of software engineering, at a mile high.

Django applications can be packaged individually for reuse. Documentation on how to do so from start to finish seemed either old, sparse, or incomplete. This was my adventure and solution.

I’ve used Django to develop a few applications. For one project, I wanted to see how many active users there were interacting with the site. The visibility of the project wasn’t too big, so the solution didn’t need to scale for a large or worldwide user base. I just needed something that could handle upwards of a few hundred requests a minute.

I didn’t find anything in pip that fit the bill though. There were solutions using Redis, some that had everything in memory (which would be wiped on every release), and there were some solutions that were overly-complex for super-scale. I just needed something simple; something that could utilize my already existent PostgreSQL backend.

I decided that creating a new model and middleware using Django was the way to go. This was my first time creating a middleware, and surprisingly it was quite simple.

First I made my new application for the project:

$ python manage.py startapp online_users

Then I made the model and some helper methods to store and retrieve the data properly. All that’s stored is a reference to a user and a timestamp of their last activity (read HTTP request) on the site.

When I ran the project to see if data was showing up as expected, everything seemed to work.

Wahoo!

This gives a small view as to how many users have been “active”, or rather how many users have made at least one action or request in the requested time period.

This solution, while extremely simple, could be generalized for others to use. Why not release it to the world? There is sure to be other small projects where this would be useful.

Django’s documentation gives a pretty good account to making reusable apps. Following their instructions, I was determined to move my online_users application out of my project and into its own repository.

First thing to do was to create the new repo, django-online-users, in GitHub. I did so with the MIT license and the Python .gitignore settings. After cloning the repo to my dev box, I copied the application directory over to its new home. Now in my django-online-users directory rested online_users.

Every project needs a good README, so I updated the template GitHub generated, and I added the requirements.txt file with the Django dependency for the project. My new directory structure was starting to take shape:

To ensure the application was still working properly, I made a new virtual environment to test things out and installed the Django dependency:

Starting the application, I visited a few pages and then checked the admin portal to see that my activity was still being logged.

Success! Cool! Now, like a good programmer, I wanted to run the application’s tests again:

$ python setup.py test
...
0 tests ran...

Drat!

Admittedly, I didn’t find a ton of useful information on the web about how to link Django tests properly and run them from setup.py. Eventually I stumbled upon a solution that worked. I added these lines to my setup.py file:

setup(
...
test_suite='nose.collector',
tests_require=['nose'],
...
)

Now when I ran python setup.py test, things were working.

Or.. at least it was attempting to run my tests. I received many verbose errors that every test crashed and burned. Essentially, Django hadn’t been setup properly. This made sense. I no longer had my settings.py or manage.py files that described how Django should work and run.

Eventually, I found I could add a slimmed-down version of my settings to the test’s __init__.py file to setup everything properly.

Running the tests again, things looked better. Django was being started, but my database wasn’t setup to handle the models properly – it was still blank. Adding this line to the bottom of the __init__.py seemed to solve that:

call_command('migrate')

That’s better! Now the tests against the model were running and passing, but the same couldn’t be said for the tests against the middleware. It was complaining that there were no URLs to which it could direct requests.

Alright, adding a simple URL to the settings in __init__.py seemed to help:

… Well fine. I guess this is another one of those times where “the code is the documentation,” and someone forgot to update the wiki. Using Pypi’s website, I was able to register the new project by uploading the PKG-INFO directory that the build made in the django_online_users.egg_info directory.

Hoping that twine was still a good way to upload the sources, I continued to follow their documentation:

$ twine upload dist/*

Success! It uploaded the wheel and the tar.gz.

Periodically I queried pip to see if my package had been distributed across their servers yet.

$ pip search django-online-users

It took roughly an hour – not to bad in my opinion.

Back in my main project I added the new dependency in requirements.txt (django-online-users==0.1) and did a pip install. Running the project locally, everything still worked!

Horray!

The new repository may not be perfect – in fact I’m sure it’s not. Nearly all of the steps and requirements of Python distribution are new to me. At the end of the day though, this worked. Now I have a general-purpose Django application that’s freely available for anyone to use, and by using it, others can view the active user count for their projects.