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()
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
Fields
Validation
Indexes and constraints
Managers
Forms
1import functools
2from collections import namedtuple
3
4
5def make_model_tuple(model):
6 """
7 Take a model or a string of the form "package_label.ModelName" and return a
8 corresponding ("package_label", "modelname") tuple. If a tuple is passed in,
9 assume it's a valid model tuple already and return it unchanged.
10 """
11 try:
12 if isinstance(model, tuple):
13 model_tuple = model
14 elif isinstance(model, str):
15 package_label, model_name = model.split(".")
16 model_tuple = package_label, model_name.lower()
17 else:
18 model_tuple = model._meta.package_label, model._meta.model_name
19 assert len(model_tuple) == 2
20 return model_tuple
21 except (ValueError, AssertionError):
22 raise ValueError(
23 f"Invalid model reference '{model}'. String model references "
24 "must be of the form 'package_label.ModelName'."
25 )
26
27
28def resolve_callables(mapping):
29 """
30 Generate key/value pairs for the given mapping where the values are
31 evaluated if they're callable.
32 """
33 for k, v in mapping.items():
34 yield k, v() if callable(v) else v
35
36
37def unpickle_named_row(names, values):
38 return create_namedtuple_class(*names)(*values)
39
40
41@functools.lru_cache
42def create_namedtuple_class(*names):
43 # Cache type() with @lru_cache since it's too slow to be called for every
44 # QuerySet evaluation.
45 def __reduce__(self):
46 return unpickle_named_row, (names, tuple(self))
47
48 return type(
49 "Row",
50 (namedtuple("Row", names),),
51 {"__reduce__": __reduce__, "__slots__": ()},
52 )