Skip to main content

Reusable Django Workflows

Project description

django-workflows

Reusable Django service helpers for common account flows:

  • Registration and verification-code email flow
  • Forgot-password code flow
  • Reset-code validation and lockout handling
  • Transport-neutral email utility with recipient validation

Installation

Install from PyPI:

pip install BmorricalDjangoWorkflows==1.2.3

In requirements.txt, pin it directly:

BmorricalDjangoWorkflows==1.2.3

Note: distribution name is BmorricalDjangoWorkflows, while imports remain under django_workflows.

Host Project Requirements

This package expects the host Django project to provide:

  • A Django user model available through django.contrib.auth.get_user_model()
  • A user meta model referenced by WORKFLOWS["USER_META_MODEL"]
  • A user manager create_user(...) method that accepts username, because this package explicitly sets username=email

The user meta model should include at least:

  • user relation
  • verify_code field
  • verify_time field
  • attempts field

Optional (used if present):

  • force_password_reset

Django Settings

Add a WORKFLOWS dict in your Django settings.

WORKFLOWS = {
	# Required in most projects unless your model path matches the default.
	"USER_META_MODEL": "users.models_user_meta.UserMeta",

	# Optional settings with defaults shown.
	"COMPANY_NAME": "My Company",
	"ENABLE_EMAIL_DELIVERY": False,
	"RESET_CODE_TTL_MINUTES": 10,
	"MAX_VERIFY_ATTEMPTS": 3,
}

Notes:

  • USER_META_MODEL must be a dotted import path such as myapp.models.UserMeta.
  • If ENABLE_EMAIL_DELIVERY is true, configure the email transport environment variables used by the mail service.

Email Delivery Environment Variables

When email delivery is enabled, set:

  • ENABLE_EMAIL_DELIVERY=true
  • EMAIL_DELIVERY_PROVIDER=mailgun|mailpit|smtp|disabled
  • EMAIL_FROM="My Company <no-reply@example.com>"

For the mailgun provider, also set:

  • EMAIL_API_KEY
  • EMAIL_DOMAIN

Optional:

  • BCC_RECIPIENTS as a comma-separated list
  • EMAIL_LOG_RESPONSE_TEXT=true to log response bodies at debug level

Example:

ENABLE_EMAIL_DELIVERY=true
EMAIL_DELIVERY_PROVIDER=mailgun
EMAIL_FROM="My Company <no-reply@example.com>"
EMAIL_DOMAIN="mg.example.com"
EMAIL_API_KEY="key-example"
BCC_RECIPIENTS="audit@example.com,ops@example.com"

If BCC_RECIPIENTS is not set, no static BCC recipients are added.

Old To New Env Mapping

If a consuming app previously used the Mailgun-specific names, update them as follows:

  • MAILGUN_COMPANY_NAME -> EMAIL_FROM
  • ENABLE_MAILGUN -> ENABLE_EMAIL_DELIVERY
  • MAILGUN_LOG_RESPONSE_TEXT -> EMAIL_LOG_RESPONSE_TEXT
  • MAILGUN_API_KEY -> EMAIL_API_KEY
  • MAILGUN_DOMAIN -> EMAIL_DOMAIN
  • MAILGUN_BCC_RECIPIENTS -> BCC_RECIPIENTS

Built-In Defaults

If a consuming app does not define one of these values in .env, the package now defaults to:

  • ENABLE_EMAIL_DELIVERY=False
  • EMAIL_API_KEY=""
  • EMAIL_DOMAIN=""
  • EMAIL_DELIVERY_PROVIDER=""
  • DEVELOPMENT_MODE=False
  • DEFAULT_FROM_EMAIL=""
  • BCC_RECIPIENTS=""
  • EMAIL_LOG_RESPONSE_TEXT=False

Additional behavior:

  • EMAIL_FROM falls back to DEFAULT_FROM_EMAIL when EMAIL_FROM is unset.
  • If EMAIL_DELIVERY_PROVIDER is blank, the package resolves the transport from the other flags:
  • If ENABLE_EMAIL_DELIVERY=true, it defaults to the mailgun transport.
  • If DEVELOPMENT_MODE=true, it defaults to the smtp transport.
  • Otherwise delivery remains disabled.

Practical implication:

  • Production apps using Mailgun should set ENABLE_EMAIL_DELIVERY, EMAIL_FROM, EMAIL_API_KEY, and EMAIL_DOMAIN.
  • Local apps using Mailpit should usually set EMAIL_DELIVERY_PROVIDER=mailpit, EMAIL_FROM, and the Django SMTP settings shown below.
  • Apps that do not want this package to send email can omit everything and leave delivery disabled.

Django Email Backend Settings

If a consuming app uses EMAIL_DELIVERY_PROVIDER=smtp or EMAIL_DELIVERY_PROVIDER=mailpit, it should configure Django's email backend in settings.py.

