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
- 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
account and provide enhanced functionality from Pinax apps
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
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!
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.
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.
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
pinax-images documentation reveals a front-end development shortcut. I install
pinax-images and managing image collections.
My npm invocation includes the
--save option to add this requirement to the cloudspotting
npm install pinax-images-panel --save
Browsing the source code I find and remove unneeded "documents" references, replacing with "images" as required.
Most of the unneeded code is gone, so I create the primary cloudspotting model,
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
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
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.
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.
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.
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'...
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
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!
- 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
pinaxcommand line tool.
- Pinax starter project
accountcould easily have been the base for cloudspotting. When comparing demo starter project
documentswith starter project
accountit turns out the difference is quite slim. Since I removed most of the document-related code during renovation, I may as well have started with
account. 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!
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-testimonials with cloudspotting. See you soon!