use existing encode_json to avoid code duplication, rename queryset customization param and move it to Meta, move docs to models from inheritance
This commit is contained in:
2
.flake8
2
.flake8
@ -1,5 +1,5 @@
|
||||
[flake8]
|
||||
ignore = ANN101, ANN102, W503, S101, CFQ004
|
||||
ignore = ANN101, ANN102, W503, S101, CFQ004, S311
|
||||
max-complexity = 8
|
||||
max-line-length = 88
|
||||
import-order-style = pycharm
|
||||
|
||||
@ -266,6 +266,44 @@ But for now you cannot change the ManyToMany column names as they go through oth
|
||||
--8<-- "../docs_src/models/docs010.py"
|
||||
```
|
||||
|
||||
## Overwriting the default QuerySet
|
||||
|
||||
If you want to customize the queries run by ormar you can define your own queryset class (that extends the ormar `QuerySet`) in your model class, default one is simply the `QuerySet`
|
||||
|
||||
You can provide a new class in `Meta` configuration of your class as `queryset_class` parameter.
|
||||
|
||||
```python
|
||||
import ormar
|
||||
from ormar.queryset.queryset import QuerySet
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
||||
class MyQuerySetClass(QuerySet):
|
||||
|
||||
async def first_or_404(self, *args, **kwargs):
|
||||
entity = await self.get_or_none(*args, **kwargs)
|
||||
if entity is None:
|
||||
# in fastapi or starlette
|
||||
raise HTTPException(404)
|
||||
|
||||
|
||||
class Book(ormar.Model):
|
||||
|
||||
class Meta(ormar.ModelMeta):
|
||||
metadata = metadata
|
||||
database = database
|
||||
tablename = "book"
|
||||
queryset_class = MyQuerySetClass
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=32)
|
||||
|
||||
|
||||
# when book not found, raise `404` in your view.
|
||||
book = await Book.objects.first_or_404(name="123")
|
||||
|
||||
```
|
||||
|
||||
### Type Hints & Legacy
|
||||
|
||||
Before version 0.4.0 `ormar` supported only one way of defining `Fields` on a `Model` using python type hints as pydantic.
|
||||
|
||||
@ -571,37 +571,3 @@ class Category(CreateDateFieldsModel, AuditCreateModel):
|
||||
```
|
||||
|
||||
That way you can inherit from both create and update classes if needed, and only one of them otherwise.
|
||||
|
||||
## __queryset_cls__
|
||||
|
||||
You can define your own queryset_class(extends the `Queryset`) in your model class, default is `QuerySet`
|
||||
|
||||
```python
|
||||
import ormar
|
||||
from ormar.queryset.queryset import QuerySet
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
||||
class MyQuerySetClass(QuerySet):
|
||||
|
||||
async def first_or_404(self, *args, **kwargs):
|
||||
entity = await self.get_or_none(*args, **kwargs)
|
||||
if entity is None:
|
||||
# in fastapi or starlette
|
||||
raise HTTPException(404)
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta:
|
||||
metadata = metadata
|
||||
database = database
|
||||
tablename = "book"
|
||||
|
||||
__queryset_cls__ = MyQuerySetClass
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=32)
|
||||
|
||||
# when book not found, raise `404` in your view.
|
||||
book = await Book.objects.first_or_404(name="123")
|
||||
|
||||
```
|
||||
|
||||
@ -25,12 +25,12 @@ except ImportError: # pragma: no cover
|
||||
from importlib_metadata import version # type: ignore
|
||||
from ormar.protocols import QuerySetProtocol, RelationProtocol # noqa: I100
|
||||
from ormar.decorators import ( # noqa: I100
|
||||
post_bulk_update,
|
||||
post_delete,
|
||||
post_relation_add,
|
||||
post_relation_remove,
|
||||
post_save,
|
||||
post_update,
|
||||
post_bulk_update,
|
||||
pre_delete,
|
||||
pre_relation_add,
|
||||
pre_relation_remove,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from typing import Callable, List, Type, TYPE_CHECKING, Union
|
||||
from typing import Callable, List, TYPE_CHECKING, Type, Union
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ormar import Model
|
||||
|
||||
@ -1,12 +1,7 @@
|
||||
import base64
|
||||
from typing import Any, TYPE_CHECKING, Type
|
||||
|
||||
from ormar.queryset.utils import to_str
|
||||
|
||||
try:
|
||||
import orjson as json
|
||||
except ImportError: # pragma: no cover
|
||||
import json # type: ignore
|
||||
from ormar.fields.parsers import encode_json
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ormar import Model
|
||||
@ -42,9 +37,7 @@ class JsonDescriptor:
|
||||
return value
|
||||
|
||||
def __set__(self, instance: "Model", value: Any) -> None:
|
||||
if not isinstance(value, str):
|
||||
value = json.dumps(value)
|
||||
value = to_str(value)
|
||||
value = encode_json(value)
|
||||
instance._internal_set(self.name, value)
|
||||
instance.set_save_status(False)
|
||||
|
||||
|
||||
@ -47,18 +47,18 @@ def populate_default_options_values( # noqa: CCR001
|
||||
:param model_fields: dict of model fields
|
||||
:type model_fields: Union[Dict[str, type], Dict]
|
||||
"""
|
||||
if not hasattr(new_model.Meta, "constraints"):
|
||||
new_model.Meta.constraints = []
|
||||
if not hasattr(new_model.Meta, "model_fields"):
|
||||
new_model.Meta.model_fields = model_fields
|
||||
if not hasattr(new_model.Meta, "abstract"):
|
||||
new_model.Meta.abstract = False
|
||||
if not hasattr(new_model.Meta, "extra"):
|
||||
new_model.Meta.extra = Extra.forbid
|
||||
if not hasattr(new_model.Meta, "orders_by"):
|
||||
new_model.Meta.orders_by = []
|
||||
if not hasattr(new_model.Meta, "exclude_parent_fields"):
|
||||
new_model.Meta.exclude_parent_fields = []
|
||||
defaults = {
|
||||
"queryset_class": ormar.QuerySet,
|
||||
"constraints": [],
|
||||
"model_fields": model_fields,
|
||||
"abstract": False,
|
||||
"extra": Extra.forbid,
|
||||
"orders_by": [],
|
||||
"exclude_parent_fields": [],
|
||||
}
|
||||
for key, value in defaults.items():
|
||||
if not hasattr(new_model.Meta, key):
|
||||
setattr(new_model.Meta, key, value)
|
||||
|
||||
if any(
|
||||
is_field_an_forward_ref(field) for field in new_model.Meta.model_fields.values()
|
||||
|
||||
@ -85,6 +85,7 @@ class ModelMeta:
|
||||
orders_by: List[str]
|
||||
exclude_parent_fields: List[str]
|
||||
extra: Extra
|
||||
queryset_class: Type[QuerySet]
|
||||
|
||||
|
||||
def add_cached_properties(new_model: Type["Model"]) -> None:
|
||||
@ -614,8 +615,6 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
|
||||
|
||||
return new_model
|
||||
|
||||
__queryset_cls__ = QuerySet
|
||||
|
||||
@property
|
||||
def objects(cls: Type["T"]) -> "QuerySet[T]": # type: ignore
|
||||
if cls.Meta.requires_ref_update:
|
||||
@ -624,7 +623,7 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
|
||||
f"ForwardRefs. \nBefore using the model you "
|
||||
f"need to call update_forward_refs()."
|
||||
)
|
||||
return cls.__queryset_cls__(model_cls=cls)
|
||||
return cls.Meta.queryset_class(model_cls=cls)
|
||||
|
||||
def __getattr__(self, item: str) -> Any:
|
||||
"""
|
||||
|
||||
@ -12,18 +12,13 @@ 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
|
||||
from ormar.exceptions import ModelPersistenceError
|
||||
from ormar.fields.parsers import encode_json
|
||||
from ormar.models.mixins import AliasMixin
|
||||
from ormar.models.mixins.relation_mixin import RelationMixin
|
||||
from ormar.queryset.utils import to_str
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ormar import ForeignKeyField, Model
|
||||
@ -208,8 +203,8 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
||||
:rtype: Dict
|
||||
"""
|
||||
for key, value in model_dict.items():
|
||||
if key in cls._json_fields and not isinstance(value, str):
|
||||
model_dict[key] = to_str(json.dumps(value))
|
||||
if key in cls._json_fields:
|
||||
model_dict[key] = encode_json(value)
|
||||
return model_dict
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -30,9 +30,9 @@ except ImportError: # pragma: no cover
|
||||
import ormar # noqa I100
|
||||
from ormar import MultipleMatches, NoMatch
|
||||
from ormar.exceptions import (
|
||||
ModelListEmptyError,
|
||||
ModelPersistenceError,
|
||||
QueryDefinitionError,
|
||||
ModelListEmptyError,
|
||||
)
|
||||
from ormar.queryset import FieldAccessor, FilterQuery, SelectAction
|
||||
from ormar.queryset.actions.order_action import OrderAction
|
||||
|
||||
@ -17,15 +17,6 @@ if TYPE_CHECKING: # pragma no cover
|
||||
from ormar import Model, BaseField
|
||||
|
||||
|
||||
def to_str(val: Union[bytes, str, int]) -> str:
|
||||
""" convert bytes to str simply """
|
||||
if isinstance(val, bytes):
|
||||
return val.decode("utf-8")
|
||||
elif isinstance(val, str):
|
||||
return val
|
||||
return str(val)
|
||||
|
||||
|
||||
def check_node_not_dict_or_not_last_node(
|
||||
part: str, is_last: bool, current_level: Any
|
||||
) -> bool:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import asyncio
|
||||
import inspect
|
||||
from typing import Any, Callable, Dict, Tuple, Type, TYPE_CHECKING, Union
|
||||
from typing import Any, Callable, Dict, TYPE_CHECKING, Tuple, Type, Union
|
||||
|
||||
from ormar.exceptions import SignalDefinitionError
|
||||
|
||||
|
||||
@ -78,14 +78,7 @@ class ItemConfig(ormar.Model):
|
||||
pairs: pydantic.Json = ormar.JSON(default=["2", "3"])
|
||||
|
||||
|
||||
class Customer(ormar.Model):
|
||||
class Meta:
|
||||
metadata = metadata
|
||||
database = database
|
||||
tablename = "customer"
|
||||
|
||||
class QuerySetCls(QuerySet):
|
||||
|
||||
class QuerySetCls(QuerySet):
|
||||
async def first_or_404(self, *args, **kwargs):
|
||||
entity = await self.get_or_none(*args, **kwargs)
|
||||
if not entity:
|
||||
@ -93,7 +86,13 @@ class Customer(ormar.Model):
|
||||
raise ValueError("customer not found")
|
||||
return entity
|
||||
|
||||
__queryset_cls__ = QuerySetCls
|
||||
|
||||
class Customer(ormar.Model):
|
||||
class Meta:
|
||||
metadata = metadata
|
||||
database = database
|
||||
tablename = "customer"
|
||||
queryset_class = QuerySetCls
|
||||
|
||||
id: Optional[int] = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=32)
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import orjson
|
||||
import json
|
||||
|
||||
from ormar.queryset.utils import to_str
|
||||
|
||||
|
||||
def test_to_str():
|
||||
expected_str = "[]"
|
||||
val = orjson.dumps([])
|
||||
assert expected_str == to_str(val)
|
||||
|
||||
val = json.dumps([])
|
||||
assert expected_str == to_str(val)
|
||||
|
||||
expected_bytes = expected_str.encode()
|
||||
assert isinstance(expected_bytes, bytes)
|
||||
|
||||
assert isinstance(to_str(expected_bytes), str)
|
||||
assert "1" == to_str(1)
|
||||
Reference in New Issue
Block a user