Skip to main content

opinionated-mixins

CI PyPI - Version PyPI - Python Version License Latest Commit

Downloads Downloads/Month Downloads/Week

Opinionated, consensus-driven model mixins — timestamps, feedback, announcements — consistent across Python storage frameworks (ORMs & ODMs).

Why?

Most projects need timestamps, soft-delete, announcements, feedback forms — and each time, you re-invent the field names, the enum values, the defaults. Should it be created_at or date_created? Is the status field called state or status? Is "resolved" a valid status or should it be "closed"?

opinionated-mixins makes these decisions for you, based on what the industry already agreed on.

Think of it like:

  • Mobbin — instead of designing UI patterns from scratch, you reference what top apps already do
  • shadcn/ui — copy well-designed components into your project and customize from there
  • Bootstrap — standardized building blocks so teams stop bikeshedding

Same idea, applied to data models. Use the standard way. Change it when you need it.

How It Works

Every field name, enum value, and default is chosen by researching what popular platforms and frameworks do (GitHub, Zendesk, JIRA, Django packages, etc.), then weighing them with the consensus criteria. Proposals require at least 3 independent real-world references.

The same mixin is implemented across all supported frameworks with identical field names and behavior, adapted to each framework's idioms.

These adjacent projects helped shape the way this library thinks about shared conventions, schemas, and reusable model contracts:

  • awesome-json-schema — a curated collection of JSON Schema resources, tools, and examples
  • OpenDTO Specification — an open specification for standardized DTO design and interoperability
  • Schema.org — a large, established shared vocabulary project for structured data

Example

from opinionated_mixins.contrib.sqlalchemy import Announcement


class MyAnnouncement(Base, Announcement):
    __tablename__ = "announcements"
    id = Column(Integer, primary_key=True)
    # Gets: title (str), content (text), category (enum)

Switch to MongoDB? Same fields, same names:

from opinionated_mixins.contrib.mongoengine import Announcement


class MyAnnouncement(Document, Announcement):
    pass
    # Same: title, content, category — with MongoEngine field types

Supported Frameworks

Framework Type Use Case
SQLAlchemy SQL ORM Declarative model mixins
SQLModel SQL ORM (Pydantic + SA) Re-exports SQLAlchemy mixins
MongoEngine MongoDB ODM Document field mixins
ODMantic MongoDB async ODM Model field mixins

Available Mixins

Mixin Fields Shared Enums
Announcement title, content, category AnnouncementCategory (8 values)
Feedback subject, content, category, status FeedbackCategory (4), FeedbackStatus (4)
Lead title, salutation, job_title, company_name, website, linkedin_url, status, source, industry, rating, opportunity_amount, currency, probability, close_date, last_contacted, next_follow_up, description, is_active LeadStatus (5), LeadSource (7), LeadRating (3)
Person first_name, last_name, middle_name, phone_number, email, street_address, postal_code, city, country, date_of_birth, bio —
Template name, content, format, type TemplateFormat (3), TemplateType (4)
User username, hashed_password, email, date_email_verified —

See open proposals for upcoming mixins.

Installation

pip install opinionated-mixins

Zero runtime dependencies. Framework packages (SQLAlchemy, MongoEngine, etc.) are your responsibility — you already have them in your project.

Architecture

opinionated-mixins provides reusable fields for Python storage frameworks. Consumers compose plain mixin classes with their own ORM or ODM base classes.

Framework implementations

  • SQLAlchemy supplies declarative mixins with columns.
  • SQLModel re-exports the SQLAlchemy implementations.
  • MongoEngine supplies document field mixins.
  • ODMantic supplies annotated field definitions through a small compatibility metaclass so plain mixin fields become ODMantic model fields when composed with odmantic.Model.

Layout

src/opinionated_mixins/contrib/<framework>/ contains one file per mixin. Each framework's __init__.py exports its mixins. Shared enums live in src/opinionated_mixins/enums.py, and tests mirror the framework layout.

Available mixins cover people, users, announcements, feedback, leads, templates, activity, notifications, timestamps, identifiers, and active flags. Field names and defaults follow the contracts recorded in the RFC index.

Design and verification

Mixins remain plain classes so consumers choose the framework base and can combine several mixins. Tests check field definitions, consistency across frameworks, and persistence using SQLite or MongoDB mocks. Optional MongoDB integration tests use testcontainers.

The package has no runtime dependencies. Consumers install their framework; contributors install development, test, and style tools with uv sync.

New mixins, fields, framework support, and breaking changes require an accepted RFC. Documentation corrections and examples do not require one.

Development

# Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install all dependencies
uv sync

# Run tests
uv run pytest tests

# Run tests with coverage
uv run coverage run -m pytest tests
uv run coverage report

# Lint (check / auto-fix)
uv run ruff check .
uv run ruff check --fix .

# Format (check / auto-fix)
uv run ruff format --check .
uv run ruff format .

# Type checking
uv run mypy --install-types --non-interactive src/opinionated_mixins

Roadmap

The RFC index records accepted designs and the status of proposals. Open issues track implementation work.

Priorities

  • Expand ODMantic examples now that plain mixin composition is supported.
  • Expand working examples and document framework-specific limitations.
  • Extend integration coverage for storage behavior and mixin composition.

The library already includes timestamp, active-flag, integer-ID, and UUID-ID mixins. CI runs linting, formatting, type checking, and tests on Python 3.10 and 3.14.

Additional mixins, fields, or storage frameworks require research and an accepted RFC. See CONTRIBUTING.md to propose work.

Proposed additional mixins

The earlier design discussion proposed the following mixins. These are candidates for RFCs; the remaining field names and contracts are provisional.

Issue #115 tracks the partial and unimplemented proposals below, along with framework coverage candidates.

