Skip to content

Contributing

Thank you for considering a contribution to django-rls-tenants. This guide covers everything you need to get started.

Code of Conduct

This project and everyone participating in it is governed by the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to dvoraj75@gmail.com.

Prerequisites

  • Python 3.11+
  • PostgreSQL 15+
  • uv (recommended) or pip

Development Setup

# Clone the repository
git clone https://github.com/dvoraj75/django-rls-tenants.git
cd django-rls-tenants

# Install all dependencies (dev + test + docs)
uv sync --group dev --group test --group docs

# Install pre-commit hooks
uv run pre-commit install

# Create a test database
createdb django_rls_tenants_test

# Run the test suite
uv run pytest

Code Style

This project uses Ruff for linting and formatting, and mypy for type checking. All configuration lives in pyproject.toml.

# Lint
uv run ruff check .

# Lint with auto-fix
uv run ruff check --fix .

# Format
uv run ruff format .

# Type check (production code only)
uv run mypy django_rls_tenants

Pre-commit hooks run Ruff and mypy automatically on every commit.

Key Style Rules

  • Line length: 99 characters.
  • Every .py file starts with a module docstring followed by from __future__ import annotations.
  • Absolute imports only -- no relative imports.
  • Google-style docstrings with Args:, Returns:, Raises: sections.
  • Modern type syntax: str | None (not Optional[str]), list[str] (not List[str]).
  • Always use specific # noqa: XXXX codes with an explanatory comment.

Testing

Tests require a running PostgreSQL instance. Configure the connection via environment variables or edit tests/settings.py.

# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov

# Run a specific test file
uv run pytest tests/test_rls/test_guc.py

# Run a single test
uv run pytest tests/test_rls/test_guc.py::test_set_get_roundtrip

# Run only integration tests
uv run pytest -m integration

# Keyword filter
uv run pytest -k "guc and not local"

Test Organization

tests/
├── conftest.py              # Shared fixtures
├── settings.py              # Django settings for test suite
├── test_app/                # Test-only Django app (models, migrations)
├── test_rls/                # rls/ layer unit tests
├── test_tenants/            # tenants/ layer unit tests
├── test_integration/        # End-to-end tests (@pytest.mark.integration)
└── test_layering.py         # Import boundary enforcement

Test Conventions

  • pytest-style: plain assert statements, pytest.raises() for exceptions.
  • File naming mirrors source: django_rls_tenants/rls/guc.py maps to tests/test_rls/test_guc.py.
  • Function naming: test_{what} or test_{action}_{expected_behavior}.
  • Module docstring: """Tests for django_rls_tenants.{module.path}."""
  • Fixtures: descriptive nouns (tenant_a, admin_user).
  • DB access: pytestmark = pytest.mark.django_db at module level.

Architecture

The library has two internal layers with a strict import boundary:

  • rls/ -- Generic PostgreSQL RLS primitives. Zero imports from tenants/.
  • tenants/ -- Django multitenancy built on rls/.

This boundary is enforced by tests/test_layering.py. Do not introduce imports from tenants/ into rls/.

Pull Request Process

  1. Fork the repository and create a branch from develop.
  2. Add or update tests for your changes.
  3. Ensure the full test suite passes: uv run pytest.
  4. Ensure linting and type checks pass: uv run ruff check . && uv run mypy django_rls_tenants.
  5. Update the [Unreleased] section in CHANGELOG.md.
  6. Open a pull request against develop with a clear description of the change.

Commit Messages

Write concise commit messages that explain why, not just what. Use imperative mood (e.g., "Add bypass flag for pre-auth requests" not "Added bypass flag...").

Reporting Bugs

Use the bug report template on GitHub Issues.

Requesting Features

Use the feature request template on GitHub Issues.