© Ben Lopatin 2020
B. LopatinDjango Standalone Appshttps://doi.org/10.1007/978-1-4842-5632-9_7

7. Namespacing in your app

Ben Lopatin1 
(1)
New York, NY, USA
 

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.

With namespacing
  • 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 example, for an app called organizations that manages accounts with multiple users, you might have several settings that regulate the user model to use, the number of members allowed per group, and whether admin users can invite new members:
GROUP_USER_MODEL = AUTH_USER_MODEL
GROUPS_LIMIT = 8
ADMINS_CAN_INVITE = True
While confusing enough in the original Django project, the scope of impact is quite limited. Transferring this motley set of names across projects in a reusable app scales out the problem however. So instead, ensure that each setting as named in the project settings has a consistent preface, for example:
ORGANIZATIONS_USER_MODEL = AUTH_USER_MODEL
ORGANIZATIONS_USER_LIMIT = 8
ORGANIZATIONS_ADMINS_CAN_INVITE = True

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.

For a quick review, management command names come from the module (file) name. A module within management/commands in an app that defines a subclass of BaseCommand simply named Command will be treated as a named management command by Django.
myapp/
        __init__.py
        management/
                __init__.py
                commands/
                        __init__.py
                        migrate_user_data.py

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.

Two solutions present themselves for both avoiding name conflicts and making clear the purpose of the command:
  1. 1.

    Prefacing the command name (module name) with an application identifier

     
  2. 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

Template tags – and filters – add both logical and formatting functionality to templates at render time. Adding new template tags is as simple as including a templatetags module in your app and then one or more tag libraries as submodules.
myapp/
        templatetags/
                __init__.py
                myapp_tags.py
Template tags and filters present two namespacing challenges:
  1. 1.

    The tag library names are global, that is, not namespaces with regard to the app.

     
  2. 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

App models and their respective database table names have default namespaces thanks to the application name itself. A project could have fifteen different apps each with its own model class named User and this would not pose any special conflict, so long as they’re imported with aliases where they might come into conflict.
from app1.models import User as UserApp1
from app2.models import User as UserApp2
from app3.models import User as UserApp3
Nonetheless, import conflicts are not the only issues we seek to avoid with naming but also descriptiveness. Where a model both serves in a very app-specific way (i.e., it would be difficult to construe how it could be used outside of the app) and it is expected to be used with other models outside of the app, then app-specific naming should be used.
class MyAppUser(models.User):
        """A user model specific to the myapp app"""

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.

If you’re writing any SQL outside of the Django application, the table name lacks the context that the model class does in the scope of the source code. So in this example, if you absolutely had to maintain the app name logs, then it would be wise to specify db_table values in your model Meta classes:
class LogEntry(models.Model):
        ...
        class Meta:
                db_table = "activitylogs_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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.56.114