import os

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL")
EMAIL_HOST = os.getenv("EMAIL_HOST", "localhost")
EMAIL_PORT = int(os.getenv("EMAIL_PORT", "25"))
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "")
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "")
EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", "False") == "True"
EMAIL_USE_SSL = os.getenv("EMAIL_USE_SSL", "False") == "True"

Notes:

  • This is only needed for the smtp and mailpit providers.
  • mailpit commonly listens on port 1025, so set EMAIL_PORT accordingly in local development.
  • If you set EMAIL_FROM, that value is preferred by this package. Otherwise it falls back to DEFAULT_FROM_EMAIL.

Example Usage

from django_workflows.users.services.auth_flow import (
	register_user_and_send_verification_email,
	send_forgot_password_code,
	verify_reset_code,
	change_password,
)

result = register_user_and_send_verification_email(
	email="person@example.com",
	first_name="First",
	last_name="Last",
	password="example-password",
)

forgot = send_forgot_password_code("person@example.com")

verify = verify_reset_code(email="person@example.com", code="AB12CD")

changed = change_password(
	email="person@example.com",
	password="new-password",
	password_verify="new-password",
)

Direct Email Service Usage

Attachment tuple shape:

  • (filename, file_bytes_or_file_object, mimetype)

Example attachment entry:

  • ("report.pdf", pdf_bytes, "application/pdf")
from django_workflows.services.email import send_email

response = send_email(
	to=["primary@example.com", "secondary@example.com"],
	subject="Welcome",
	html="<p>Thanks for joining.</p>",
	cc="manager@example.com",
	bcc=["audit@example.com"],
	attachments=[("report.pdf", pdf_bytes, "application/pdf")],
)

# Optional: explicit runtime override for enablement
send_email(
	to="user@example.com",
	subject="Dry run",
	html="<p>This will not send.</p>",
	enabled=False,
)

Local Development

Run tests:

make test

Run tests with coverage:

make test-coverage

CI runs tests on push and pull requests using .github/workflows/tests.yml.

Troubleshooting

ImproperlyConfigured for USER_META_MODEL

Error example:

WORKFLOWS['USER_META_MODEL'] must be a dotted path like 'myapp.models.UserMeta'.

Fix:

  • Set WORKFLOWS["USER_META_MODEL"] to a valid dotted import path.
  • Verify the target model is importable by Django at runtime.

UserMeta field errors

If you see attribute errors around verification state, confirm your user meta model provides:

  • verify_code
  • verify_time
  • attempts

Email delivery not sending

Check:

  • ENABLE_EMAIL_DELIVERY=true
  • EMAIL_DELIVERY_PROVIDER
  • EMAIL_FROM or DEFAULT_FROM_EMAIL
  • EMAIL_API_KEY and EMAIL_DOMAIN when using the Mailgun provider

No static BCC recipients applied

This is expected unless you set BCC_RECIPIENTS.

Release

Create a release tag:

./release.sh 0.1.0

After release, update the package version in your consuming application's dependency files.

Tag pushes like v1.2.3 trigger .github/workflows/publish.yml, which publishes to PyPI.

Publishing To PyPI (Maintainer Steps)

  1. Check whether the target package name is available.
curl -I https://pypi.org/pypi/BmorricalDjangoWorkflows/json

If this returns 404, the name is usually available.

  1. Create a PyPI account: https://pypi.org/account/register/

  2. Create a PyPI API token: PyPI Account -> Account settings -> API tokens.

  3. Add repository secret in GitHub:

  • Name: PYPI_API_TOKEN
  • Value: your PyPI token (starts with pypi-)
  1. Bump and tag a release:
./release.sh 1.2.3
  1. Confirm the workflow run succeeded and the package appears on PyPI.

  2. Update consuming applications:

BmorricalDjangoWorkflows==1.2.3

If upload fails because the name is already taken, change name= in setup.py, bump version, and release again.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bmorricaldjangoworkflows-2.0.3.tar.gz (14.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bmorricaldjangoworkflows-2.0.3-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file bmorricaldjangoworkflows-2.0.3.tar.gz.

File metadata

  • Download URL: bmorricaldjangoworkflows-2.0.3.tar.gz
  • Upload date:
  • Size: 14.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for bmorricaldjangoworkflows-2.0.3.tar.gz
Algorithm Hash digest
SHA256 1bc77940ec1247c8a57a5607283ed86eadd84f284f0d8e0df644f10abe327ee4
MD5 bab9790c98e93efb726ebc812f3e00e0
BLAKE2b-256 e4c530034c0e32eaf8af3d06d1500a69798dd10458b23445917131b11f73df9c

See more details on using hashes here.

File details

Details for the file bmorricaldjangoworkflows-2.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for bmorricaldjangoworkflows-2.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 a8e320ce39fde3c241d4ef41f2b6c1025c04b6dc7a00fc6ef62b8cd5cdf07e3b
MD5 05a9dd1185d7f9ef845645922a0f0368
BLAKE2b-256 4eca1931191c0a2f953ca4a9de26a74934bc1888222e007a3f11268689f5be75

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page