next part of the docs and api documentation in beta ver

This commit is contained in:
collerek
2021-01-04 19:38:21 +01:00
parent eec17e2f78
commit 9f8e8e87e8
64 changed files with 7414 additions and 37 deletions

View File

@ -1,3 +1,24 @@
"""
The `ormar` package is an async mini ORM for Python, with support for **Postgres,
MySQL**, and **SQLite**.
The main benefit of using `ormar` are:
* getting an **async ORM that can be used with async frameworks**
(fastapi, starlette etc.)
* getting just **one model to maintain** - you don't have to maintain pydantic
and other orm model (sqlalchemy, peewee, gino etc.)
The goal was to create a simple ORM that can be **used directly
(as request and response models)
with `fastapi`** that bases it's data validation on pydantic.
Ormar - apart form obvious ORM in name - get it's name from ormar in swedish which means
snakes, and ormar(e) in italian which means cabinet.
And what's a better name for python ORM than snakes cabinet :)
"""
from ormar.decorators import (
post_delete,
post_save,

View File

@ -2,6 +2,7 @@
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)

View File

@ -13,7 +13,7 @@ def property_field(func: Callable) -> Union[property, Callable]:
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.
: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

View File

@ -1,3 +1,8 @@
"""
Gathers all exceptions thrown by ormar.
"""
class AsyncOrmException(Exception):
"""
Base ormar Exception
@ -8,7 +13,8 @@ class AsyncOrmException(Exception):
class ModelDefinitionError(AsyncOrmException):
"""
Raised for errors related to the model definition itself.
Raised for errors related to the model definition itself:
* setting @property_field on method with arguments other than func(self)
* defining a Field without required parameters
* defining a model with more than one primary_key
@ -46,7 +52,8 @@ class MultipleMatches(AsyncOrmException):
class QueryDefinitionError(AsyncOrmException):
"""
Raised for errors in query definition.
Raised for errors in query definition:
* using contains or icontains filter with instance of the Model
* using Queryset.update() without filter and setting each flag to True
* using Queryset.delete() without filter and setting each flag to True

View File

@ -77,6 +77,11 @@ class UniqueColumns(UniqueConstraint):
@dataclass
class ForeignKeyConstraint:
"""
Internal container to store ForeignKey definitions used later
to produce sqlalchemy.ForeignKeys
"""
name: str
ondelete: str
onupdate: str
@ -114,10 +119,10 @@ def ForeignKey( # noqa CFQ002
It is for reversed FK and auto generated FK on through model in Many2Many relations.
:type virtual: bool
:param onupdate: parameter passed to sqlalchemy.ForeignKey.
How to treat child rows on update of parent (the one wher FK is defined) model.
How to treat child rows on update of parent (the one where FK is defined) model.
:type onupdate: str
:param ondelete: parameter passed to sqlalchemy.ForeignKey.
How to treat child rows on delete of parent (the one wher FK is defined) model.
How to treat child rows on delete of parent (the one where FK is defined) model.
:type ondelete: str
:param kwargs: all other args to be populated by BaseField
:type kwargs: Any

View File

@ -116,6 +116,10 @@ class ModelFieldFactory:
class String(ModelFieldFactory, str):
"""
String field factory that construct Field classes and populated their values.
"""
_type = str
def __new__( # type: ignore # noqa CFQ002
@ -142,10 +146,24 @@ class String(ModelFieldFactory, str):
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.String(length=kwargs.get("max_length"))
@classmethod
def validate(cls, **kwargs: Any) -> None:
"""
Used to validate if all required parameters on a given field type are set.
:param kwargs: all params passed during construction
:type kwargs: Any
"""
max_length = kwargs.get("max_length", None)
if max_length is None or max_length <= 0:
raise ModelDefinitionError(
@ -154,6 +172,10 @@ class String(ModelFieldFactory, str):
class Integer(ModelFieldFactory, int):
"""
Integer field factory that construct Field classes and populated their values.
"""
_type = int
def __new__( # type: ignore
@ -184,10 +206,23 @@ class Integer(ModelFieldFactory, int):
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.Integer()
class Text(ModelFieldFactory, str):
"""
Text field factory that construct Field classes and populated their values.
"""
_type = str
def __new__( # type: ignore
@ -206,10 +241,23 @@ class Text(ModelFieldFactory, str):
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.Text()
class Float(ModelFieldFactory, float):
"""
Float field factory that construct Field classes and populated their values.
"""
_type = float
def __new__( # type: ignore
@ -234,6 +282,15 @@ class Float(ModelFieldFactory, float):
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.Float()
@ -246,46 +303,115 @@ if TYPE_CHECKING: # pragma: nocover
else:
class Boolean(ModelFieldFactory, int):
"""
Boolean field factory that construct Field classes and populated their values.
"""
_type = bool
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.Boolean()
class DateTime(ModelFieldFactory, datetime.datetime):
"""
DateTime field factory that construct Field classes and populated their values.
"""
_type = datetime.datetime
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.DateTime()
class Date(ModelFieldFactory, datetime.date):
"""
Date field factory that construct Field classes and populated their values.
"""
_type = datetime.date
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.Date()
class Time(ModelFieldFactory, datetime.time):
"""
Time field factory that construct Field classes and populated their values.
"""
_type = datetime.time
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.Time()
class JSON(ModelFieldFactory, pydantic.Json):
"""
JSON field factory that construct Field classes and populated their values.
"""
_type = pydantic.Json
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.JSON()
class BigInteger(Integer, int):
"""
BigInteger field factory that construct Field classes and populated their values.
"""
_type = int
def __new__( # type: ignore
@ -316,10 +442,23 @@ class BigInteger(Integer, int):
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
return sqlalchemy.BigInteger()
class Decimal(ModelFieldFactory, decimal.Decimal):
"""
Decimal field factory that construct Field classes and populated their values.
"""
_type = decimal.Decimal
def __new__( # type: ignore # noqa CFQ002
@ -359,12 +498,26 @@ class Decimal(ModelFieldFactory, decimal.Decimal):
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
precision = kwargs.get("precision")
scale = kwargs.get("scale")
return sqlalchemy.DECIMAL(precision=precision, scale=scale)
@classmethod
def validate(cls, **kwargs: Any) -> None:
"""
Used to validate if all required parameters on a given field type are set.
:param kwargs: all params passed during construction
:type kwargs: Any
"""
precision = kwargs.get("precision")
scale = kwargs.get("scale")
if precision is None or precision < 0 or scale is None or scale < 0:
@ -374,6 +527,10 @@ class Decimal(ModelFieldFactory, decimal.Decimal):
class UUID(ModelFieldFactory, uuid.UUID):
"""
UUID field factory that construct Field classes and populated their values.
"""
_type = uuid.UUID
def __new__( # type: ignore # noqa CFQ002
@ -392,5 +549,14 @@ class UUID(ModelFieldFactory, uuid.UUID):
@classmethod
def get_column_type(cls, **kwargs: Any) -> Any:
"""
Return proper type of db column for given field type.
Accepts required and optional parameters that each column type accepts.
:param kwargs: key, value pairs of sqlalchemy options
:type kwargs: Any
:return: initialized column with proper options
:rtype: sqlalchemy Column
"""
uuid_format = kwargs.get("uuid_format", "hex")
return sqlalchemy_uuid.UUID(uuid_format=uuid_format)

View File

@ -1,3 +1,9 @@
"""
Definition of Model, it's parents NewBaseModel and mixins used by models.
Also defines a Metaclass that handles all constructions and relations registration,
ass well as vast number of helper functions for pydantic, sqlalchemy and relations.
"""
from ormar.models.newbasemodel import NewBaseModel # noqa I100
from ormar.models.model import Model # noqa I100

View File

@ -61,7 +61,7 @@ def validate_related_names_in_relations(
(populated by default as model.name.lower()+'s').
Also related_names have to be unique for given related model.
:raises: ModelDefinitionError if validation of related_names fail
:raises ModelDefinitionError: if validation of related_names fail
:param model_fields: dictionary of declared ormar model fields
:type model_fields: Dict[str, ormar.Field]
:param new_model:

View File

@ -157,7 +157,7 @@ def verify_related_name_dont_duplicate(
auto generated) is already used on related model, but is connected with other model
than the one that we connect right now.
:raises: ModelDefinitionError if name is already used but lead to different related
:raises ModelDefinitionError: if name is already used but lead to different related
model
:param child: related Model class
:type child: ormar.models.metaclass.ModelMetaclass
@ -191,7 +191,7 @@ def reverse_field_not_already_registered(
"""
Checks if child is already registered in parents pydantic fields.
:raises: ModelDefinitionError if related name is already used but lead to different
:raises ModelDefinitionError: if related name is already used but lead to different
related model
:param child: related Model class
:type child: ormar.models.metaclass.ModelMetaclass

