Merge pull request #286 from naturalethic/allow-custom-model-config

Allow custom model config
This commit is contained in:
collerek
2021-07-25 12:35:52 +02:00
committed by GitHub
4 changed files with 91 additions and 9 deletions

View File

@ -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.
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
When querying the database with given model by default the Model is ordered by the `primary_key`

View 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)

View File

@ -1,3 +1,4 @@
import inspect
from typing import (
Any,
Dict,
@ -552,7 +553,21 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
:param attrs: class namespace
: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, model_fields = extract_annotations_and_default_vals(attrs)
for base in reversed(bases):
@ -580,9 +595,11 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
populate_meta_sqlalchemy_table_if_required(new_model.Meta)
expand_reverse_relationships(new_model)
# 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)
add_field_descriptor(name=name, field=field, new_model=new_model)
add_field_descriptor(
name=field_name, field=field, new_model=new_model
)
if (
new_model.Meta.pkname
@ -640,10 +657,7 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
model=field.to,
access_chain=item,
)
else:
return FieldAccessor(
source_model=cast(Type["Model"], self),
field=field,
access_chain=item,
source_model=cast(Type["Model"], self), field=field, access_chain=item,
)
return object.__getattribute__(self, item)

View File

@ -150,6 +150,12 @@ class Bus2(Car2):
max_persons: int = ormar.Integer()
class ImmutablePerson(Person):
class Config:
allow_mutation = False
validate_assignment = False
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
metadata.create_all(engine)
@ -173,6 +179,13 @@ def test_duplicated_related_name_on_different_model():
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():
class RedefinedField(DateFieldsModel):
class Meta(ormar.ModelMeta):
@ -495,3 +508,13 @@ async def test_inheritance_with_multi_relation():
assert len(unicorns) == 2
assert unicorns[1].name == "Unicorn 2"
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"