A step-by-step guide to creating a Django project from scratch with Pinax starter projects and apps.
This post documents my step-by-step creation of cloudspotting
, a demonstration project built with the Pinax ecosystem. I show the ease of building a full-featured application using Pinax starter projects and apps. Links throughout this text provide git repository source and commit references on GitHub, including goofs I made along the way.
The idea for cloudspotting is simple: Users create cloud image collections organized by cloud type, i.e. "cumulonimbus" and "cirrus". Each cloudspotting user can view everyone else's collections, and "like" any specific collection.
Cloudspotting development begins with a Pinax starter project and adds example usage of pinax-images
, pinax-likes
, pinax-announcements
, and pinax-testimonials
.
Our Audience
- Somewhat familiar with Django
- Unfamiliar with Pinax starter projects
- Eager to leverage Pinax apps
Choosing a Starter Project
My first goal is determining where to begin. The Pinax ecosystem contains starter projects in separate branches of pinax-starter-projects.
Some branches are basic building blocks, "foundational" projects such as zero and account. These projects form a quick-start base for building your own site.
Other starter projects are "demo" projects, which show how a particular Pinax app works. Examples include documents, stripe, and blog.
These starter projects incorporate zero
and account
and provide enhanced functionality from Pinax apps pinax-documents
, pinax-stripe
, and pinax-blog
respectively.
Since Pinax doesn't have a demo starter project for pinax-images
, I must decide between a foundational and a demo project.
I choose the Pinax documents
demo starter project because pinax-documents
uses AJAX for document management and pinax-images
conforms to the same eldarion-ajax
standard as pinax-documents
.
Document management is not cloudspotting's focus but image management is, and hopefully pinax-images
is a drop-in replacement. Furthermore it seems the documents
starter project contains all the pieces needed for a "standard" Django app.
I will rip out pinax-documents
related files, integrate pinax-images
, create cloudspotting models and views, and add tests. With that decision made it's time to get coding!
Development
Initial Configuration
First I read the Pinax Starter Project directions and create a virtual environment:
virtualenv cloudspotting
source cloudspotting/bin/activate
cd cloudspotting
Following the same directions I install and invoke the "pinax" command line utility:
pip install pinax-cli
pinax start --dev documents cloudspotting
FYI, "pinax-cli" is the package name, which stands for "Pinax Command Line Interface". "pinax" is the installed executable you invoke to create projects.
The "pinax start" command magically creates cloudspotting source code based on the Pinax documents
starter project.
Since the documents
starter project does not have an official release, I add the --dev
option to get the latest development branch of documents
. (Without that option, the "pinax" command line tool would try to obtain the latest official release.)
Following best practices I initialize a git repository and make an initial commit from the newly created project.
Renovation
Now it's time to renovate, ripping out code I don't need and adding new functionality. First I replace "pinax-documents" with "pinax-images" in requirements.txt, then install project requirements:
pip install -r requirements.txt
Reading pinax-images
documentation reveals a front-end development shortcut. I install pinax-images-panel
, a Javascript library for communicating with pinax-images
and managing image collections.
My npm invocation includes the --save
option to add this requirement to the cloudspotting packages.json
file.
npm install pinax-images-panel --save
Browsing the source code I find and remove unneeded "documents" references, replacing with "images" as required.
Building Cloudspotting
Most of the unneeded code is gone, so I create the primary cloudspotting model, CloudSpotting
.
There isn't much to a CloudSpotting, just the cloud type, the user who created the collection, and a collection of images. The image_set
field points to an instance of ImageSet
, defined in pinax-images
.
class CloudSpotting(models.Model):
cloud_type = models.CharField(max_length=100)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="+")
image_set = models.ForeignKey(ImageSet, blank=True, null=True)
New models need migrations, so I create an initial migration.
$ ./manage.py makemigrations cloudspotting
Migrations for 'cloudspotting':
0001_initial.py:
- Create model CloudSpotting
- Alter unique_together for cloudspotting (1 constraint(s))
(cloudspotting) graham@Grahams-MacBook-Pro ~/venv/cloudspotting/src/cloudspotting
$
Cloudspotting will support standard views like "create image collection" and "list all image collections".
My design says users must be signed up to use cloudspotting views, and may only manipulate their own images and collections.
I create Django generic class-based views and urls which satisfy these requirements using Django's LoginRequiredMixin and a custom get_queryset()
mixin.
Next I add cloudspotting templates to accompany the new views. The use of Django generic class-based views allows me to simplify views.py by removing explicit template names and relying on the default Django CBV template naming pattern.
Pinax contributor Anna Ossowski helps me out and kindly creates a nice README for the project. Thanks Anna!
Shakedown Cruise
Cloudspotting now has minimal models, views, urls, and templates and is ready to run. I could write tests and prove the code works as expected (always good practice), but I'm eager to see if cloudspotting runs smoothly or belches smoke. I create database tables and invoke the Django development server to see what happens.
First I create database tables via migration:
$ ./manage.py migrate
Operations to perform:
Apply all migrations: auth, sites, account, admin, pinax_images, sessions, eventlog, contenttypes
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying account.0001_initial... OK
Applying account.0002_fix_str... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying pinax_images.0001_initial... OK
Applying cloudspotting.0001_initial... OK
Applying eventlog.0001_initial... OK
Applying eventlog.0002_auto_20150113_1450... OK
Applying eventlog.0003_auto_20160111_0208... OK
Applying sessions.0001_initial... OK
Applying sites.0001_initial... OK
Applying sites.0002_alter_domain_unique... OK
(cloudspotting) graham@Grahams-MacBook-Pro ~/venv/cloudspotting/src/cloudspotting
$
and then start the development server:
$ ./manage.py runserver
Success, of sorts. I am able to sign up and create a "CloudSpotting" instance, but quickly discover a problem.
The image collection detail view, which allows image addition and removal from our CloudSpotting, does not work. The Javascript code for this page came from pinax-images-panel
and
a quick check with the author reveals a build process incompatibility. Apparently I need to "add React support to the gulp build process", whatever that means. I reach out for help and move on to writing tests.
Adding Tests
All projects need tests and cloudspotting is no exception. I add simple smoke tests proving CloudSpotting works as expected. Invoking the tests I see:
$ ./manage.py test
Creating test database for alias 'default'...
EEEEE
======================================================================
ERROR: test_create (cloudspotting.tests.tests.TestViews)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/graham/venv/cloudspotting/src/cloudspotting/cloudspotting/tests/tests.py", line 10, in setUp
super(Tests, self).setUp()
NameError: name 'Tests' is not defined
<four more identical failures>
----------------------------------------------------------------------
Ran 5 tests in 0.002s
FAILED (errors=5)
Destroying test database for alias 'default'...
Oops, a typical cut-n-paste error, I did not specify the super()
inheritance properly in setUp()
. I fix the problem and run tests again.
$ ./manage.py test
Creating test database for alias 'default'...
.....
----------------------------------------------------------------------
Ran 5 tests in 0.387s
OK
Destroying test database for alias 'default'...
Much better!
All tests pass now, but the detail page still doesn't allow image addition or deletion. Pinax contributor Patrick Altman helps me with a thorough review.
He fixes my CloudSpotting model, changing a ForeignKey
field to a OneToOneField
and revising migration 0001_initial.py.
How did Patrick change the initial migration? I guess he updated the model definition, then deleted "cloudspotting/migrations/" directory, then recreated migrations for cloudspotting.
Patrick also realizes each new CloudSpotting instance needs an empty ImageSet, so he creates one in the CloudSpottingCreateView.form_valid
method.
His entire pull request includes a few other useful template updates and file changes/deletions, including updates for the newly-fixed pinax-images-panel
. Now the cloudspotting detail pages works great. I can add images, set an image as the "title" image for a collection, and delete images. Thanks Patrick!
I continue exercising the site, trying to find other problems, but everything works fine. On a whim I decide to verify tests are still passing... and see five failures! If the site works, why are tests failing?
I remember Patrick's previous CloudSpotting model change requires an ImageSet for each new CloudSpotting. The view tests manually create CloudSpotting instances for checking detail and list views, so the solution is simple: fix CloudSpotting creation in TestViews.setup()
. One small update later and all tests pass. Phase 1 complete!
Lessons Learned
- Building a project from scratch using Pinax starter projects is quite enjoyable. You get a solid pre-built framework as a base for your code in just a few seconds using the
pinax
command line tool. - Pinax starter project
account
could easily have been the base for cloudspotting. When comparing demo starter projectdocuments
with starter projectaccount
it turns out the difference is quite slim. Since I removed most of the document-related code during renovation, I may as well have started withaccount
. Nevertheless my path to success worked just fine. - Peer code review is wonderful! Ask a trusted developer to review your code and plan to return the favor another day. You'll be glad you did.
- Most importantly, using Pinax starter projects frees you from thinking about Django configuration and basic account handling. With the basics covered you are free to concentrate on your project goals. Pinax starter projects FTW!
Wrapping Up
You can view the current Pinax cloudspotting source code in the cloudspotting repository on GitHub.
If you have questions or comments about this post, the cloudspotting project, or Pinax in general, please join our Pinax Slack team and post your thoughts in the #cloudspotting or #general channels. The Pinax community is welcoming and helpful and it's a great place to get started. My Slack username is "grahamullrich". Feel free to ping me using "@grahamullrich" in any Pinax channel.
Inspiration for this project came from one of my favorite books, "The Cloudspotter's Guide" by Gavin Pretor-Pinney.
Many thanks to Eldarion for supporting this project, and to Anna Ossowski and Patrick Altman for their assistance.
Stay tuned for "Building Cloudspotting, Part 2" when I integrate pinax-likes
, pinax-announcements
, and pinax-testimonials
with cloudspotting. See you soon!