2024-03-27 12:53:46 +00:00
|
|
|
import datetime
|
2023-11-15 17:20:13 +00:00
|
|
|
from collections import ChainMap
|
2024-03-27 12:53:46 +00:00
|
|
|
from typing import Optional
|
2023-11-15 17:20:13 +00:00
|
|
|
|
|
|
|
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-27 12:53:46 +00:00
|
|
|
"""
|
|
|
|
|
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."""
|
|
|
|
|
2024-03-27 12:53:46 +00:00
|
|
|
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.
|
2024-03-27 12:53:46 +00:00
|
|
|
|
|
|
|
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`.
|
2024-03-27 12:53:46 +00:00
|
|
|
"""
|
|
|
|
|
2023-11-15 17:20:13 +00:00
|
|
|
@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
|
|
|
|
"""
|
2023-11-15 17:20:13 +00:00
|
|
|
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()
|