canaille-globuzma/canaille/backends/models.py

114 lines
3.4 KiB
Python
Raw Normal View History

import datetime
from collections import ChainMap
from typing import Optional
from canaille.app import classproperty
2023-08-17 13:55:41 +00:00
class Model:
2024-03-29 08:35:29 +00:00
"""The model abstract class.
2023-08-17 13:55:41 +00:00
2024-03-29 08:35:29 +00:00
It details all the methods and attributes that are expected to be
implemented for every model and for every backend.
"""
2024-03-29 08:35:29 +00:00
created: Optional[datetime.datetime]
"""The :class:`~datetime.datetime` that the resource was added to the
service provider."""
last_modified: Optional[datetime.datetime]
2024-03-29 08:35:29 +00:00
"""The most recent :class:`~datetime.datetime` that the details of this
resource were updated at the service provider.
If this resource has never been modified since its initial creation,
2024-03-29 08:35:29 +00:00
the value MUST be the same as the value of :attr:`~canaille.backends.models.Model.created`.
"""
@classproperty
def attributes(cls):
return ChainMap(
*(c.__annotations__ for c in cls.__mro__ if "__annotations__" in c.__dict__)
)
2023-08-17 13:55:41 +00:00
@classmethod
def query(cls, **kwargs):
"""
Performs a query on the database and return a collection of instances.
Parameters can be any valid attribute with the expected value:
>>> User.query(first_name="George")
If several arguments are passed, the methods only returns the model
instances that return matches all the argument values:
>>> User.query(first_name="George", last_name="Abitbol")
If the argument value is a collection, the methods will return the
models that matches any of the values:
>>> User.query(first_name=["George", "Jane"])
"""
raise NotImplementedError()
@classmethod
def fuzzy(cls, query, attributes=None, **kwargs):
2024-03-29 08:35:29 +00:00
"""Works like :meth:`~canaille.backends.models.Model.query` but
attribute values loosely be matched."""
2023-08-17 13:55:41 +00:00
raise NotImplementedError()
@classmethod
def get(cls, identifier=None, **kwargs):
2024-03-29 08:35:29 +00:00
"""Works like :meth:`~canaille.backends.models.Model.query` but return
only one element or :py:data:`None` if no item is matching."""
2023-08-17 13:55:41 +00:00
raise NotImplementedError()
@property
def identifier(self):
2023-12-28 17:31:57 +00:00
"""Returns a unique value that will be used to identify the model
instance.
This value will be used in URLs in canaille, so it should be
unique and short.
2023-08-17 13:55:41 +00:00
"""
raise NotImplementedError()
def save(self):
2023-12-28 17:31:57 +00:00
"""Validates the current modifications in the database."""
2023-08-17 13:55:41 +00:00
raise NotImplementedError()
def delete(self):
2023-12-28 17:31:57 +00:00
"""Removes the current instance from the database."""
2023-08-17 13:55:41 +00:00
raise NotImplementedError()
def update(self, **kwargs):
2023-12-28 17:31:57 +00:00
"""Assign a whole dict to the current instance. This is useful to
update models based on forms.
2023-08-17 13:55:41 +00:00
>>> user = User.get(user_name="george")
>>> user.first_name
George
>>> user.update({
... first_name="Jane",
... last_name="Calamity",
... })
>>> user.first_name
Jane
"""
for attribute, value in kwargs.items():
setattr(self, attribute, value)
2023-08-17 13:55:41 +00:00
def reload(self):
2023-12-28 17:31:57 +00:00
"""Cancels the unsaved modifications.
2023-08-17 13:55:41 +00:00
>>> user = User.get(user_name="george")
>>> user.display_name
George
>>> user.display_name = "Jane"
>>> user.display_name
Jane
>>> user.reload()
>>> user.display_name
George
"""
raise NotImplementedError()