/example-django

An example repo to demonstrate Django support in Pants v2

Primary LanguagePythonApache License 2.0Apache-2.0

example-django

An example repository to demonstrate Django support in Pants.

The demo consists of a set of services that implement an elaborate "Hello, World" site. This repo shows how Pants can support multiple Django services in a single repo, with minimal copypasta.

The layout in this repos enforces a strict distinction between Django apps (reusable units of functionality such as models and related views), vs. services ("projects" in Django parlance) - deployable services composed of multiple apps and typically attached to one or more database instances.

See pantsbuild.org for much more detailed documentation about Pants.

See here for an example of basic Python support in Pants.

Architecture

We utilize a flexible "database-first" architecture: Since migrating production databases is significantly harder and riskier than modifying stateless services, our architecture treats the databases as standalone logical entities not subservient to any app or service. "Code is for now, data is forever."

  • The models in each app map to one of the logical databases, via a custom database router.
  • The settings for each server map the logical databases required by its apps to physical databases.

If it makes sense to do so, different services can reuse the same app in different databases, by pointing the same logical database at different physical databases in each service.

The only constraint is that if a model in one app reference a model in another app via foreign key then both apps must be mapped to the same logical db. You can see an example of this in helloworld/translate/models.py.

This architecture allows services to evolve in a flexible way as more apps and more functionality are developed.

Databases

In order to demonstrate multi-db configurations, there are two logical databases:

  • users: User-related data.
  • greetings: Greeting-related data.

Currently the services map these logical databases to sqlite dbs. We plan to add examples of using PostgreSQL.

Services

There are four distinct services, defined under helloworld/services:

  • helloworld.services.admin: Runs the Django admin UI.
  • helloworld.services.frontend: Serves end user requests.
  • helloworld.services.user: A backend service that provides user data.
  • helloworld.services.welcome: A backend service that generates greetings.

Each service has its own manage.py, gunicorn.py, urls.py and settings.py. The settings.py inherits most of its settings from helloworld/settings_base.py and adds anything specific to that service.

Note that we make no argument for or against microservices vs. monolithic services as a deployment architecture. We do make an argument in favor of a monorepo over multiple repos as a codebase architecture, and this repo demonstrates the utility of a monorepo, especially if you deploy multiple services.

Apps

The services are composed of four Django apps:

  • helloworld.greet: Functionality related to selecting a greeting.
  • helloworld.person: Functionality related to identifying the person to greet.
  • helloworld.translate: Functionality related to translating greetings into various languages.
  • helloworld.ui: Functionality related to rendering greetings to the end user.

To view the frontend:

In three terminals run:

  • ./pants run helloworld/service/frontend/manage.py -- runserver
  • ./pants run helloworld/service/user/manage.py -- runserver
  • ./pants run helloworld/service/welcome/manage.py -- runserver

And visit this URL in a browser: http://127.0.0.1:8000/?person=sherlock&lang=es .

Useful Pants commands

To run all tests:

./pants test ::

To run formatters and linters:

./pants fmt lint validate ::

To run typechecking:

./pants typecheck ::

To build deployable gunicorn .pex files for all services:

./pants package ::

manage.py

To run management commands for a service, use that service's manage.py, e.g.,

./pants run helloworld/service/admin/manage.py -- runserver

Note that for runserver, each dev server will run on its own port, see DEV_PORTS in helloworld/util/discovery.py.

Also, with runserver we turn off Django's autoreloader, since we rely on Pants's own file-watching instead, by setting restartable=True on the pex_binary targets for manage.py. Pants will correctly restart servers in situations where Django cannot, such as changes to BUILD files, to .proto files, or to 3rdparty dependencies.

To run migrations, it's best to use the admin service's manage.py, as it has access to all apps:

./pants run helloworld/service/admin/manage.py -- migrate --database=users --database=greetings