canaille-globuzma/canaille/backends/models.py

139 lines
4.5 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
It details all the common attributes shared by every models.
"""
id: Optional[str]
"""A unique identifier for a SCIM resource as defined by the service
provider. Id will be :py:data:`None` until the
:meth:`~canaille.backends.models.BackendModel.save` method is called.
Each representation of the resource MUST include a non-empty "id"
value. This identifier MUST be unique across the SCIM service
provider's entire set of resources. It MUST be a stable, non-
reassignable identifier that does not change when the same resource
is returned in subsequent requests. The value of the "id" attribute
is always issued by the service provider and MUST NOT be specified
by the client. The string "bulkId" is a reserved keyword and MUST
NOT be used within any unique identifier value. The attribute
characteristics are "caseExact" as "true", a mutability of
"readOnly", and a "returned" characteristic of "always". See
Section 9 for additional considerations regarding privacy.
"""
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`.
"""
class BackendModel:
"""The backend model abstract class.
It details all the methods and attributes that are expected to be
implemented for every model and for every backend.
"""
@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-04-05 14:09:20 +00:00
"""Works like :meth:`~canaille.backends.models.BackendModel.query` but
2024-03-29 08:35:29 +00:00
attribute values loosely be matched."""
2023-08-17 13:55:41 +00:00
raise NotImplementedError()
@classmethod
def get(cls, identifier=None, **kwargs):
2024-04-05 14:09:20 +00:00
"""Works like :meth:`~canaille.backends.models.BackendModel.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()