Merge pull request #286 from naturalethic/allow-custom-model-config
Allow custom model config
This commit is contained in:
@ -373,6 +373,31 @@ You can set this parameter by providing `Meta` class `constraints` argument.
|
|||||||
To set one column as unique use [`unique`](../fields/common-parameters.md#unique) common parameter.
|
To set one column as unique use [`unique`](../fields/common-parameters.md#unique) common parameter.
|
||||||
Of course you can set many columns as unique with this param but each of them will be checked separately.
|
Of course you can set many columns as unique with this param but each of them will be checked separately.
|
||||||
|
|
||||||
|
### Pydantic configuration
|
||||||
|
|
||||||
|
As each `ormar.Model` is also a `pydantic` model, you might want to tweak the settings of the pydantic configuration.
|
||||||
|
|
||||||
|
The way to do this in pydantic is to adjust the settings on the `Config` class provided to your model, and it works exactly the same for ormer.Models.
|
||||||
|
|
||||||
|
So in order to set your own preferences you need to provide not only the `Meta` class but also the `Config` class to your model.
|
||||||
|
|
||||||
|
!!!note
|
||||||
|
To read more about available settings visit the [pydantic](https://pydantic-docs.helpmanual.io/usage/model_config/) config page.
|
||||||
|
|
||||||
|
Note that if you do not provide your own configuration, ormar will do it for you.
|
||||||
|
The default config provided is as follows:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Config(pydantic.BaseConfig):
|
||||||
|
orm_mode = True
|
||||||
|
validate_assignment = True
|
||||||
|
```
|
||||||
|
|
||||||
|
So to overwrite setting or provide your own a sample model can look like following:
|
||||||
|
```Python hl_lines="15-16"
|
||||||
|
--8<-- "../docs_src/models/docs016.py"
|
||||||
|
```
|
||||||
|
|
||||||
## Model sort order
|
## Model sort order
|
||||||
|
|
||||||
When querying the database with given model by default the Model is ordered by the `primary_key`
|
When querying the database with given model by default the Model is ordered by the `primary_key`
|
||||||
|
|||||||
20
docs_src/models/docs016.py
Normal file
20
docs_src/models/docs016.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import databases
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
import ormar
|
||||||
|
|
||||||
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
|
class Course(ormar.Model):
|
||||||
|
class Meta:
|
||||||
|
database = database
|
||||||
|
metadata = metadata
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
allow_mutation = False
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
completed: bool = ormar.Boolean(default=False)
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import inspect
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Dict,
|
Dict,
|
||||||
@ -552,7 +553,21 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
|
|||||||
:param attrs: class namespace
|
:param attrs: class namespace
|
||||||
:type attrs: Dict
|
:type attrs: Dict
|
||||||
"""
|
"""
|
||||||
attrs["Config"] = get_pydantic_base_orm_config()
|
DefaultConfig = get_pydantic_base_orm_config()
|
||||||
|
if "Config" in attrs:
|
||||||
|
ProvidedConfig = attrs["Config"]
|
||||||
|
if not inspect.isclass(ProvidedConfig):
|
||||||
|
raise ModelDefinitionError(
|
||||||
|
f"Config provided for class {name} has to be a class."
|
||||||
|
)
|
||||||
|
|
||||||
|
class Config(ProvidedConfig, DefaultConfig): # type: ignore
|
||||||
|
pass
|
||||||
|
|
||||||
|
attrs["Config"] = Config
|
||||||
|
else:
|
||||||
|
attrs["Config"] = DefaultConfig
|
||||||
|
|
||||||
attrs["__name__"] = name
|
attrs["__name__"] = name
|
||||||
attrs, model_fields = extract_annotations_and_default_vals(attrs)
|
attrs, model_fields = extract_annotations_and_default_vals(attrs)
|
||||||
for base in reversed(bases):
|
for base in reversed(bases):
|
||||||
@ -580,9 +595,11 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
|
|||||||
populate_meta_sqlalchemy_table_if_required(new_model.Meta)
|
populate_meta_sqlalchemy_table_if_required(new_model.Meta)
|
||||||
expand_reverse_relationships(new_model)
|
expand_reverse_relationships(new_model)
|
||||||
# TODO: iterate only related fields
|
# TODO: iterate only related fields
|
||||||
for name, field in new_model.Meta.model_fields.items():
|
for field_name, field in new_model.Meta.model_fields.items():
|
||||||
register_relation_in_alias_manager(field=field)
|
register_relation_in_alias_manager(field=field)
|
||||||
add_field_descriptor(name=name, field=field, new_model=new_model)
|
add_field_descriptor(
|
||||||
|
name=field_name, field=field, new_model=new_model
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
new_model.Meta.pkname
|
new_model.Meta.pkname
|
||||||
@ -640,10 +657,7 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
|
|||||||
model=field.to,
|
model=field.to,
|
||||||
access_chain=item,
|
access_chain=item,
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
return FieldAccessor(
|
return FieldAccessor(
|
||||||
source_model=cast(Type["Model"], self),
|
source_model=cast(Type["Model"], self), field=field, access_chain=item,
|
||||||
field=field,
|
|
||||||
access_chain=item,
|
|
||||||
)
|
)
|
||||||
return object.__getattribute__(self, item)
|
return object.__getattribute__(self, item)
|
||||||
|
|||||||
@ -150,6 +150,12 @@ class Bus2(Car2):
|
|||||||
max_persons: int = ormar.Integer()
|
max_persons: int = ormar.Integer()
|
||||||
|
|
||||||
|
|
||||||
|
class ImmutablePerson(Person):
|
||||||
|
class Config:
|
||||||
|
allow_mutation = False
|
||||||
|
validate_assignment = False
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True, scope="module")
|
@pytest.fixture(autouse=True, scope="module")
|
||||||
def create_test_database():
|
def create_test_database():
|
||||||
metadata.create_all(engine)
|
metadata.create_all(engine)
|
||||||
@ -173,6 +179,13 @@ def test_duplicated_related_name_on_different_model():
|
|||||||
max_persons: int = ormar.Integer()
|
max_persons: int = ormar.Integer()
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_is_not_a_class_raises_error():
|
||||||
|
with pytest.raises(ModelDefinitionError):
|
||||||
|
|
||||||
|
class ImmutablePerson2(Person):
|
||||||
|
Config = dict(allow_mutation=False, validate_assignment=False)
|
||||||
|
|
||||||
|
|
||||||
def test_field_redefining_in_concrete_models():
|
def test_field_redefining_in_concrete_models():
|
||||||
class RedefinedField(DateFieldsModel):
|
class RedefinedField(DateFieldsModel):
|
||||||
class Meta(ormar.ModelMeta):
|
class Meta(ormar.ModelMeta):
|
||||||
@ -495,3 +508,13 @@ async def test_inheritance_with_multi_relation():
|
|||||||
assert len(unicorns) == 2
|
assert len(unicorns) == 2
|
||||||
assert unicorns[1].name == "Unicorn 2"
|
assert unicorns[1].name == "Unicorn 2"
|
||||||
assert len(unicorns[1].co_owners) == 1
|
assert len(unicorns[1].co_owners) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_config():
|
||||||
|
# Custom config inherits defaults
|
||||||
|
assert getattr(ImmutablePerson.__config__, "orm_mode") is True
|
||||||
|
# Custom config can override defaults
|
||||||
|
assert getattr(ImmutablePerson.__config__, "validate_assignment") is False
|
||||||
|
sam = ImmutablePerson(name="Sam")
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
sam.name = "Not Sam"
|
||||||
|
|||||||
Reference in New Issue
Block a user