Proposal Current coverage Remaining work
Timestamp CreatedAt and UpdatedAt provide created_at and updated_at. Implemented as separate composable mixins.
UUID UUIDID provides UUID primary keys for SQLAlchemy and SQLModel. Implemented for SQL storage; MongoDB models use their native identifiers.
SoftDelete IsActive provides an active flag. A dedicated deleted_at / deleted_by contract.
Audit Activity records events and their actors. Record-level created_by / updated_by fields.
Address Person includes street address, city, postal code, and country. A dedicated address mixin, including additional address lines and state/province.
Contact Person includes email and phone number; Lead includes website and LinkedIn URL. A dedicated contact mixin and a decision on mobile/fax fields.
Status Feedback and Lead have domain-specific status enums. A general status mixin with change reason, time, and actor.
Slug No dedicated implementation; design in RFC-0014. Implement the slug field across adapters per the accepted RFC.
Metadata Activity and Notification include JSON data. A general metadata/tags contract.
Version No dedicated implementation. Version, latest-version flag, and parent reference.
Priority No dedicated implementation. Priority and ordering fields.
Expiration No dedicated implementation. Expiration time and computed expiry behavior.

Framework considerations

Each accepted mixin should provide consistent field names and behavior across applicable supported frameworks, using the framework's native field types, constraints, and validation.

  • SQLAlchemy: supported; declarative columns, types, and constraints.
  • MongoEngine: supported; document fields and validation.
  • ODMantic: supported annotated field definitions with plain mixin composition.
  • SQLModel: supported through re-exports of SQLAlchemy implementations.
  • TortoiseORM: proposed expansion; native ORM field definitions and persistence tests.
  • Beanie: proposed expansion; document fields compatible with Pydantic validation, with the consumer choosing the document base.
  • Pydantic: proposed input-layer expansion; annotated fields and validation, with the consumer choosing the model base.
  • Dataclasses: proposed expansion; field annotations and defaults.
  • WTForms: proposed input-layer expansion; form fields and validators.

The last five frameworks are roadmap candidates, not current support. Adding them requires accepted framework RFCs; Pydantic, dataclasses, and WTForms also require revisiting the current storage-only scope.

Frequently asked questions

Which frameworks are supported?

SQLAlchemy, SQLModel, MongoEngine, and ODMantic. These are storage frameworks; input validation libraries and form frameworks are outside the current scope. See Architecture for framework limitations.

How do I install the package?

pip install opinionated-mixins

Install your chosen framework separately. The base package has no runtime dependencies and requires Python 3.10 or newer.

How do I use a mixin?

Compose it with your application's framework base:

from opinionated_mixins.contrib.sqlalchemy import Person
from sqlalchemy import Column, Integer
from sqlalchemy.orm import DeclarativeBase


class Base(DeclarativeBase):
    pass


class Contact(Person, Base):
    __tablename__ = "contacts"
    id = Column(Integer, primary_key=True)

Contact receives the Person fields, including first_name, last_name, phone_number, street_address, and date_of_birth. You can override columns on the concrete model.

Can ODMantic models inherit fields from these mixins?

Yes. ODMantic mixins use a compatibility metaclass that copies mixin annotations into the concrete model namespace before ODMantic builds the model. The Starlette example still declares fields directly where it intentionally uses datetime.datetime for BSON date storage.

Mixins also compose with odmantic.EmbeddedModel for embedded documents.

How do I query ODMantic date-only fields?

ODMantic does not serialize query operands. The Person.date_of_birth and Lead date fields are stored as midnight BSON datetimes, so queries must use that same representation:

import datetime

dob = datetime.date(1990, 1, 15)
operand = datetime.datetime.combine(dob, datetime.time())
person = await engine.find_one(MyPerson, MyPerson.date_of_birth == operand)

Use BSON-native operands with comparison methods, membership queries, and standalone odmantic.query helpers. This also applies to decimal amounts: use bson.Decimal128 when querying Lead.opportunity_amount. Ordinary Pydantic dumps retain date and decimal values.

How do I run the checks?

uv sync
uv run pytest tests
uv run ruff check .
uv run ruff format --check .
uv run mypy src/opinionated_mixins

How do I propose a change or report a bug?

See CONTRIBUTING.md. New mixins, fields, framework support, and breaking changes follow the RFC process. For bugs, use the bug report template and include a minimal reproduction.

Contributing

New mixin ideas? Open a Model Proposal. New fields on existing mixins? Open a Field Proposal.

Both require independent real-world references (see the consensus criteria) — this project runs on consensus, not opinion.

License

opinionated-mixins is distributed under the terms of the MIT license.

Metadata

Release files for opinionated-mixins 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for opinionated-mixins 0.1.0
File Size Uploaded
opinionated_mixins-0.1.0.tar.gz 60.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for opinionated-mixins 0.1.0
File Interpreter ABI Platform
opinionated_mixins-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 102.7 kB

Release files / opinionated_mixins-0.1.0.tar.gz

Download URL opinionated_mixins-0.1.0.tar.gz
Size 60.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a3f203571dd4438c34182077224b58708d63c60c78da12f91f0805f0320f6381
BLAKE2b-256 checksum
How to use checksums
b842dd46212b8be532c25661fee89f11276476c88d32778731dc4f8c051bd06a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / opinionated_mixins-0.1.0-py3-none-any.whl

Download URL opinionated_mixins-0.1.0-py3-none-any.whl
Size 42.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ae08dd5c1d299fc24806c5af235e2aa00501e6cb05312ee55a7d27aa7357b2a
BLAKE2b-256 checksum
How to use checksums
d2d0cd04fe776bffb70a6c95b8981556e20966b867c820f5a1a673f090bcb33c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page