Merge pull request #508 from collerek/check_timezones_filters
Fix timezones and add support for pydantic 1.9.0
This commit is contained in:
@ -1,17 +1,20 @@
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 21.9b0
|
||||
rev: 21.12b0
|
||||
hooks:
|
||||
- id: black
|
||||
exclude: ^(docs_src/|examples/)
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 3.9.2
|
||||
hooks:
|
||||
- id: flake8
|
||||
exclude: ^(docs_src/|examples/|tests/)
|
||||
args: [ '--max-line-length=88' ]
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.910
|
||||
hooks:
|
||||
- id: mypy
|
||||
exclude: ^(docs_src/|examples/)
|
||||
args: [--no-strict-optional, --ignore-missing-imports]
|
||||
additional_dependencies: [
|
||||
types-ujson>=0.1.1,
|
||||
|
||||
3
Makefile
3
Makefile
@ -1,6 +1,3 @@
|
||||
PIPENV_RUN := pipenv run
|
||||
PG_DOCKERFILE_NAME := fastapi-users-test-mongo
|
||||
|
||||
test_all: test_pg test_mysql test_sqlite
|
||||
|
||||
test_pg: export DATABASE_URL=postgresql://username:password@localhost:5432/testsuite
|
||||
|
||||
@ -61,6 +61,12 @@ As of now `ormar` is supported by:
|
||||
* [`fastapi-crudrouter`](https://github.com/awtkns/fastapi-crudrouter)
|
||||
* [`fastapi-pagination`](https://github.com/uriyyo/fastapi-pagination)
|
||||
|
||||
Ormar remains sql dialect agnostic - so only columns working in all supported backends are implemented.
|
||||
|
||||
It's relatively easy to implement columns for specific dialects as an extensions of ormar.
|
||||
|
||||
Postgres specific columns implementation: [`ormar-postgres-extensions`](https://github.com/tophat/ormar-postgres-extensions)
|
||||
|
||||
If you maintain or use a different library and would like it to support `ormar` let us know how we can help.
|
||||
|
||||
### Dependencies
|
||||
@ -74,7 +80,7 @@ Ormar is built with:
|
||||
|
||||
### License
|
||||
|
||||
`ormar` is built as open-sorce software and will remain completely free (MIT license).
|
||||
`ormar` is built as open-source software and will remain completely free (MIT license).
|
||||
|
||||
As I write open-source code to solve everyday problems in my work or to promote and build strong python
|
||||
community you can say thank you and buy me a coffee or sponsor me with a monthly amount to help ensure my work remains free and maintained.
|
||||
|
||||
@ -1,3 +1,21 @@
|
||||
# 0.10.24
|
||||
|
||||
## ✨ Features
|
||||
|
||||
* Add `post_bulk_update` signal (by @ponytailer - thanks!) [#524](https://github.com/collerek/ormar/pull/524)
|
||||
|
||||
## 🐛 Fixes
|
||||
|
||||
* Fix support for `pydantic==1.9.0` [#502](https://github.com/collerek/ormar/issues/502)
|
||||
* Fix timezone issues with datetime [#504](https://github.com/collerek/ormar/issues/504)
|
||||
* Remove literal binds in query generation to unblock postgres arrays [#/tophat/ormar-postgres-extensions/9](https://github.com/tophat/ormar-postgres-extensions/pull/9)
|
||||
* Fix bulk update for `JSON` fields [#519](https://github.com/collerek/ormar/issues/519)
|
||||
|
||||
## 💬 Other
|
||||
|
||||
* Improve performance of `bulk_create` by bypassing `databases` `execute_many` suboptimal implementation. (by @Mng-dev-ai thanks!) [#520](https://github.com/collerek/ormar/pull/520)
|
||||
* Bump min. required `databases` version to `>=5.4`.
|
||||
|
||||
# 0.10.23
|
||||
|
||||
## ✨ Features
|
||||
|
||||
@ -19,4 +19,4 @@ class Course(ormar.Model):
|
||||
|
||||
@property_field
|
||||
def prefixed_name(self):
|
||||
return 'custom_prefix__' + self.name
|
||||
return "custom_prefix__" + self.name
|
||||
|
||||
@ -173,9 +173,7 @@ def post_relation_remove(
|
||||
return receiver(signal="post_relation_remove", senders=senders)
|
||||
|
||||
|
||||
def post_bulk_update(
|
||||
senders: Union[Type["Model"], List[Type["Model"]]]
|
||||
) -> Callable:
|
||||
def post_bulk_update(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable:
|
||||
"""
|
||||
Connect given function to all senders for post_bulk_update signal.
|
||||
|
||||
|
||||
@ -87,4 +87,5 @@ class ModelListEmptyError(AsyncOrmException):
|
||||
"""
|
||||
Raised for objects is empty when bulk_update
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@ -381,7 +381,6 @@ if TYPE_CHECKING: # pragma: nocover
|
||||
def Boolean(**kwargs: Any) -> bool:
|
||||
pass
|
||||
|
||||
|
||||
else:
|
||||
|
||||
class Boolean(ModelFieldFactory, int):
|
||||
@ -545,7 +544,6 @@ if TYPE_CHECKING: # pragma: nocover # noqa: C901
|
||||
) -> Union[str, bytes]:
|
||||
pass
|
||||
|
||||
|
||||
else:
|
||||
|
||||
class LargeBinary(ModelFieldFactory, bytes):
|
||||
|
||||
@ -20,7 +20,6 @@ except ImportError: # pragma: no cover
|
||||
import pydantic
|
||||
from pydantic.class_validators import make_generic_validator
|
||||
from pydantic.fields import ModelField, SHAPE_LIST
|
||||
from pydantic.main import SchemaExtraCallable
|
||||
|
||||
import ormar # noqa: I100, I202
|
||||
from ormar.models.helpers.models import meta_field_not_set
|
||||
@ -249,7 +248,7 @@ def overwrite_binary_format(schema: Dict[str, Any], model: Type["Model"]) -> Non
|
||||
]
|
||||
|
||||
|
||||
def construct_modify_schema_function(fields_with_choices: List) -> SchemaExtraCallable:
|
||||
def construct_modify_schema_function(fields_with_choices: List) -> Callable:
|
||||
"""
|
||||
Modifies the schema to include fields with choices validator.
|
||||
Those fields will be displayed in schema as Enum types with available choices
|
||||
@ -275,7 +274,7 @@ def construct_modify_schema_function(fields_with_choices: List) -> SchemaExtraCa
|
||||
return staticmethod(schema_extra) # type: ignore
|
||||
|
||||
|
||||
def construct_schema_function_without_choices() -> SchemaExtraCallable:
|
||||
def construct_schema_function_without_choices() -> Callable:
|
||||
"""
|
||||
Modifies model example and description if needed.
|
||||
|
||||
|
||||
@ -12,6 +12,11 @@ from typing import (
|
||||
cast,
|
||||
)
|
||||
|
||||
try:
|
||||
import orjson as json
|
||||
except ImportError: # pragma: no cover
|
||||
import json # type: ignore
|
||||
|
||||
import pydantic
|
||||
|
||||
import ormar # noqa: I100, I202
|
||||
@ -31,6 +36,8 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
||||
if TYPE_CHECKING: # pragma: nocover
|
||||
_choices_fields: Optional[Set]
|
||||
_skip_ellipsis: Callable
|
||||
_json_fields: Set[str]
|
||||
_bytes_fields: Set[str]
|
||||
__fields__: Dict[str, pydantic.fields.ModelField]
|
||||
|
||||
@classmethod
|
||||
@ -53,6 +60,7 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
||||
new_kwargs = cls.substitute_models_with_pks(new_kwargs)
|
||||
new_kwargs = cls.populate_default_values(new_kwargs)
|
||||
new_kwargs = cls.reconvert_str_to_bytes(new_kwargs)
|
||||
new_kwargs = cls.dump_all_json_fields_to_str(new_kwargs)
|
||||
new_kwargs = cls.translate_columns_to_aliases(new_kwargs)
|
||||
return new_kwargs
|
||||
|
||||
@ -68,6 +76,7 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
||||
new_kwargs = cls.parse_non_db_fields(new_kwargs)
|
||||
new_kwargs = cls.substitute_models_with_pks(new_kwargs)
|
||||
new_kwargs = cls.reconvert_str_to_bytes(new_kwargs)
|
||||
new_kwargs = cls.dump_all_json_fields_to_str(new_kwargs)
|
||||
new_kwargs = cls.translate_columns_to_aliases(new_kwargs)
|
||||
return new_kwargs
|
||||
|
||||
@ -172,18 +181,13 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
||||
:return: dictionary of model that is about to be saved
|
||||
:rtype: Dict
|
||||
"""
|
||||
bytes_fields = {
|
||||
name
|
||||
for name, field in cls.Meta.model_fields.items()
|
||||
if field.__type__ == bytes
|
||||
}
|
||||
bytes_base64_fields = {
|
||||
name
|
||||
for name, field in cls.Meta.model_fields.items()
|
||||
if field.represent_as_base64_str
|
||||
}
|
||||
for key, value in model_dict.items():
|
||||
if key in bytes_fields and isinstance(value, str):
|
||||
if key in cls._bytes_fields and isinstance(value, str):
|
||||
model_dict[key] = (
|
||||
value.encode("utf-8")
|
||||
if key not in bytes_base64_fields
|
||||
@ -191,6 +195,22 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
||||
)
|
||||
return model_dict
|
||||
|
||||
@classmethod
|
||||
def dump_all_json_fields_to_str(cls, model_dict: Dict) -> Dict:
|
||||
"""
|
||||
Receives dictionary of model that is about to be saved and changes
|
||||
all json fields into strings
|
||||
|
||||
:param model_dict: dictionary of model that is about to be saved
|
||||
:type model_dict: Dict
|
||||
:return: dictionary of model that is about to be saved
|
||||
:rtype: Dict
|
||||
"""
|
||||
for key, value in model_dict.items():
|
||||
if key in cls._json_fields and not isinstance(value, str):
|
||||
model_dict[key] = json.dumps(value)
|
||||
return model_dict
|
||||
|
||||
@classmethod
|
||||
def populate_default_values(cls, new_kwargs: Dict) -> Dict:
|
||||
"""
|
||||
|
||||
@ -76,7 +76,6 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
||||
pk: Any
|
||||
__model_fields__: Dict[str, BaseField]
|
||||
__table__: sqlalchemy.Table
|
||||
__fields__: Dict[str, pydantic.fields.ModelField]
|
||||
__pydantic_model__: Type[BaseModel]
|
||||
__pkname__: str
|
||||
__tablename__: str
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import datetime
|
||||
from typing import Any, Dict, TYPE_CHECKING, Type
|
||||
from typing import Any, TYPE_CHECKING, Type
|
||||
|
||||
import sqlalchemy
|
||||
from sqlalchemy import text
|
||||
|
||||
import ormar # noqa: I100, I202
|
||||
from ormar.exceptions import QueryDefinitionError
|
||||
@ -126,7 +124,7 @@ class FilterAction(QueryAction):
|
||||
sufix = "%" if "end" not in self.operator else ""
|
||||
self.filter_value = f"{prefix}{self.filter_value}{sufix}"
|
||||
|
||||
def get_text_clause(self) -> sqlalchemy.sql.expression.TextClause:
|
||||
def get_text_clause(self) -> sqlalchemy.sql.expression.BinaryExpression:
|
||||
"""
|
||||
Escapes characters if it's required.
|
||||
Substitutes values of the models if value is a ormar Model with its pk value.
|
||||
@ -138,67 +136,20 @@ class FilterAction(QueryAction):
|
||||
if isinstance(self.filter_value, ormar.Model):
|
||||
self.filter_value = self.filter_value.pk
|
||||
|
||||
self._convert_dates_if_required()
|
||||
|
||||
op_attr = FILTER_OPERATORS[self.operator]
|
||||
if self.operator == "isnull":
|
||||
op_attr = "is_" if self.filter_value else "isnot"
|
||||
filter_value = None
|
||||
else:
|
||||
filter_value = self.filter_value
|
||||
clause = getattr(self.column, op_attr)(filter_value)
|
||||
clause = self._compile_clause(
|
||||
clause, modifiers={"escape": "\\" if self.has_escaped_character else None}
|
||||
)
|
||||
return clause
|
||||
|
||||
def _convert_dates_if_required(self) -> None:
|
||||
"""
|
||||
Converts dates, time and datetime to isoformat
|
||||
"""
|
||||
if isinstance(
|
||||
self.filter_value, (datetime.date, datetime.time, datetime.datetime)
|
||||
):
|
||||
self.filter_value = self.filter_value.isoformat()
|
||||
|
||||
if isinstance(self.filter_value, (list, tuple, set)):
|
||||
self.filter_value = [
|
||||
x.isoformat()
|
||||
if isinstance(x, (datetime.date, datetime.time, datetime.datetime))
|
||||
else x
|
||||
for x in self.filter_value
|
||||
]
|
||||
|
||||
def _compile_clause(
|
||||
self, clause: sqlalchemy.sql.expression.BinaryExpression, modifiers: Dict
|
||||
) -> sqlalchemy.sql.expression.TextClause:
|
||||
"""
|
||||
Compiles the clause to str using appropriate database dialect, replace columns
|
||||
names with aliased names and converts it back to TextClause.
|
||||
|
||||
:param clause: original not compiled clause
|
||||
:type clause: sqlalchemy.sql.elements.BinaryExpression
|
||||
:param modifiers: sqlalchemy modifiers - used only to escape chars here
|
||||
:type modifiers: Dict[str, NoneType]
|
||||
:return: compiled and escaped clause
|
||||
:rtype: sqlalchemy.sql.elements.TextClause
|
||||
"""
|
||||
for modifier, modifier_value in modifiers.items():
|
||||
clause.modifiers[modifier] = modifier_value
|
||||
|
||||
clause_text = str(
|
||||
clause.compile(
|
||||
dialect=self.target_model.Meta.database._backend._dialect,
|
||||
compile_kwargs={"literal_binds": True},
|
||||
if self.table_prefix:
|
||||
aliased_table = self.source_model.Meta.alias_manager.prefixed_table_name(
|
||||
self.table_prefix, self.column.table
|
||||
)
|
||||
)
|
||||
alias = f"{self.table_prefix}_" if self.table_prefix else ""
|
||||
aliased_name = f"{alias}{self.table.name}.{self.column.name}"
|
||||
clause_text = clause_text.replace(
|
||||
f"{self.table.name}.{self.column.name}", aliased_name
|
||||
)
|
||||
dialect_name = self.target_model.Meta.database._backend._dialect.name
|
||||
if dialect_name != "sqlite": # pragma: no cover
|
||||
clause_text = clause_text.replace("%%", "%") # remove %% in some dialects
|
||||
clause = text(clause_text)
|
||||
aliased_column = getattr(aliased_table.c, self.column.name)
|
||||
else:
|
||||
aliased_column = self.column
|
||||
clause = getattr(aliased_column, op_attr)(filter_value)
|
||||
if self.has_escaped_character:
|
||||
clause.modifiers["escape"] = "\\"
|
||||
return clause
|
||||
|
||||
@ -121,19 +121,12 @@ class FilterGroup:
|
||||
:return: complied and escaped clause
|
||||
:rtype: sqlalchemy.sql.elements.TextClause
|
||||
"""
|
||||
prefix = " NOT " if self.exclude else ""
|
||||
if self.filter_type == FilterType.AND:
|
||||
clause = sqlalchemy.text(
|
||||
f"{prefix}( "
|
||||
+ str(sqlalchemy.sql.and_(*self._get_text_clauses()))
|
||||
+ " )"
|
||||
)
|
||||
clause = sqlalchemy.sql.and_(*self._get_text_clauses()).self_group()
|
||||
else:
|
||||
clause = sqlalchemy.text(
|
||||
f"{prefix}( "
|
||||
+ str(sqlalchemy.sql.or_(*self._get_text_clauses()))
|
||||
+ " )"
|
||||
)
|
||||
clause = sqlalchemy.sql.or_(*self._get_text_clauses()).self_group()
|
||||
if self.exclude:
|
||||
clause = sqlalchemy.sql.not_(clause)
|
||||
return clause
|
||||
|
||||
|
||||
|
||||
@ -187,6 +187,7 @@ class Query:
|
||||
for order in list(self.sorted_orders.keys()):
|
||||
if order is not None and order.get_field_name_text() != pk_aliased_name:
|
||||
aliased_col = order.get_field_name_text()
|
||||
# maxes[aliased_col] = order.get_text_clause()
|
||||
maxes[aliased_col] = order.get_min_or_max()
|
||||
elif order.get_field_name_text() == pk_aliased_name:
|
||||
maxes[pk_aliased_name] = order.get_text_clause()
|
||||
|
||||
@ -18,11 +18,21 @@ import databases
|
||||
import sqlalchemy
|
||||
from sqlalchemy import bindparam
|
||||
|
||||
try:
|
||||
from sqlalchemy.engine import LegacyRow
|
||||
except ImportError: # pragma: no cover
|
||||
if TYPE_CHECKING:
|
||||
|
||||
class LegacyRow(dict): # type: ignore
|
||||
pass
|
||||
|
||||
|
||||
import ormar # noqa I100
|
||||
from ormar import MultipleMatches, NoMatch
|
||||
from ormar.exceptions import (
|
||||
ModelPersistenceError, QueryDefinitionError,
|
||||
ModelListEmptyError
|
||||
ModelPersistenceError,
|
||||
QueryDefinitionError,
|
||||
ModelListEmptyError,
|
||||
)
|
||||
from ormar.queryset import FieldAccessor, FilterQuery, SelectAction
|
||||
from ormar.queryset.actions.order_action import OrderAction
|
||||
@ -608,7 +618,9 @@ class QuerySet(Generic[T]):
|
||||
model_cls=self.model_cls, # type: ignore
|
||||
exclude_through=exclude_through,
|
||||
)
|
||||
column_map = alias_resolver.resolve_columns(columns_names=list(rows[0].keys()))
|
||||
column_map = alias_resolver.resolve_columns(
|
||||
columns_names=list(cast(LegacyRow, rows[0]).keys())
|
||||
)
|
||||
result = [
|
||||
{column_map.get(k): v for k, v in dict(x).items() if k in column_map}
|
||||
for x in rows
|
||||
@ -1052,10 +1064,8 @@ class QuerySet(Generic[T]):
|
||||
:param objects: list of ormar models already initialized and ready to save.
|
||||
:type objects: List[Model]
|
||||
"""
|
||||
ready_objects = [
|
||||
obj.prepare_model_to_save(obj.dict())
|
||||
for obj in objects
|
||||
]
|
||||
ready_objects = [obj.prepare_model_to_save(obj.dict()) for obj in objects]
|
||||
|
||||
# don't use execute_many, as in databases it's executed in a loop
|
||||
# instead of using execute_many from drivers
|
||||
expr = self.table.insert().values(ready_objects)
|
||||
@ -1109,9 +1119,9 @@ class QuerySet(Generic[T]):
|
||||
f"{self.model.__name__} has to have {pk_name} filled."
|
||||
)
|
||||
new_kwargs = obj.prepare_model_to_update(new_kwargs)
|
||||
ready_objects.append({
|
||||
"new_" + k: v for k, v in new_kwargs.items() if k in columns
|
||||
})
|
||||
ready_objects.append(
|
||||
{"new_" + k: v for k, v in new_kwargs.items() if k in columns}
|
||||
)
|
||||
|
||||
pk_column = self.model_meta.table.c.get(self.model.get_column_alias(pk_name))
|
||||
pk_column_name = self.model.get_column_alias(pk_name)
|
||||
@ -1137,4 +1147,3 @@ class QuerySet(Generic[T]):
|
||||
await cast(Type["Model"], self.model_cls).Meta.signals.post_bulk_update.send(
|
||||
sender=self.model_cls, instances=objects # type: ignore
|
||||
)
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ class AliasManager:
|
||||
def __init__(self) -> None:
|
||||
self._aliases_new: Dict[str, str] = dict()
|
||||
self._reversed_aliases: Dict[str, str] = dict()
|
||||
self._prefixed_tables: Dict[str, text] = dict()
|
||||
|
||||
def __contains__(self, item: str) -> bool:
|
||||
return self._aliases_new.__contains__(item)
|
||||
@ -77,15 +78,19 @@ class AliasManager:
|
||||
:rtype: List[text]
|
||||
"""
|
||||
alias = f"{alias}_" if alias else ""
|
||||
aliased_fields = [f"{alias}{x}" for x in fields] if fields else []
|
||||
all_columns = (
|
||||
table.columns
|
||||
if not fields
|
||||
else [col for col in table.columns if col.name in fields]
|
||||
else [
|
||||
col
|
||||
for col in table.columns
|
||||
if col.name in fields or col.name in aliased_fields
|
||||
]
|
||||
)
|
||||
return [column.label(f"{alias}{column.name}") for column in all_columns]
|
||||
|
||||
@staticmethod
|
||||
def prefixed_table_name(alias: str, table: sqlalchemy.Table) -> text:
|
||||
def prefixed_table_name(self, alias: str, table: sqlalchemy.Table) -> text:
|
||||
"""
|
||||
Creates text clause with table name with aliased name.
|
||||
|
||||
@ -96,7 +101,9 @@ class AliasManager:
|
||||
:return: sqlalchemy text clause as "table_name aliased_name"
|
||||
:rtype: sqlalchemy text clause
|
||||
"""
|
||||
return table.alias(f"{alias}_{table.name}")
|
||||
full_alias = f"{alias}_{table.name}"
|
||||
key = f"{full_alias}_{id(table)}"
|
||||
return self._prefixed_tables.setdefault(key, table.alias(full_alias))
|
||||
|
||||
def add_relation_type(
|
||||
self, source_model: Type["Model"], relation_name: str, reverse_name: str = None
|
||||
|
||||
@ -77,7 +77,8 @@ class Signal:
|
||||
"""
|
||||
new_receiver_key = make_id(receiver)
|
||||
receiver_func: Union[Callable, None] = self._receivers.pop(
|
||||
new_receiver_key, None)
|
||||
new_receiver_key, None
|
||||
)
|
||||
return True if receiver_func is not None else False
|
||||
|
||||
async def send(self, sender: Type["Model"], **kwargs: Any) -> None:
|
||||
@ -100,6 +101,7 @@ class SignalEmitter(dict):
|
||||
Emitter that registers the signals in internal dictionary.
|
||||
If signal with given name does not exist it's auto added on access.
|
||||
"""
|
||||
|
||||
def __getattr__(self, item: str) -> Signal:
|
||||
return self.setdefault(item, Signal())
|
||||
|
||||
|
||||
141
poetry.lock
generated
141
poetry.lock
generated
@ -303,7 +303,7 @@ test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pr
|
||||
|
||||
[[package]]
|
||||
name = "databases"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
description = "Async database support for Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -315,6 +315,7 @@ sqlalchemy = ">=1.4,<1.5"
|
||||
|
||||
[package.extras]
|
||||
mysql = ["aiomysql"]
|
||||
mysql_asyncmy = ["asyncmy"]
|
||||
postgresql = ["asyncpg"]
|
||||
postgresql_aiopg = ["aiopg"]
|
||||
sqlite = ["aiosqlite"]
|
||||
@ -1051,7 +1052,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "1.8.2"
|
||||
version = "1.9.0"
|
||||
description = "Data validation and settings management using python 3.6 type hinting"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -1264,7 +1265,7 @@ contextvars = {version = ">=2.1", markers = "python_version < \"3.7\""}
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "1.4.28"
|
||||
version = "1.4.29"
|
||||
description = "Database Abstraction Library"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -1556,7 +1557,7 @@ sqlite = []
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.6.2"
|
||||
content-hash = "878132e71b738d73cb9345d2ed0892e1f3105df8ef0e50392b38096268b9f837"
|
||||
content-hash = "7f70457628e806c602066d934eeac8f5550e53107b31906a5ec3a20bca9dbbda"
|
||||
|
||||
[metadata.files]
|
||||
aiocontextvars = [
|
||||
@ -1790,8 +1791,8 @@ cryptography = [
|
||||
{file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"},
|
||||
]
|
||||
databases = [
|
||||
{file = "databases-0.5.3-py3-none-any.whl", hash = "sha256:23862bd96241d8fcbf97eea82995ccb3baa8415c3cb106832b7509f296322f86"},
|
||||
{file = "databases-0.5.3.tar.gz", hash = "sha256:b69d74ee0b47fa30bb6e76db0c58da998e973393259d29215d8fb29352162bd6"},
|
||||
{file = "databases-0.5.4-py3-none-any.whl", hash = "sha256:85a6b0dd92e4bc95205c08141baf1e192c8aedb2159ce03bee39bb4117cfed83"},
|
||||
{file = "databases-0.5.4.tar.gz", hash = "sha256:04a3294d053bd8d9f4162fc4975ab11a3e9ad01ae37992adce84440725957fec"},
|
||||
]
|
||||
"databind.core" = [
|
||||
{file = "databind.core-1.3.2-py3-none-any.whl", hash = "sha256:e4cb849c730e651ddc6bd13e71066b7d87037251d7426366b231f6b7e51212c1"},
|
||||
@ -2282,28 +2283,41 @@ pycparser = [
|
||||
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
|
||||
]
|
||||
pydantic = [
|
||||
{file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
|
||||
{file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
|
||||
{file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
|
||||
{file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
|
||||
{file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
|
||||
{file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
|
||||
{file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
|
||||
{file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
|
||||
{file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
|
||||
{file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
|
||||
{file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
|
||||
{file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
|
||||
{file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
|
||||
{file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
|
||||
{file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
|
||||
{file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
|
||||
{file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
|
||||
{file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
|
||||
{file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
|
||||
{file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
|
||||
{file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
|
||||
{file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c"},
|
||||
{file = "pydantic-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed"},
|
||||
{file = "pydantic-1.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6"},
|
||||
{file = "pydantic-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034"},
|
||||
{file = "pydantic-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721"},
|
||||
{file = "pydantic-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16"},
|
||||
{file = "pydantic-1.9.0-py3-none-any.whl", hash = "sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3"},
|
||||
{file = "pydantic-1.9.0.tar.gz", hash = "sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a"},
|
||||
]
|
||||
pydoc-markdown = [
|
||||
{file = "pydoc-markdown-4.5.0.tar.gz", hash = "sha256:131636ed32324d255816e476d72eb592542f120fce0d9a4ddca888934bc51282"},
|
||||
@ -2397,41 +2411,42 @@ sniffio = [
|
||||
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
|
||||
]
|
||||
sqlalchemy = [
|
||||
{file = "SQLAlchemy-1.4.28-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:e659f256b7d402338563913bdeba53bf1eadd4c09e6f6dc93cc47938f7962a8f"},
|
||||
{file = "SQLAlchemy-1.4.28-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:38df997ffa9007e953ad574f2263f61b9b683fd63ae397480ea4960be9bda0fd"},
|
||||
{file = "SQLAlchemy-1.4.28-cp27-cp27m-win_amd64.whl", hash = "sha256:6dd6fa51cf08d9433d28802228d2204e175324f1a284c4492e4af2dd36a2d485"},
|
||||
{file = "SQLAlchemy-1.4.28-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bb2d8530b7cc94b7fd9341843c3e49b6db48ea22313a8db9df21c41615b5e7b1"},
|
||||
{file = "SQLAlchemy-1.4.28-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:3b64f5d1c1d0e5f2ed4aa66f2b65ff6bdcdf4c5cc83b71c4bbf69695b09e9e19"},
|
||||
{file = "SQLAlchemy-1.4.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c02991e22ddce134ef1093ef5a9d5de448fc87b91432e4f879826e93cd1c7"},
|
||||
{file = "SQLAlchemy-1.4.28-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:387365c157e96eceacdd6c5468815ad05a523ba778680de4c8139a029e1fe044"},
|
||||
{file = "SQLAlchemy-1.4.28-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5639800f1cfe751569af2242041b30a08a6c0b9e5d95ed674ec8082d381eff13"},
|
||||
{file = "SQLAlchemy-1.4.28-cp310-cp310-win32.whl", hash = "sha256:261fcb3ff8c59e17ec44f9e61713a44ceaa97ae816da978d5cd1dc2c36f32478"},
|
||||
{file = "SQLAlchemy-1.4.28-cp310-cp310-win_amd64.whl", hash = "sha256:29d10796e5604ab7bc067eda7231a2d2411a51eda43082673641245a49d1c4bb"},
|
||||
{file = "SQLAlchemy-1.4.28-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:4490b10f83cd56ca2cdcd94b140d89911ac331e42a727b79157963b1b04fdd0c"},
|
||||
{file = "SQLAlchemy-1.4.28-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83ee7f6fa5faed23996c67044376d46815f65183ad6d744d94d68b18cdef060b"},
|
||||
{file = "SQLAlchemy-1.4.28-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f667a947378bcb12a371ab38bed1b708f3a682d1ba30176422652082919285a2"},
|
||||
{file = "SQLAlchemy-1.4.28-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61965abc63c8b54038574698888e91a126753a4bdc0ec001397acb14501834e0"},
|
||||
{file = "SQLAlchemy-1.4.28-cp36-cp36m-win32.whl", hash = "sha256:41a02030f8934b0de843341e7014192a0c16ee2726a06da154c81153fbe56b33"},
|
||||
{file = "SQLAlchemy-1.4.28-cp36-cp36m-win_amd64.whl", hash = "sha256:c3497cd63c5f90112b8882ea4dd694052166f779ce9055cd5c4305e0b76d72d9"},
|
||||
{file = "SQLAlchemy-1.4.28-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:5d91dce14ac3347bce301062ca825e7fb7e15c133f3909f15989e94878b1082f"},
|
||||
{file = "SQLAlchemy-1.4.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08e39d65b38d4c3f77c4c9bf090b0ba4ec5721a6e0a74b63d2a9781cdcacf142"},
|
||||
{file = "SQLAlchemy-1.4.28-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c85ead1d17acc5e8b282c578394dba253728bcbcbeb66e4ef0e25f4bab53935a"},
|
||||
{file = "SQLAlchemy-1.4.28-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:daddcd6ba1706cc5fcc9cfaa913aa4bf331172dc7efd385fe3ee1feae3b513bc"},
|
||||
{file = "SQLAlchemy-1.4.28-cp37-cp37m-win32.whl", hash = "sha256:ce4f2b34378561bc2e42635888fe86efe13d104ba1d95b5ca67b4d60d8e53e67"},
|
||||
{file = "SQLAlchemy-1.4.28-cp37-cp37m-win_amd64.whl", hash = "sha256:4999b03daa6c9afb9a0bf9e3b8769128ef1880557dacfca86fa7562920c49f6b"},
|
||||
{file = "SQLAlchemy-1.4.28-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:dd041324328cece3ccdf70cfbd71b5ab968e564a22318ffd88b054f5eadeb9be"},
|
||||
{file = "SQLAlchemy-1.4.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf2c1d64c4ee0f30e08e1844ff0acf3c1b6c4277c0e89ec3e8bf1722d245b108"},
|
||||
{file = "SQLAlchemy-1.4.28-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:525e962af8f25fc24ce019e6f237d49f8720d757a8a56c9b4caa2d91e2c66111"},
|
||||
{file = "SQLAlchemy-1.4.28-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b72744fed32ecf2bf786d2e2f6756c04126c323ba939f47177b9722775626889"},
|
||||
{file = "SQLAlchemy-1.4.28-cp38-cp38-win32.whl", hash = "sha256:b5541355b8d4970753d4f7292f73a320704b20406e06cd29b469d156f0a484d8"},
|
||||
{file = "SQLAlchemy-1.4.28-cp38-cp38-win_amd64.whl", hash = "sha256:cf3a3c2f32d53a4166b2eb8de35f93bcb640e51c32033024af500017d8e8a8c9"},
|
||||
{file = "SQLAlchemy-1.4.28-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:dfa093bd8ecfceafff62078910178567323005e44fbe4d7933e6cbce4512cea2"},
|
||||
{file = "SQLAlchemy-1.4.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:555d56b71f61b4c9fa55fe203fe6e1e561c9385fa97c5849783ae050a89113af"},
|
||||
{file = "SQLAlchemy-1.4.28-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c90b21360cf14d33c8a004f991aa336c7906a8db825d4ec38722c5ff1c47dada"},
|
||||
{file = "SQLAlchemy-1.4.28-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2019b332cf4f9a513133fdf056dc4cecec7fbae7016ebc574d0f310103eed7ee"},
|
||||
{file = "SQLAlchemy-1.4.28-cp39-cp39-win32.whl", hash = "sha256:ca500f30619daf863ab1c66d57d53a0987361a8f3266454290198aabd18f2599"},
|
||||
{file = "SQLAlchemy-1.4.28-cp39-cp39-win_amd64.whl", hash = "sha256:853de08e881dae0305647dd61b4429758f11d1bf02a9faf02793cad44bb2e0d5"},
|
||||
{file = "SQLAlchemy-1.4.28.tar.gz", hash = "sha256:7fdb7b775fb0739d3e71461509f978beb788935bc0aa9e47df14837cb33e5226"},
|
||||
{file = "SQLAlchemy-1.4.29-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:da64423c05256f4ab8c0058b90202053b201cbe3a081f3a43eb590cd554395ab"},
|
||||
{file = "SQLAlchemy-1.4.29-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0fc4eec2f46b40bdd42112b3be3fbbf88e194bcf02950fbb88bcdc1b32f07dc7"},
|
||||
{file = "SQLAlchemy-1.4.29-cp27-cp27m-win32.whl", hash = "sha256:101d2e100ba9182c9039699588e0b2d833c54b3bad46c67c192159876c9f27ea"},
|
||||
{file = "SQLAlchemy-1.4.29-cp27-cp27m-win_amd64.whl", hash = "sha256:ceac84dd9abbbe115e8be0c817bed85d9fa639b4d294e7817f9e61162d5f766c"},
|
||||
{file = "SQLAlchemy-1.4.29-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:15b65887b6c324cad638c7671cb95985817b733242a7eb69edd7cdf6953be1e0"},
|
||||
{file = "SQLAlchemy-1.4.29-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:78abc507d17753ed434b6cc0c0693126279723d5656d9775bfcac966a99a899b"},
|
||||
{file = "SQLAlchemy-1.4.29-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb8c993706e86178ce15a6b86a335a2064f52254b640e7f53365e716423d33f4"},
|
||||
{file = "SQLAlchemy-1.4.29-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:804e22d5b6165a4f3f019dd9c94bec5687de985a9c54286b93ded9f7846b8c82"},
|
||||
{file = "SQLAlchemy-1.4.29-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56d9d62021946263d4478c9ca012fbd1805f10994cb615c88e7bfd1ae14604d8"},
|
||||
{file = "SQLAlchemy-1.4.29-cp310-cp310-win32.whl", hash = "sha256:027f356c727db24f3c75828c7feb426f87ce1241242d08958e454bd025810660"},
|
||||
{file = "SQLAlchemy-1.4.29-cp310-cp310-win_amd64.whl", hash = "sha256:debaf09a823061f88a8dee04949814cf7e82fb394c5bca22c780cb03172ca23b"},
|
||||
{file = "SQLAlchemy-1.4.29-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:dc27dcc6c72eb38be7f144e9c2c4372d35a3684d3a6dd43bd98c1238358ee17c"},
|
||||
{file = "SQLAlchemy-1.4.29-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4ddd4f2e247128c58bb3dd4489922874afce157d2cff0b2295d67fcd0f22494"},
|
||||
{file = "SQLAlchemy-1.4.29-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9ce960a1dc60524136cf6f75621588e2508a117e04a6e3eedb0968bd13b8c824"},
|
||||
{file = "SQLAlchemy-1.4.29-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5919e647e1d4805867ea556ed4967c68b4d8b266059fa35020dbaed8ffdd60f3"},
|
||||
{file = "SQLAlchemy-1.4.29-cp36-cp36m-win32.whl", hash = "sha256:886359f734b95ad1ef443b13bb4518bcade4db4f9553c9ce33d6d04ebda8d44e"},
|
||||
{file = "SQLAlchemy-1.4.29-cp36-cp36m-win_amd64.whl", hash = "sha256:e9cc6d844e24c307c3272677982a9b33816aeb45e4977791c3bdd47637a8d810"},
|
||||
{file = "SQLAlchemy-1.4.29-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:5e9cd33459afa69c88fa648e803d1f1245e3caa60bfe8b80a9595e5edd3bda9c"},
|
||||
{file = "SQLAlchemy-1.4.29-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeaebceb24b46e884c4ad3c04f37feb178b81f6ce720af19bfa2592ca32fdef7"},
|
||||
{file = "SQLAlchemy-1.4.29-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e89347d3bd2ef873832b47e85f4bbd810a5e626c5e749d90a07638da100eb1c8"},
|
||||
{file = "SQLAlchemy-1.4.29-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a717c2e70fd1bb477161c4cc85258e41d978584fbe5522613618195f7e87d9b"},
|
||||
{file = "SQLAlchemy-1.4.29-cp37-cp37m-win32.whl", hash = "sha256:f74d6c05d2d163464adbdfbc1ab85048cc15462ff7d134b8aed22bd521e1faa5"},
|
||||
{file = "SQLAlchemy-1.4.29-cp37-cp37m-win_amd64.whl", hash = "sha256:621854dbb4d2413c759a5571564170de45ef37299df52e78e62b42e2880192e1"},
|
||||
{file = "SQLAlchemy-1.4.29-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:f3909194751bb6cb7c5511dd18bcf77e6e3f0b31604ed4004dffa9461f71e737"},
|
||||
{file = "SQLAlchemy-1.4.29-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd49d21d1f03c81fbec9080ecdc4486d5ddda67e7fbb75ebf48294465c022cdc"},
|
||||
{file = "SQLAlchemy-1.4.29-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e5f6959466a42b6569774c257e55f9cd85200d5b0ba09f0f5d8b5845349c5822"},
|
||||
{file = "SQLAlchemy-1.4.29-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0072f9887aabe66db23f818bbe950cfa1b6127c5cb769b00bcc07935b3adb0ad"},
|
||||
{file = "SQLAlchemy-1.4.29-cp38-cp38-win32.whl", hash = "sha256:ad618d687d26d4cbfa9c6fa6141d59e05bcdfc60cb6e1f1d3baa18d8c62fef5f"},
|
||||
{file = "SQLAlchemy-1.4.29-cp38-cp38-win_amd64.whl", hash = "sha256:878daecb6405e786b07f97e1c77a9cfbbbec17432e8a90c487967e32cfdecb33"},
|
||||
{file = "SQLAlchemy-1.4.29-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:e027bdf0a4cf6bd0a3ad3b998643ea374d7991bd117b90bf9982e41ceb742941"},
|
||||
{file = "SQLAlchemy-1.4.29-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5de7adfb91d351f44062b8dedf29f49d4af7cb765be65816e79223a4e31062b"},
|
||||
{file = "SQLAlchemy-1.4.29-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fbc6e63e481fa323036f305ada96a3362e1d60dd2bfa026cac10c3553e6880e9"},
|
||||
{file = "SQLAlchemy-1.4.29-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dd0502cb091660ad0d89c5e95a29825f37cde2a5249957838e975871fbffaad"},
|
||||
{file = "SQLAlchemy-1.4.29-cp39-cp39-win32.whl", hash = "sha256:37b46bfc4af3dc226acb6fa28ecd2e1fd223433dc5e15a2bad62bf0a0cbb4e8b"},
|
||||
{file = "SQLAlchemy-1.4.29-cp39-cp39-win_amd64.whl", hash = "sha256:08cfd35eecaba79be930c9bfd2e1f0c67a7e1314355d83a378f9a512b1cf7587"},
|
||||
{file = "SQLAlchemy-1.4.29.tar.gz", hash = "sha256:fa2bad14e1474ba649cfc969c1d2ec915dd3e79677f346bbfe08e93ef9020b39"},
|
||||
]
|
||||
starlette = [
|
||||
{file = "starlette-0.16.0-py3-none-any.whl", hash = "sha256:38eb24bf705a2c317e15868e384c1b8a12ca396e5a3c3a003db7e667c43f939f"},
|
||||
|
||||
@ -3,7 +3,7 @@ name = "ormar"
|
||||
|
||||
[tool.poetry]
|
||||
name = "ormar"
|
||||
version = "0.10.23"
|
||||
version = "0.10.24"
|
||||
description = "A simple async ORM with fastapi in mind and pydantic validation."
|
||||
authors = ["Radosław Drążkiewicz <collerek@gmail.com>"]
|
||||
license = "MIT"
|
||||
@ -42,9 +42,9 @@ classifiers = [
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.6.2"
|
||||
databases = ">=0.3.2,<0.5.4"
|
||||
pydantic = ">=1.6.1,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<=1.8.2"
|
||||
SQLAlchemy = ">=1.3.18,<1.4.29"
|
||||
databases = ">=0.3.2,!=0.5.0,!=0.5.1,!=0.5.2,!=0.5.3,<0.5.5"
|
||||
pydantic = ">=1.6.1,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<=1.9.1"
|
||||
SQLAlchemy = ">=1.3.18,<=1.4.29"
|
||||
asyncpg = { version = ">=0.24,<0.26", optional = true }
|
||||
psycopg2-binary = { version = "^2.9.1", optional = true }
|
||||
aiomysql = { version = ">=0.0.21,<0.0.23", optional = true }
|
||||
@ -152,6 +152,10 @@ disallow_untyped_calls = false
|
||||
disallow_untyped_defs = false
|
||||
disallow_incomplete_defs = false
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "docs_src.*"
|
||||
ignore_errors = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = ["sqlalchemy.*", "asyncpg"]
|
||||
ignore_missing_imports = true
|
||||
|
||||
@ -55,6 +55,16 @@ class DateModel(ormar.Model):
|
||||
creation_date: date = ormar.Date()
|
||||
|
||||
|
||||
class MyModel(ormar.Model):
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
created_at: datetime = ormar.DateTime(timezone=True, nullable=False)
|
||||
|
||||
class Meta:
|
||||
tablename = "mymodels"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
@ -116,3 +126,18 @@ async def test_query_with_time_in_filter():
|
||||
assert len(outdated_samples) == 2
|
||||
assert outdated_samples[0] == sample2
|
||||
assert outdated_samples[1] == sample3
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_filtering_by_timezone_with_timedelta():
|
||||
async with database:
|
||||
now_utc = datetime.now(timezone.utc)
|
||||
object = MyModel(created_at=now_utc)
|
||||
await object.save()
|
||||
|
||||
one_hour_ago = datetime.now(timezone.utc) - timedelta(hours=1)
|
||||
created_since_one_hour_ago = await MyModel.objects.filter(
|
||||
created_at__gte=one_hour_ago
|
||||
).all()
|
||||
|
||||
assert len(created_since_one_hour_ago) == 1
|
||||
|
||||
@ -142,16 +142,16 @@ def test_combining_groups_together():
|
||||
group = (Product.name == "Test") & (Product.rating >= 3.0)
|
||||
group.resolve(model_cls=Product)
|
||||
assert len(group._nested_groups) == 2
|
||||
assert str(group.get_text_clause()) == (
|
||||
"( ( product.name = 'Test' ) AND" " ( product.rating >= 3.0 ) )"
|
||||
)
|
||||
assert str(
|
||||
group.get_text_clause().compile(compile_kwargs={"literal_binds": True})
|
||||
) == ("((product.name = 'Test') AND (product.rating >= 3.0))")
|
||||
|
||||
group = ~((Product.name == "Test") & (Product.rating >= 3.0))
|
||||
group.resolve(model_cls=Product)
|
||||
assert len(group._nested_groups) == 2
|
||||
assert str(group.get_text_clause()) == (
|
||||
" NOT ( ( product.name = 'Test' ) AND" " ( product.rating >= 3.0 ) )"
|
||||
)
|
||||
assert str(
|
||||
group.get_text_clause().compile(compile_kwargs={"literal_binds": True})
|
||||
) == ("NOT ((product.name = 'Test') AND" " (product.rating >= 3.0))")
|
||||
|
||||
group = ((Product.name == "Test") & (Product.rating >= 3.0)) | (
|
||||
Product.category.name << (["Toys", "Books"])
|
||||
@ -159,11 +159,13 @@ def test_combining_groups_together():
|
||||
group.resolve(model_cls=Product)
|
||||
assert len(group._nested_groups) == 2
|
||||
assert len(group._nested_groups[0]._nested_groups) == 2
|
||||
group_str = str(group.get_text_clause())
|
||||
group_str = str(
|
||||
group.get_text_clause().compile(compile_kwargs={"literal_binds": True})
|
||||
)
|
||||
category_prefix = group._nested_groups[1].actions[0].table_prefix
|
||||
assert group_str == (
|
||||
"( ( ( product.name = 'Test' ) AND ( product.rating >= 3.0 ) ) "
|
||||
f"OR ( {category_prefix}_categories.name IN ('Toys', 'Books') ) )"
|
||||
"(((product.name = 'Test') AND (product.rating >= 3.0)) "
|
||||
f"OR ({category_prefix}_categories.name IN ('Toys', 'Books')))"
|
||||
)
|
||||
|
||||
group = (Product.name % "Test") | (
|
||||
@ -173,15 +175,17 @@ def test_combining_groups_together():
|
||||
group.resolve(model_cls=Product)
|
||||
assert len(group._nested_groups) == 2
|
||||
assert len(group._nested_groups[1]._nested_groups) == 2
|
||||
group_str = str(group.get_text_clause())
|
||||
group_str = str(
|
||||
group.get_text_clause().compile(compile_kwargs={"literal_binds": True})
|
||||
)
|
||||
price_list_prefix = (
|
||||
group._nested_groups[1]._nested_groups[0].actions[0].table_prefix
|
||||
)
|
||||
category_prefix = group._nested_groups[1]._nested_groups[1].actions[0].table_prefix
|
||||
assert group_str == (
|
||||
f"( ( product.name LIKE '%Test%' ) "
|
||||
f"OR ( ( {price_list_prefix}_price_lists.name LIKE 'Aa%' ) "
|
||||
f"OR ( {category_prefix}_categories.name IN ('Toys', 'Books') ) ) )"
|
||||
f"((product.name LIKE '%Test%') "
|
||||
f"OR (({price_list_prefix}_price_lists.name LIKE 'Aa%') "
|
||||
f"OR ({category_prefix}_categories.name IN ('Toys', 'Books'))))"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -40,9 +40,10 @@ def test_or_group():
|
||||
assert result.actions[0].target_model == Author
|
||||
assert result.actions[1].target_model == Book
|
||||
assert (
|
||||
str(result.get_text_clause()) == f"( authors.name = 'aa' OR "
|
||||
str(result.get_text_clause().compile(compile_kwargs={"literal_binds": True}))
|
||||
== f"(authors.name = 'aa' OR "
|
||||
f"{result.actions[1].table_prefix}"
|
||||
f"_books.title = 'bb' )"
|
||||
f"_books.title = 'bb')"
|
||||
)
|
||||
|
||||
|
||||
@ -53,9 +54,10 @@ def test_and_group():
|
||||
assert result.actions[0].target_model == Author
|
||||
assert result.actions[1].target_model == Book
|
||||
assert (
|
||||
str(result.get_text_clause()) == f"( authors.name = 'aa' AND "
|
||||
str(result.get_text_clause().compile(compile_kwargs={"literal_binds": True}))
|
||||
== f"(authors.name = 'aa' AND "
|
||||
f"{result.actions[1].table_prefix}"
|
||||
f"_books.title = 'bb' )"
|
||||
f"_books.title = 'bb')"
|
||||
)
|
||||
|
||||
|
||||
@ -68,12 +70,13 @@ def test_nested_and():
|
||||
assert len(result._nested_groups) == 2
|
||||
book_prefix = result._nested_groups[0].actions[1].table_prefix
|
||||
assert (
|
||||
str(result.get_text_clause()) == f"( ( authors.name = 'aa' OR "
|
||||
str(result.get_text_clause().compile(compile_kwargs={"literal_binds": True}))
|
||||
== f"((authors.name = 'aa' OR "
|
||||
f"{book_prefix}"
|
||||
f"_books.title = 'bb' ) AND "
|
||||
f"( authors.name = 'cc' OR "
|
||||
f"_books.title = 'bb') AND "
|
||||
f"(authors.name = 'cc' OR "
|
||||
f"{book_prefix}"
|
||||
f"_books.title = 'dd' ) )"
|
||||
f"_books.title = 'dd'))"
|
||||
)
|
||||
|
||||
|
||||
@ -84,11 +87,12 @@ def test_nested_group_and_action():
|
||||
assert len(result._nested_groups) == 1
|
||||
book_prefix = result._nested_groups[0].actions[1].table_prefix
|
||||
assert (
|
||||
str(result.get_text_clause()) == f"( ( authors.name = 'aa' OR "
|
||||
str(result.get_text_clause().compile(compile_kwargs={"literal_binds": True}))
|
||||
== f"((authors.name = 'aa' OR "
|
||||
f"{book_prefix}"
|
||||
f"_books.title = 'bb' ) AND "
|
||||
f"_books.title = 'bb') AND "
|
||||
f"{book_prefix}"
|
||||
f"_books.title = 'dd' )"
|
||||
f"_books.title = 'dd')"
|
||||
)
|
||||
|
||||
|
||||
@ -108,12 +112,14 @@ def test_deeply_nested_or():
|
||||
assert len(result._nested_groups) == 2
|
||||
assert len(result._nested_groups[0]._nested_groups) == 2
|
||||
book_prefix = result._nested_groups[0]._nested_groups[0].actions[1].table_prefix
|
||||
result_qry = str(result.get_text_clause())
|
||||
result_qry = str(
|
||||
result.get_text_clause().compile(compile_kwargs={"literal_binds": True})
|
||||
)
|
||||
expected_qry = (
|
||||
f"( ( ( authors.name = 'aa' OR {book_prefix}_books.title = 'bb' ) AND "
|
||||
f"( authors.name = 'cc' OR {book_prefix}_books.title = 'dd' ) ) "
|
||||
f"OR ( ( {book_prefix}_books.year < 1900 OR {book_prefix}_books.title = '11' ) AND "
|
||||
f"( {book_prefix}_books.year > 'xx' OR {book_prefix}_books.title = '22' ) ) )"
|
||||
f"(((authors.name = 'aa' OR {book_prefix}_books.title = 'bb') AND "
|
||||
f"(authors.name = 'cc' OR {book_prefix}_books.title = 'dd')) "
|
||||
f"OR (({book_prefix}_books.year < 1900 OR {book_prefix}_books.title = '11') AND"
|
||||
f" ({book_prefix}_books.year > 'xx' OR {book_prefix}_books.title = '22')))"
|
||||
)
|
||||
assert result_qry.replace("\n", "") == expected_qry.replace("\n", "")
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ class DataSourceTableColumn(ormar.Model):
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
def create_test_database(): # pragma: no cover
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.drop_all(engine)
|
||||
metadata.create_all(engine)
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
|
||||
import databases
|
||||
import pydantic
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
|
||||
import ormar
|
||||
from ormar.exceptions import (
|
||||
ModelPersistenceError, QueryDefinitionError,
|
||||
ModelListEmptyError
|
||||
ModelPersistenceError,
|
||||
QueryDefinitionError,
|
||||
ModelListEmptyError,
|
||||
)
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
@ -63,6 +65,17 @@ class Note(ormar.Model):
|
||||
category: Optional[Category] = ormar.ForeignKey(Category)
|
||||
|
||||
|
||||
class ItemConfig(ormar.Model):
|
||||
class Meta:
|
||||
metadata = metadata
|
||||
database = database
|
||||
tablename = "item_config"
|
||||
|
||||
id: Optional[int] = ormar.Integer(primary_key=True)
|
||||
item_id: str = ormar.String(max_length=32, index=True)
|
||||
pairs: pydantic.Json = ormar.JSON(default=["2", "3"])
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
@ -315,3 +328,23 @@ async def test_bulk_update_not_saved_objts():
|
||||
|
||||
with pytest.raises(ModelListEmptyError):
|
||||
await Note.objects.bulk_update([])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bulk_operations_with_json():
|
||||
async with database:
|
||||
items = [
|
||||
ItemConfig(item_id="test1"),
|
||||
ItemConfig(item_id="test2"),
|
||||
ItemConfig(item_id="test3"),
|
||||
]
|
||||
await ItemConfig.objects.bulk_create(items)
|
||||
items = await ItemConfig.objects.all()
|
||||
assert all(x.pairs == ["2", "3"] for x in items)
|
||||
|
||||
for item in items:
|
||||
item.pairs = ["1"]
|
||||
|
||||
await ItemConfig.objects.bulk_update(items)
|
||||
items = await ItemConfig.objects.all()
|
||||
assert all(x.pairs == ["1"] for x in items)
|
||||
|
||||
@ -7,8 +7,13 @@ import sqlalchemy
|
||||
|
||||
import ormar
|
||||
from ormar import (
|
||||
post_bulk_update, post_delete, post_save, post_update,
|
||||
pre_delete, pre_save, pre_update
|
||||
post_bulk_update,
|
||||
post_delete,
|
||||
post_save,
|
||||
post_update,
|
||||
pre_delete,
|
||||
pre_save,
|
||||
pre_update,
|
||||
)
|
||||
from ormar.signals import SignalEmitter
|
||||
from ormar.exceptions import SignalDefinitionError
|
||||
@ -202,7 +207,9 @@ async def test_signal_functions(cleanup):
|
||||
|
||||
await Album.objects.bulk_update(albums)
|
||||
|
||||
cnt = await AuditLog.objects.filter(event_type__contains="BULK_POST").count()
|
||||
cnt = await AuditLog.objects.filter(
|
||||
event_type__contains="BULK_POST"
|
||||
).count()
|
||||
assert cnt == len(albums)
|
||||
|
||||
album.signals.bulk_post_update.disconnect(after_bulk_update)
|
||||
|
||||
Reference in New Issue
Block a user