Plain is headed towards 1.0! Subscribe for development updates →

plain.models

Model your data and store it in a database.

# app/users/models.py
from plain import models
from plain.passwords.models import PasswordField


class User(models.Model):
    email = models.EmailField(unique=True)
    password = PasswordField()
    is_admin = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.email

Create, update, and delete instances of your models:

from .models import User


# Create a new user
user = User.objects.create(
    email="[email protected]",
    password="password",
)

# Update a user
user.email = "[email protected]"
user.save()

# Delete a user
user.delete()

# Query for users
admin_users = User.objects.filter(is_admin=True)

Installation

# app/settings.py
INSTALLED_PACKAGES = [
    ...
    "plain.models",
]

To connect to a database, you can provide a DATABASE_URL environment variable.

DATABASE_URL=postgresql://user:password@localhost:5432/dbname

Or you can manually define the DATABASES setting.

# app/settings.py
DATABASES = {
    "default": {
        "ENGINE": "plain.models.backends.postgresql",
        "NAME": "dbname",
        "USER": "user",
        "PASSWORD": "password",
        "HOST": "localhost",
        "PORT": "5432",
    }
}

Multiple backends are supported, including Postgres, MySQL, and SQLite.

Querying

Migrations

Migration docs

Fields

Field docs

Validation

Indexes and constraints

Managers

Forms

  1from plain.exceptions import ObjectDoesNotExist
  2
  3from . import (
  4    preflight,  # noqa
  5)
  6from .aggregates import *  # NOQA
  7from .aggregates import __all__ as aggregates_all
  8from .constraints import *  # NOQA
  9from .constraints import __all__ as constraints_all
 10from .db import (
 11    DEFAULT_DB_ALIAS,
 12    PLAIN_VERSION_PICKLE_KEY,
 13    DatabaseError,
 14    DataError,
 15    Error,
 16    IntegrityError,
 17    InterfaceError,
 18    InternalError,
 19    NotSupportedError,
 20    OperationalError,
 21    ProgrammingError,
 22    close_old_connections,
 23    connection,
 24    connections,
 25    reset_queries,
 26    router,
 27)
 28from .deletion import (
 29    CASCADE,
 30    DO_NOTHING,
 31    PROTECT,
 32    RESTRICT,
 33    SET,
 34    SET_DEFAULT,
 35    SET_NULL,
 36    ProtectedError,
 37    RestrictedError,
 38)
 39from .enums import *  # NOQA
 40from .enums import __all__ as enums_all
 41from .expressions import (
 42    Case,
 43    Exists,
 44    Expression,
 45    ExpressionList,
 46    ExpressionWrapper,
 47    F,
 48    Func,
 49    OrderBy,
 50    OuterRef,
 51    RowRange,
 52    Subquery,
 53    Value,
 54    ValueRange,
 55    When,
 56    Window,
 57    WindowFrame,
 58)
 59from .fields import *  # NOQA
 60from .fields import __all__ as fields_all
 61from .fields.json import JSONField
 62from .fields.proxy import OrderWrt
 63from .indexes import *  # NOQA
 64from .indexes import __all__ as indexes_all
 65from .lookups import Lookup, Transform
 66from .manager import Manager
 67from .query import Prefetch, QuerySet, prefetch_related_objects
 68from .query_utils import FilteredRelation, Q
 69from .registry import register_model
 70
 71# Imports that would create circular imports if sorted
 72from .base import DEFERRED, Model  # isort:skip
 73from .fields.related import (  # isort:skip
 74    ForeignKey,
 75    ForeignObject,
 76    OneToOneField,
 77    ManyToManyField,
 78    ForeignObjectRel,
 79    ManyToOneRel,
 80    ManyToManyRel,
 81    OneToOneRel,
 82)
 83
 84
 85__all__ = aggregates_all + constraints_all + enums_all + fields_all + indexes_all
 86__all__ += [
 87    "ObjectDoesNotExist",
 88    "CASCADE",
 89    "DO_NOTHING",
 90    "PROTECT",
 91    "RESTRICT",
 92    "SET",
 93    "SET_DEFAULT",
 94    "SET_NULL",
 95    "ProtectedError",
 96    "RestrictedError",
 97    "Case",
 98    "Exists",
 99    "Expression",
100    "ExpressionList",
101    "ExpressionWrapper",
102    "F",
103    "Func",
104    "OrderBy",
105    "OuterRef",
106    "RowRange",
107    "Subquery",
108    "Value",
109    "ValueRange",
110    "When",
111    "Window",
112    "WindowFrame",
113    "JSONField",
114    "OrderWrt",
115    "Lookup",
116    "Transform",
117    "Manager",
118    "Prefetch",
119    "Q",
120    "QuerySet",
121    "prefetch_related_objects",
122    "DEFERRED",
123    "Model",
124    "FilteredRelation",
125    "ForeignKey",
126    "ForeignObject",
127    "OneToOneField",
128    "ManyToManyField",
129    "ForeignObjectRel",
130    "ManyToOneRel",
131    "ManyToManyRel",
132    "OneToOneRel",
133]
134
135# DB-related exports
136__all__ += [
137    "connection",
138    "connections",
139    "router",
140    "reset_queries",
141    "close_old_connections",
142    "DatabaseError",
143    "IntegrityError",
144    "InternalError",
145    "ProgrammingError",
146    "DataError",
147    "NotSupportedError",
148    "Error",
149    "InterfaceError",
150    "OperationalError",
151    "DEFAULT_DB_ALIAS",
152    "PLAIN_VERSION_PICKLE_KEY",
153]
154
155# Registry exports
156__all__ += ["register_model"]