Skip to content

Commit

Permalink
Changes:
Browse files Browse the repository at this point in the history
- fix invalidation causing _db_schemas removed
- fix copying bugs
- instead of passing down keyword arguments from create_edgy_model to type add an
  argument matching the other behaviour
  • Loading branch information
devkral committed Jan 13, 2025
1 parent f40055c commit d24d558
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 43 deletions.
5 changes: 3 additions & 2 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ hide:

### Changed

- `create_edgy_model` passes through additional keyword arguments to the edgy model class.
- `create_edgy_model` has now `__type_kwargs__` which contains a dict of keyword arguments provided to `__new__` of type.
- RelatedField uses now `no_copy`.
- `add_to_registry` returns the type which was actually added to registry instead of None.
- Through models use now `no_copy` when autogenerated.
- BREAKING: instead of silent replacing models with the same `__name__` now an error is raised.

### Fixed

- Copying registries is working now.
- Copying registries and models is working now.
- Fix deleting (clearing cache) of BaseForeignKey target.
- Creating two models with the same name did lead to silent replacements.
- Invalidating causes schema errors.

## 0.24.2

Expand Down
6 changes: 4 additions & 2 deletions edgy/core/db/models/metaclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,11 @@ def invalidate(
if self.model is None:
return
if clear_class_attrs:
for attr in ("_table", "_pknames", "_pkcolumns", "_db_schemas", "__proxy_model__"):
for attr in ("_table", "_pknames", "_pkcolumns", "__proxy_model__"):
with contextlib.suppress(AttributeError):
delattr(self.model, attr)
# needs an extra invalidation
self.model._db_schemas = {}

def full_init(self, init_column_mappers: bool = True, init_class_attrs: bool = True) -> None:
if not self._fields_are_initialized:
Expand Down Expand Up @@ -730,14 +732,14 @@ def __new__(
# (excluding the edgy.Model class itself).
if not has_parents:
return new_class
new_class._db_schemas = {}

# Ensure the model_fields are updated to the latest
# required since pydantic 2.10
new_class.__pydantic_fields__ = model_fields
# error since pydantic 2.10
with contextlib.suppress(AttributeError):
new_class.model_fields = model_fields
new_class._db_schemas = {}

# Set the owner of the field, must be done as early as possible
# don't use meta.fields to not trigger the lazy evaluation
Expand Down
4 changes: 2 additions & 2 deletions edgy/core/db/models/mixins/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class _EmptyClass: ...
"_pknames",
"_table",
"_db_schemas",
"__proxy_model__",
"meta",
}
_removed_copy_keys.difference_update(
Expand Down Expand Up @@ -319,8 +320,7 @@ def copy_edgy_model(
__definitions__=attrs,
__metadata__=cls.meta,
__bases__=cls.__bases__,
skip_registry=True,
**kwargs,
__type_kwargs__={**kwargs, "skip_registry": True},
)
# should also allow masking database with None
if hasattr(cls, "database"):
Expand Down
6 changes: 4 additions & 2 deletions edgy/core/utils/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def create_edgy_model(
__bases__: Optional[tuple[type["BaseModelType"], ...]] = None,
__proxy__: bool = False,
__pydantic_extra__: Any = None,
**kwargs: Any,
__type_kwargs__: Optional[dict] = None,
) -> type["Model"]:
"""
Generates an `edgy.Model` with all the required definitions to generate the pydantic
Expand Down Expand Up @@ -48,8 +48,10 @@ def create_edgy_model(
core_definitions.update(**{"Meta": __metadata__})
if __pydantic_extra__:
core_definitions.update(**{"__pydantic_extra__": __pydantic_extra__})
if not __type_kwargs__:
__type_kwargs__ = {}

model: type[Model] = type(__name__, __bases__, core_definitions, **kwargs)
model: type[Model] = type(__name__, __bases__, core_definitions, **__type_kwargs__)
return model


Expand Down
29 changes: 2 additions & 27 deletions tests/contrib/multi_tenancy/test_mt_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,33 +100,8 @@ async def test_create_a_tenant_schema_copy():
NewCart = copied.get_model("Cart")
assert NewCart.meta.fields["products"].target is NewProduct
assert NewCart.meta.fields["products"].through is not Cart.meta.fields["products"].through
cart = await NewCart.query.using(schema=tenant.schema_name).create()
for i in range(5):
await cart.products.add(
await NewProduct.query.using(schema=tenant.schema_name).create(name=f"product-{i}")
)

products = await cart.products.using(schema=tenant.schema_name).all()
assert len(products) == 5

total = await NewProduct.query.using(schema=tenant.schema_name).all()

assert len(total) == 5

total = await NewProduct.query.all()

assert len(total) == 0

for i in range(15):
await NewProduct.query.create(name=f"product-{i}")

total = await NewProduct.query.all()

assert len(total) == 15

total = await NewProduct.query.using(schema=tenant.schema_name).all()

assert len(total) == 5
assert hasattr(Cart.meta.fields["products"].through, "_db_schemas")
assert hasattr(NewCart.meta.fields["products"].through, "_db_schemas")


async def test_raises_ModelSchemaError_on_public_schema():
Expand Down
28 changes: 20 additions & 8 deletions tests/contrib/multi_tenancy/test_tenant_models_using.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,41 @@ class Meta:
is_tenant = True


async def test_schema_with_using_in_different_place():
tenant = await Tenant.query.create(
@pytest.mark.parametrize("use_copy", ["false", "instant", "after"])
async def test_schema_with_using_in_different_place(use_copy):
if use_copy == "instant":
copied = models.__copy__()
NewTenant = copied.get_model("Tenant")
NewProduct = copied.get_model("Product")
else:
NewTenant = Tenant
NewProduct = Product
tenant = await NewTenant.query.create(
schema_name="edgy", domain_url="https://edgy.dymmond.com", tenant_name="edgy"
)
if use_copy == "after":
copied = models.__copy__()
NewTenant = copied.get_model("Tenant")
NewProduct = copied.get_model("Product")
for i in range(5):
await Product.query.using(schema=tenant.schema_name).create(name=f"product-{i}")
await NewProduct.query.using(schema=tenant.schema_name).create(name=f"product-{i}")

total = await Product.query.filter().using(schema=tenant.schema_name).all()
total = await NewProduct.query.filter().using(schema=tenant.schema_name).all()

assert len(total) == 5

total = await Product.query.all()
total = await NewProduct.query.all()

assert len(total) == 0

for i in range(15):
await Product.query.create(name=f"product-{i}")
await NewProduct.query.create(name=f"product-{i}")

total = await Product.query.all()
total = await NewProduct.query.all()

assert len(total) == 15

total = await Product.query.filter().using(schema=tenant.schema_name).all()
total = await NewProduct.query.filter().using(schema=tenant.schema_name).all()

assert len(total) == 5

Expand Down
3 changes: 3 additions & 0 deletions tests/models/test_model_copying.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Meta:
assert len(models2.models) == 3

through = models2.get_model("Cart").meta.fields["products"].through
assert "_db_schemas" in through.__dict__
assert through is models2.get_model(through.__name__)
assert through is not models.get_model(through.__name__)
for reg in [models, models2]:
Expand Down Expand Up @@ -110,6 +111,7 @@ class Meta:
assert len(models2.models) == 3

through = models2.get_model("Cart").meta.fields["products"].through
assert "_db_schemas" in through.__dict__
assert through is models2.get_model(through.__name__)
assert through is not models.get_model(through.__name__)
assert through.__name__ == "ThroughModel"
Expand Down Expand Up @@ -167,6 +169,7 @@ class Meta:
assert len(models2.models) == 3

through = models2.get_model("Cart").meta.fields["products"].through
assert "_db_schemas" in through.__dict__
assert through is models2.get_model(through.__name__)
assert through is not models3.get_model(through.__name__)
assert through.__name__ == "ThroughModel"
2 changes: 2 additions & 0 deletions tests/registry/test_registry_copying.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Meta:
"Product"
)
through = models_copy.get_model("Cart").meta.fields["products"].through
assert "_db_schemas" in through.__dict__
assert through is models_copy.get_model(through.__name__)


Expand Down Expand Up @@ -93,4 +94,5 @@ class Meta:
"Product"
)
through = models_copy.get_model("Cart").meta.fields["products"].through
assert "_db_schemas" in through.__dict__
assert through is models_copy.get_model(through.__name__)

0 comments on commit d24d558

Please sign in to comment.