Namespaces are one honking great idea -- let’s do more of those!
—The Zen of Python
In the previous several chapters, we walked through how to add several features to your app, including HTML templates and static files, which are organized and accessed by using app namespacing.
In this chapter, we’ll learn how namespacing decisions pervade the rest of your standalone app and how to take advantage of namespacing to make integration and use of your app easier.
Namespaces at a glance
Namespaces are a way of organizing named objects – in Python and beyond – such that they have both a parent identifier and, more importantly, their names don’t conflict. We’ve addressed namespaces a few times already, primarily in the context of template and static file directories, but their use extends well beyond configuring directories for collecting files.
Two different modules can each define a function with the same name as the other, such that my_module.does_a_thing and your_module.does_a_thing don’t conflict.
Two different classes can each define a method with the same name as the other, such that MyClass.does_a_thing doesn’t conflict with YourClass.does_a_thing.
Two different dictionaries can each contain the same key mapped to different values, such that my_dict[“key”] = 4 while your_dict[“key”] = 1.
The use of varied namespaces is easy to take for granted in your own project, but when introducing code into varied Django projects, you should take the time to ensure it’s sensibly namespaced.
App itself
Our entrypoint to namespacing a standalone Django app is the app itself, more specifically, its module name and how it’s named in its AppConfig. This is the easiest but also most important step. In order to avoid name collisions in Django project codebases, and, more importantly, to make distinctions obvious, the app name should be descriptive and not obviously overlap known existing app names, either those that ship with Django or are published for shared use.
If, for example, you built a standalone app for interfacing with the Stripe billing service, you might be tempted to name it stripe. But if you did this, you’d run into conflicts with the authoritative Stripe Python SDK. Instead, you might decide to name it something related to Django, like django_stripe or djstripe, except now the latter at least conflicts with an existing published standalone app! If your app is a substitute for the functionality of djstripe, then they’re unlikely to be used in the same project and thus collide; however, unless your app is a fork of djstripe, it’s likely to cause confusion for developers working with the app. In this case choose a different name.
When the descriptive name for an app is unavailable or ill-advised because of a conflict, choosing an adapted name with extra context, like stripe_billing, or using a synonym or allusion, works too, like zebra. The zebra app is a now unmaintained app for integrating Stripe payments in Django projects, so named because zebras have stripes.
URLs
There are multiple ways of adding namespaces to URLs to make identifying them in a project obvious and to avoid naming conflicts. In the event of a naming conflict, the first matching named URL will be used. This can be confusing especially if no exception is raised.
It’s entirely possible to use the base URL name itself to establish the namespace. There’s little fundamental difference between myapp_list and myapp:list. The latter is clearer about where the namespace “breaks,” but both ensure that a list-related view is unique to the myapp name.
Settings
If your standalone app allows configuration via django.conf.settings, then these will also need to be consistently namespaced. What may work locally in your own Django project is not guaranteed to work across every other project.
For more on structuring settings and handling defaults within apps, see Chapter 7.
Management commands
Django’s management commands function as command line–based interfaces to Django projects. A good way to think about them is views but for terminal processing instead of HTTP requests. There are many reasons for a standalone app to include management commands: syncing data, importing and exporting data, or providing a way to create default data.
Unlike URL names, however, management command names are global. If you want to include a management command that will migrate user data across systems, using the name migrate would conflict with the Django ORM’s migrate command, overriding the base migrate command causing no small amount of heartburn.
- 1.
Prefacing the command name (module name) with an application identifier
- 2.
Making each command name as descriptive and unique as possible
Using the application name as a namespace for management commands is not a common practice, but this doesn’t mean it’s not a good one. This is a good strategy if your app has several management commands or if your app has a simple name. Both django-jet and dj-stripe follow this practice, prefacing management commands with jet_ and dj_stripe, respectively. In the case of dj-stripe, this means that a command to sync_models is clearly defined as being related to dj-stripe, instead of being a globally ambiguous command.
In the case of management commands, making the command names explicit even without a name prefix is often sufficient. This can involve including the app name elsewhere or referencing something unique to the app, like a class of data or service. django-cachalot provides the invalidate_cachalot management command, which is both an obviously app-specific name and also clear in what it does. django-reversions provides createinitialrevisions and deleterevisions.
Absent a URL-like namespacing scheme for management commands, which strategy you choose will be context dependent.
Template tags
There’s no limit on how many modules you put in the templatetags package. Just keep in mind that a {% load %} statement will load tags/filters for the given Python module name, not the name of the app.
—Django docs
- 1.
The tag library names are global, that is, not namespaces with regard to the app.
- 2.
Individual tags and filters are similarly loaded into a single namesapce, though only in the context of a template that loads the library.
The implication is that you should aim to use name prefixing to keep your template tag modules uniquely named and name individual tags and filters such that they’re implicitly app namespaced, at least where they provide some kind of app-specific functionality. If the tags or filters are intended to be used or useful beyond the context of data in your app, then it may make sense to name them more generally.
Models and database tables
Database table naming is largely an afterthought in developing Django projects since the ORM generates default table names. While there’s something to be said about creating descriptive and human friendly table names, the primary concern for a reusable app is simply that the app preface in the table name can be expected to be unique.
Let’s say you have an app that provides a kind of user-facing logging, and you name the app logs; it’s not a very good name for a standalone app, but let’s take it as a given. The database table names will be prefaced with logs_, for example, for a model named LogEntry, the table name would be logs_logentry.
Now, anyone writing SQL queries or inspecting the database will have a much clearer understanding of what the table represents and what kind of data it contains.