add part of docstrings
This commit is contained in:
@ -1,3 +1,11 @@
|
|||||||
|
"""
|
||||||
|
Module with all decorators that are exposed for users.
|
||||||
|
|
||||||
|
Currently only:
|
||||||
|
* property_field - exposing @property like function as field in Model.dict()
|
||||||
|
* predefined signals decorators (pre/post + save/update/delete)
|
||||||
|
|
||||||
|
"""
|
||||||
from ormar.decorators.property_field import property_field
|
from ormar.decorators.property_field import property_field
|
||||||
from ormar.decorators.signals import (
|
from ormar.decorators.signals import (
|
||||||
post_delete,
|
post_delete,
|
||||||
|
|||||||
@ -6,6 +6,19 @@ from ormar.exceptions import ModelDefinitionError
|
|||||||
|
|
||||||
|
|
||||||
def property_field(func: Callable) -> Union[property, Callable]:
|
def property_field(func: Callable) -> Union[property, Callable]:
|
||||||
|
"""
|
||||||
|
Decorator to set a property like function on Model to be exposed
|
||||||
|
as field in dict() and fastapi response.
|
||||||
|
Although you can decorate a @property field like this and this will work,
|
||||||
|
mypy validation will complain about this.
|
||||||
|
Note that "fields" exposed like this do not go through validation.
|
||||||
|
|
||||||
|
:raises: ModelDefinitionError if method has any other argument than self.
|
||||||
|
:param func: decorated function to be exposed
|
||||||
|
:type func: Callable
|
||||||
|
:return: decorated function passed in func param, with set __property_field__ = True
|
||||||
|
:rtype: Union[property, Callable]
|
||||||
|
"""
|
||||||
if isinstance(func, property): # pragma: no cover
|
if isinstance(func, property): # pragma: no cover
|
||||||
func.fget.__property_field__ = True
|
func.fget.__property_field__ = True
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -7,7 +7,28 @@ if TYPE_CHECKING: # pragma: no cover
|
|||||||
def receiver(
|
def receiver(
|
||||||
signal: str, senders: Union[Type["Model"], List[Type["Model"]]]
|
signal: str, senders: Union[Type["Model"], List[Type["Model"]]]
|
||||||
) -> Callable:
|
) -> Callable:
|
||||||
|
"""
|
||||||
|
Connect given function to all senders for given signal name.
|
||||||
|
|
||||||
|
:param signal: name of the signal to register to
|
||||||
|
:type signal: str
|
||||||
|
:param senders: one or a list of "Model" classes
|
||||||
|
that should have the signal receiver registered
|
||||||
|
:type senders: Union[Type["Model"], List[Type["Model"]]]
|
||||||
|
:return: returns the original function untouched
|
||||||
|
:rtype: Callable
|
||||||
|
"""
|
||||||
|
|
||||||
def _decorator(func: Callable) -> Callable:
|
def _decorator(func: Callable) -> Callable:
|
||||||
|
"""
|
||||||
|
|
||||||
|
Internal decorator that does all the registeriing.
|
||||||
|
|
||||||
|
:param func: function to register as receiver
|
||||||
|
:type func: Callable
|
||||||
|
:return: untouched function already registered for given signal
|
||||||
|
:rtype: Callable
|
||||||
|
"""
|
||||||
if not isinstance(senders, list):
|
if not isinstance(senders, list):
|
||||||
_senders = [senders]
|
_senders = [senders]
|
||||||
else:
|
else:
|
||||||
@ -21,24 +42,78 @@ def receiver(
|
|||||||
|
|
||||||
|
|
||||||
def post_save(senders: Union[Type["Model"], List[Type["Model"]]],) -> Callable:
|
def post_save(senders: Union[Type["Model"], List[Type["Model"]]],) -> Callable:
|
||||||
|
"""
|
||||||
|
Connect given function to all senders for post_save signal.
|
||||||
|
|
||||||
|
:param senders: one or a list of "Model" classes
|
||||||
|
that should have the signal receiver registered
|
||||||
|
:type senders: Union[Type["Model"], List[Type["Model"]]]
|
||||||
|
:return: returns the original function untouched
|
||||||
|
:rtype: Callable
|
||||||
|
"""
|
||||||
return receiver(signal="post_save", senders=senders)
|
return receiver(signal="post_save", senders=senders)
|
||||||
|
|
||||||
|
|
||||||
def post_update(senders: Union[Type["Model"], List[Type["Model"]]],) -> Callable:
|
def post_update(senders: Union[Type["Model"], List[Type["Model"]]],) -> Callable:
|
||||||
|
"""
|
||||||
|
Connect given function to all senders for post_update signal.
|
||||||
|
|
||||||
|
:param senders: one or a list of "Model" classes
|
||||||
|
that should have the signal receiver registered
|
||||||
|
:type senders: Union[Type["Model"], List[Type["Model"]]]
|
||||||
|
:return: returns the original function untouched
|
||||||
|
:rtype: Callable
|
||||||
|
"""
|
||||||
return receiver(signal="post_update", senders=senders)
|
return receiver(signal="post_update", senders=senders)
|
||||||
|
|
||||||
|
|
||||||
def post_delete(senders: Union[Type["Model"], List[Type["Model"]]],) -> Callable:
|
def post_delete(senders: Union[Type["Model"], List[Type["Model"]]],) -> Callable:
|
||||||
|
"""
|
||||||
|
Connect given function to all senders for post_delete signal.
|
||||||
|
|
||||||
|
:param senders: one or a list of "Model" classes
|
||||||
|
that should have the signal receiver registered
|
||||||
|
:type senders: Union[Type["Model"], List[Type["Model"]]]
|
||||||
|
:return: returns the original function untouched
|
||||||
|
:rtype: Callable
|
||||||
|
"""
|
||||||
return receiver(signal="post_delete", senders=senders)
|
return receiver(signal="post_delete", senders=senders)
|
||||||
|
|
||||||
|
|
||||||
def pre_save(senders: Union[Type["Model"], List[Type["Model"]]],) -> Callable:
|
def pre_save(senders: Union[Type["Model"], List[Type["Model"]]],) -> Callable:
|
||||||
|
"""
|
||||||
|
Connect given function to all senders for pre_save signal.
|
||||||
|
|
||||||
|
:param senders: one or a list of "Model" classes
|
||||||
|
that should have the signal receiver registered
|
||||||
|
:type senders: Union[Type["Model"], List[Type["Model"]]]
|
||||||
|
:return: returns the original function untouched
|
||||||
|
:rtype: Callable
|
||||||
|
"""
|
||||||
return receiver(signal="pre_save", senders=senders)
|
return receiver(signal="pre_save", senders=senders)
|
||||||
|
|
||||||
|
|
||||||
def pre_update(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable:
|
def pre_update(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable:
|
||||||
|
"""
|
||||||
|
Connect given function to all senders for pre_update signal.
|
||||||
|
|
||||||
|
:param senders: one or a list of "Model" classes
|
||||||
|
that should have the signal receiver registered
|
||||||
|
:type senders: Union[Type["Model"], List[Type["Model"]]]
|
||||||
|
:return: returns the original function untouched
|
||||||
|
:rtype: Callable
|
||||||
|
"""
|
||||||
return receiver(signal="pre_update", senders=senders)
|
return receiver(signal="pre_update", senders=senders)
|
||||||
|
|
||||||
|
|
||||||
def pre_delete(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable:
|
def pre_delete(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable:
|
||||||
|
"""
|
||||||
|
Connect given function to all senders for pre_delete signal.
|
||||||
|
|
||||||
|
:param senders: one or a list of "Model" classes
|
||||||
|
that should have the signal receiver registered
|
||||||
|
:type senders: Union[Type["Model"], List[Type["Model"]]]
|
||||||
|
:return: returns the original function untouched
|
||||||
|
:rtype: Callable
|
||||||
|
"""
|
||||||
return receiver(signal="pre_delete", senders=senders)
|
return receiver(signal="pre_delete", senders=senders)
|
||||||
|
|||||||
@ -1,3 +1,9 @@
|
|||||||
|
"""
|
||||||
|
Module with classes and constructors for ormar Fields.
|
||||||
|
Base Fields types (like String, Integer etc.)
|
||||||
|
as well as relation Fields (ForeignKey, ManyToMany).
|
||||||
|
Also a definition for custom CHAR based sqlalchemy UUID field
|
||||||
|
"""
|
||||||
from ormar.fields.base import BaseField
|
from ormar.fields.base import BaseField
|
||||||
from ormar.fields.foreign_key import ForeignKey, UniqueColumns
|
from ormar.fields.foreign_key import ForeignKey, UniqueColumns
|
||||||
from ormar.fields.many_to_many import ManyToMany, ManyToManyField
|
from ormar.fields.many_to_many import ManyToMany, ManyToManyField
|
||||||
|
|||||||
@ -14,6 +14,16 @@ if TYPE_CHECKING: # pragma no cover
|
|||||||
|
|
||||||
|
|
||||||
class BaseField(FieldInfo):
|
class BaseField(FieldInfo):
|
||||||
|
"""
|
||||||
|
BaseField serves as a parent class for all basic Fields in ormar.
|
||||||
|
It keeps all common parameters available for all fields as well as
|
||||||
|
set of useful functions.
|
||||||
|
|
||||||
|
All values are kept as class variables, ormar Fields are never instantiated.
|
||||||
|
Subclasses pydantic.FieldInfo to keep the fields related
|
||||||
|
to pydantic field types like ConstrainedStr
|
||||||
|
"""
|
||||||
|
|
||||||
__type__ = None
|
__type__ = None
|
||||||
|
|
||||||
column_type: sqlalchemy.Column
|
column_type: sqlalchemy.Column
|
||||||
@ -37,14 +47,44 @@ class BaseField(FieldInfo):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_valid_uni_relation(cls) -> bool:
|
def is_valid_uni_relation(cls) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if field is a relation definition but only for ForeignKey relation,
|
||||||
|
so excludes ManyToMany fields, as well as virtual ForeignKey
|
||||||
|
(second side of FK relation).
|
||||||
|
|
||||||
|
Is used to define if a field is a db ForeignKey column that
|
||||||
|
should be saved/populated when dealing with internal/own
|
||||||
|
Model columns only.
|
||||||
|
|
||||||
|
:return: result of the check
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
return not issubclass(cls, ormar.fields.ManyToManyField) and not cls.virtual
|
return not issubclass(cls, ormar.fields.ManyToManyField) and not cls.virtual
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_alias(cls) -> str:
|
def get_alias(cls) -> str:
|
||||||
|
"""
|
||||||
|
Used to translate Model column names to database column names during db queries.
|
||||||
|
|
||||||
|
:return: returns custom database column name if defined by user,
|
||||||
|
otherwise field name in ormar/pydantic
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
return cls.alias if cls.alias else cls.name
|
return cls.alias if cls.alias else cls.name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_valid_field_info_field(cls, field_name: str) -> bool:
|
def is_valid_field_info_field(cls, field_name: str) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if field belongs to pydantic FieldInfo
|
||||||
|
- used during setting default pydantic values.
|
||||||
|
Excludes defaults and alias as they are populated separately
|
||||||
|
(defaults) or not at all (alias)
|
||||||
|
|
||||||
|
:param field_name: field name of BaseFIeld
|
||||||
|
:type field_name: str
|
||||||
|
:return: True if field is present on pydantic.FieldInfo
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
return (
|
return (
|
||||||
field_name not in ["default", "default_factory", "alias"]
|
field_name not in ["default", "default_factory", "alias"]
|
||||||
and not field_name.startswith("__")
|
and not field_name.startswith("__")
|
||||||
@ -53,6 +93,17 @@ class BaseField(FieldInfo):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def convert_to_pydantic_field_info(cls, allow_null: bool = False) -> FieldInfo:
|
def convert_to_pydantic_field_info(cls, allow_null: bool = False) -> FieldInfo:
|
||||||
|
"""
|
||||||
|
Converts a BaseField into pydantic.FieldInfo
|
||||||
|
that is later easily processed by pydantic.
|
||||||
|
Used in an ormar Model Metaclass.
|
||||||
|
|
||||||
|
:param allow_null: flag if the default value can be None
|
||||||
|
or if it should be populated by pydantic Undefined
|
||||||
|
:type allow_null: bool
|
||||||
|
:return: actual instance of pydantic.FieldInfo with all needed fields populated
|
||||||
|
:rtype: pydantic.FieldInfo
|
||||||
|
"""
|
||||||
base = cls.default_value()
|
base = cls.default_value()
|
||||||
if base is None:
|
if base is None:
|
||||||
base = (
|
base = (
|
||||||
@ -67,6 +118,23 @@ class BaseField(FieldInfo):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_value(cls, use_server: bool = False) -> Optional[FieldInfo]:
|
def default_value(cls, use_server: bool = False) -> Optional[FieldInfo]:
|
||||||
|
"""
|
||||||
|
Returns a FieldInfo instance with populated default
|
||||||
|
(static) or default_factory (function).
|
||||||
|
If the field is a autoincrement primary key the default is None.
|
||||||
|
Otherwise field have to has either default, or default_factory populated.
|
||||||
|
|
||||||
|
If all default conditions fail None is returned.
|
||||||
|
|
||||||
|
Used in converting to pydantic FieldInfo.
|
||||||
|
|
||||||
|
:param use_server: flag marking if server_default should be
|
||||||
|
treated as default value, default False
|
||||||
|
:type use_server: bool
|
||||||
|
:return: returns a call to pydantic.Field
|
||||||
|
which is returning a FieldInfo instance
|
||||||
|
:rtype: Optional[pydantic.FieldInfo]
|
||||||
|
"""
|
||||||
if cls.is_auto_primary_key():
|
if cls.is_auto_primary_key():
|
||||||
return Field(default=None)
|
return Field(default=None)
|
||||||
if cls.has_default(use_server=use_server):
|
if cls.has_default(use_server=use_server):
|
||||||
@ -78,6 +146,17 @@ class BaseField(FieldInfo):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_default(cls, use_server: bool = False) -> Any: # noqa CCR001
|
def get_default(cls, use_server: bool = False) -> Any: # noqa CCR001
|
||||||
|
"""
|
||||||
|
Return default value for a field.
|
||||||
|
If the field is Callable the function is called and actual result is returned.
|
||||||
|
Used to populate default_values for pydantic Model in ormar Model Metaclass.
|
||||||
|
|
||||||
|
:param use_server: flag marking if server_default should be
|
||||||
|
treated as default value, default False
|
||||||
|
:type use_server: bool
|
||||||
|
:return: default value for the field if set, otherwise implicit None
|
||||||
|
:rtype: Any
|
||||||
|
"""
|
||||||
if cls.has_default():
|
if cls.has_default():
|
||||||
default = (
|
default = (
|
||||||
cls.default
|
cls.default
|
||||||
@ -90,18 +169,45 @@ class BaseField(FieldInfo):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def has_default(cls, use_server: bool = True) -> bool:
|
def has_default(cls, use_server: bool = True) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if the field has default value set.
|
||||||
|
|
||||||
|
:param use_server: flag marking if server_default should be
|
||||||
|
treated as default value, default False
|
||||||
|
:type use_server: bool
|
||||||
|
:return: result of the check if default value is set
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
return cls.default is not None or (
|
return cls.default is not None or (
|
||||||
cls.server_default is not None and use_server
|
cls.server_default is not None and use_server
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_auto_primary_key(cls) -> bool:
|
def is_auto_primary_key(cls) -> bool:
|
||||||
|
"""
|
||||||
|
Checks if field is first a primary key and if it,
|
||||||
|
it's than check if it's set to autoincrement.
|
||||||
|
Autoincrement primary_key is nullable/optional.
|
||||||
|
|
||||||
|
:return: result of the check for primary key and autoincrement
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
if cls.primary_key:
|
if cls.primary_key:
|
||||||
return cls.autoincrement
|
return cls.autoincrement
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_column(cls, name: str) -> sqlalchemy.Column:
|
def get_column(cls, name: str) -> sqlalchemy.Column:
|
||||||
|
"""
|
||||||
|
Returns definition of sqlalchemy.Column used in creation of sqlalchemy.Table.
|
||||||
|
Populates name, column type constraints, as well as a number of parameters like
|
||||||
|
primary_key, index, unique, nullable, default and server_default.
|
||||||
|
|
||||||
|
:param name: name of the db column - used if alias is not set
|
||||||
|
:type name: str
|
||||||
|
:return: actual definition of the database column as sqlalchemy requires.
|
||||||
|
:rtype: sqlalchemy.Column
|
||||||
|
"""
|
||||||
return sqlalchemy.Column(
|
return sqlalchemy.Column(
|
||||||
cls.alias or name,
|
cls.alias or name,
|
||||||
cls.column_type,
|
cls.column_type,
|
||||||
@ -118,4 +224,20 @@ class BaseField(FieldInfo):
|
|||||||
def expand_relationship(
|
def expand_relationship(
|
||||||
cls, value: Any, child: Union["Model", "NewBaseModel"], to_register: bool = True
|
cls, value: Any, child: Union["Model", "NewBaseModel"], to_register: bool = True
|
||||||
) -> Any:
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Function overwritten for relations, in basic field the value is returned as is.
|
||||||
|
For relations the child model is first constructed (if needed),
|
||||||
|
registered in relation and returned.
|
||||||
|
For relation fields the value can be a pk value (Any type of field),
|
||||||
|
dict (from Model) or actual instance/list of a "Model".
|
||||||
|
|
||||||
|
:param value: a Model field value, returned untouched for non relation fields.
|
||||||
|
:type value: Any
|
||||||
|
:param child: a child Model to register
|
||||||
|
:type child: Union["Model", "NewBaseModel"]
|
||||||
|
:param to_register: flag if the relation should be set in RelationshipManager
|
||||||
|
:type to_register: bool
|
||||||
|
:return: returns untouched value for normal fields, expands only for relations
|
||||||
|
:rtype: Any
|
||||||
|
"""
|
||||||
return value
|
return value
|
||||||
|
|||||||
Reference in New Issue
Block a user