View File

@ -84,7 +84,7 @@ def check_pk_column_validity(
was not already set (only one allowed per model) and if field is not marked
as pydantic_only as it needs to be a database field.
:raises: ModelDefintionError if pkname already set or field is pydantic_only
:raises ModelDefintionError: if pkname already set or field is pydantic_only
:param field_name: name of field
:type field_name: str
:param field: ormar.Field
@ -121,7 +121,7 @@ def sqlalchemy_columns_from_model_fields(
Append fields to columns if it's not pydantic_only,
virtual ForeignKey or ManyToMany field.
:raises: ModelDefinitionError if validation of related_names fail,
:raises ModelDefinitionError: if validation of related_names fail,
or pkname validation fails.
:param model_fields: dictionary of declared ormar model fields
:type model_fields: Dict[str, ormar.Field]
@ -162,7 +162,7 @@ def populate_meta_tablename_columns_and_pk(
If not calls the sqlalchemy_columns_from_model_fields to populate
columns from ormar.fields definitions.
:raises: if pkname is not present raises ModelDefinitionError.
:raises ModelDefinitionError: if pkname is not present raises ModelDefinitionError.
Each model has to have pk.
:param name: name of the current Model

View File

@ -48,7 +48,7 @@ CONFIG_KEY = "Config"
class ModelMeta:
"""
Class used for type hinting.
Users can subclass this one for conveniance but it's not required.
Users can subclass this one for convenience but it's not required.
The only requirement is that ormar.Model has to have inner class with name Meta.
"""
@ -86,7 +86,7 @@ def choices_validator(cls: Type["Model"], values: Dict[str, Any]) -> Dict[str, A
Validator that is attached to pydantic model pre root validators.
Validator checks if field value is in field.choices list.
:raises: ValueError if field value is outside of allowed choices.
:raises ValueError: if field value is outside of allowed choices.
:param cls: constructed class
:type cls: Model class
:param values: dictionary of field values (pydantic side)
@ -321,7 +321,7 @@ def copy_data_from_parent_model( # noqa: CCR001
Since relation fields requires different related_name for different children
:raises: ModelDefinitionError if non abstract model is subclassed
:raises ModelDefinitionError: if non abstract model is subclassed
:param base_class: one of the parent classes
:type base_class: Model or model parent class
:param curr_class: current constructed class
@ -500,6 +500,7 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
Construct parent pydantic Metaclass/ Model.
If class has Meta class declared (so actual ormar Models) it also:
* populate sqlalchemy columns, pkname and tables from model_fields
* register reverse relationships on related models
* registers all relations in alias manager that populates table_prefixes

View File

@ -12,7 +12,7 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
"""
@classmethod
def _prepare_model_to_save(cls, new_kwargs: dict) -> dict:
def prepare_model_to_save(cls, new_kwargs: dict) -> dict:
"""
Combines all preparation methods before saving.
Removes primary key for if it's nullable or autoincrement pk field,

View File

@ -456,7 +456,7 @@ class Model(NewBaseModel):
Sets model save status to True.
:raises: If the pk column is not set will throw ModelPersistenceError
:raises ModelPersistenceError: If the pk column is not set
:param kwargs: list of fields to update as field=value pairs
:type kwargs: Any
@ -512,7 +512,7 @@ class Model(NewBaseModel):
Be careful as the related models can be overwritten by pk_only models in load.
Does NOT refresh the related models fields if they were loaded before.
:raises: If given pk is not found in database the NoMatch exception is raised.
:raises NoMatch: If given pk is not found in database.
:return: reloaded Model
:rtype: Model

View File

@ -103,7 +103,7 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
should be explicitly set to None, as otherwise pydantic will try to populate
them with their default values if default is set.
:raises: ModelError if abstract model is initialized or unknown field is passed
:raises ModelError: if abstract model is initialized or unknown field is passed
:param args: ignored args
:type args: Any
:param kwargs: keyword arguments - all fields values and some special params

View File

@ -245,7 +245,7 @@ class QueryClause:
Escapes the special characters ["%", "_"] if needed.
Adds `%` for `like` queries.
:raises: QueryDefinitionError if contains or icontains is used with
:raises QueryDefinitionError: if contains or icontains is used with
ormar model instance
:param op: operator used in query
:type op: str

View File

@ -647,8 +647,8 @@ class QuerySet:
"""
Gets the first row from the db ordered by primary key column ascending.
:raises: NoMatch if no rows are returned
:raises: MultipleMatches if more than 1 row is returned.
:raises NoMatch: if no rows are returned
:raises MultipleMatches: if more than 1 row is returned.
:param kwargs: fields names and proper value types
:type kwargs: Any
:return: returned model
@ -675,8 +675,8 @@ class QuerySet:
Passing a criteria is actually calling filter(**kwargs) method described below.
:raises: NoMatch if no rows are returned
:raises: MultipleMatches if more than 1 row is returned.
:raises NoMatch: if no rows are returned
:raises MultipleMatches: if more than 1 row is returned.
:param kwargs: fields names and proper value types
:type kwargs: Any
:return: returned model
@ -771,7 +771,7 @@ class QuerySet:
:rtype: Model
"""
new_kwargs = dict(**kwargs)
new_kwargs = self.model._prepare_model_to_save(new_kwargs)
new_kwargs = self.model.prepare_model_to_save(new_kwargs)
expr = self.table.insert()
expr = expr.values(**new_kwargs)
@ -817,7 +817,7 @@ class QuerySet:
ready_objects = []
for objt in objects:
new_kwargs = objt.dict()
new_kwargs = objt._prepare_model_to_save(new_kwargs)
new_kwargs = objt.prepare_model_to_save(new_kwargs)
ready_objects.append(new_kwargs)
expr = self.table.insert()

View File

@ -88,7 +88,7 @@ def convert_set_to_required_dict(set_to_convert: set) -> Dict:
:param set_to_convert: set to convert to dict
:type set_to_convert: set
:return: set converted to dict of ellipsis
:rtype: Dict[str, ellipsis]
:rtype: Dict
"""
new_dict = dict()
for key in set_to_convert:

View File

@ -211,8 +211,8 @@ class QuerysetProxy(ormar.QuerySetProtocol):
List of related models is cleared before the call.
:raises: NoMatch if no rows are returned
:raises: MultipleMatches if more than 1 row is returned.
:raises NoMatch: if no rows are returned
:raises MultipleMatches: if more than 1 row is returned.
:param kwargs: fields names and proper value types
:type kwargs: Any
:return: returned model

View File

@ -16,10 +16,11 @@ if TYPE_CHECKING: # pragma no cover
class RelationType(Enum):
"""
Different types of relations supported by ormar.
ForeignKey = PRIMARY
reverse ForeignKey = REVERSE
ManyToMany = MULTIPLE
Different types of relations supported by ormar:
* ForeignKey = PRIMARY
* reverse ForeignKey = REVERSE
* ManyToMany = MULTIPLE
"""
PRIMARY = 1

View File

@ -1,3 +1,7 @@
"""
Signals and SignalEmitter that gathers the signals on models Meta.
Used to signal receivers functions about events, i.e. post_save, pre_delete etc.
"""
from ormar.signals.signal import Signal, SignalEmitter
__all__ = ["Signal", "SignalEmitter"]

View File

@ -14,7 +14,7 @@ def callable_accepts_kwargs(func: Callable) -> bool:
:param func: function which signature needs to be checked
:type func: function
:return:
:return: result of the check
:rtype: bool
"""
return any(
@ -51,7 +51,7 @@ class Signal:
"""
Connects given receiver function to the signal.
:raises: SignalDefinitionError if receiver is not callable
:raises SignalDefinitionError: if receiver is not callable
or not accept **kwargs
:param receiver: receiver function
:type receiver: Callable