Skip to content

Commit

Permalink
CHanges:
Browse files Browse the repository at this point in the history
mini fixes
update docs
  • Loading branch information
devkral committed Jan 12, 2025
1 parent f85640e commit e9afab2
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 37 deletions.
4 changes: 3 additions & 1 deletion docs/fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ The reverse end of a `ForeignKey` is a [Many to one relation](./queries/many-to-
##### Parameters

* `to` - A string [model](./models.md) name or a class object of that same model.
* `target_registry` - Registry where the model callback is installed if `to` is a string.
* `target_registry` - Registry where the model callback is installed if `to` is a string. Defaults to the field owner registry.
* `related_name` - The name to use for the relation from the related object back to this one. Can be set to `False` to disable a reverse connection.
Note: Setting to `False` will also prevent prefetching and reversing via `__`.
See also [related_name](./queries/related-name.md) for defaults
Expand Down Expand Up @@ -586,11 +586,13 @@ class MyModel(edgy.Model):
##### Parameters

* `to` - A string [model](./models.md) name or a class object of that same model.
* `target_registry` - Registry where the model callback is installed if `to` is a string. Defaults to the field owner registry.
* `from_fields` - Provide the `related_fields` for the implicitly generated ForeignKey to the owner model.
* `to_fields` - Provide the `related_fields` for the implicitly generated ForeignKey to the child model.
* `related_name` - The name to use for the relation from the related object back to this one.
* `through` - The model to be used for the relationship. Edgy generates the model by default
if None is provided or `through` is an abstract model.
* `through_registry` - Registry where the model callback is installed if `through` is a string or empty. Defaults to the field owner registry.
* `through_tablename` - Custom tablename for `through`. E.g. when special characters are used in model names.
* `embed_through` - When traversing, embed the through object in this attribute. Otherwise it is not accessable from the result.
if an empty string was provided, the old behaviour is used to query from the through model as base (default).
Expand Down
3 changes: 3 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ hide:
- `add_to_registry` has now an additional keyword-only argument `on_conflict` for controlling what happens when a same named model already exists.
- Passing a tuple or list of types to `replace_related_field` is now allowed.
- Add `through_registry` to ManyToMany.
- Add `no_copy` to MetaInfo.

### Changed

- `add_to_registry` deprecates passing arguments as positional arguments except the first one (registry).
- `create_edgy_model` passes through additional keyword arguments to the edgy model class.
- RelatedField uses now `no_copy`.
- Through models use now `no_copy` when autogenerated.

### Fixed

Expand Down
62 changes: 29 additions & 33 deletions edgy/core/connection/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@
from copy import copy as shallow_copy
from functools import cached_property, partial
from types import TracebackType
from typing import (
TYPE_CHECKING,
Any,
Callable,
Literal,
Optional,
Union,
cast,
overload,
)
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal, Optional, Union, cast, overload

import sqlalchemy
from loguru import logger
Expand Down Expand Up @@ -103,6 +94,13 @@ class Registry:
The command center for the models of Edgy.
"""

model_registry_types: ClassVar[tuple[str, ...]] = (
"models",
"reflected",
"tenant_models",
"pattern_models",
)

db_schema: Union[str, None] = None
content_type: Union[type["BaseModelType"], None] = None
dbs_reflected: set[Union[str, None]]
Expand Down Expand Up @@ -170,19 +168,24 @@ def __copy__(self) -> "Registry":
try:
content_type = self.get_model(
"ContentType", include_content_type_attr=False
).copy_edgy_model(unlink_same_registry=True)
).copy_edgy_model()
except LookupError:
content_type = self.content_type
_copy = Registry(
self.database, with_content_type=content_type, schema=self.db_schema, extra=self.extra
)
for i in ["models", "reflected", "tenant_models", "pattern_models"]:
dict_models = getattr(_copy, i)
for registry_type in self.model_registry_types:
dict_models = getattr(_copy, registry_type)
dict_models.update(
(
(key, val.copy_edgy_model(registry=_copy, unlink_same_registry=True))
for key, val in getattr(self, i).items()
if key not in dict_models and not val.meta.no_copy
(
key,
val.copy_edgy_model(
registry=_copy if registry_type in {"models", "reflected"} else None
),
)
for key, val in getattr(self, registry_type).items()
if not val.meta.no_copy and key not in dict_models
)
)
_copy.dbs_reflected = set(self.dbs_reflected)
Expand Down Expand Up @@ -307,25 +310,18 @@ def get_model(
and self.content_type is not None
):
return self.content_type
if model_name in self.models:
return self.models[model_name]
elif model_name in self.reflected:
return self.reflected[model_name]
elif model_name in self.tenant_models:
return self.tenant_models[model_name]
else:
raise LookupError(f"Registry doesn't have a {model_name} model.") from None
for model_dict_name in self.model_registry_types:
model_dict: dict = getattr(self, model_dict_name)
if model_name in model_dict:
return cast(type["BaseModelType"], model_dict[model_name])
raise LookupError(f'Registry doesn\'t have a "{model_name}" model.') from None

def delete_model(self, model_name: str) -> bool:
if model_name in self.models:
del self.models[model_name]
return True
elif model_name in self.reflected:
del self.reflected[model_name]
return True
elif model_name in self.tenant_models:
del self.tenant_models[model_name]
return True
for model_dict_name in self.model_registry_types:
model_dict: dict = getattr(self, model_dict_name)
if model_name in model_dict:
del model_dict[model_name]
return True
return False

def refresh_metadata(
Expand Down
13 changes: 10 additions & 3 deletions edgy/core/db/models/mixins/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,15 @@ def add_to_registry(
registry.delete_model(cls.__name__)
else:
with contextlib.suppress(LookupError):
original_model = registry.get_model(cls.__name__)
original_model = registry.get_model(
cls.__name__, include_content_type_attr=False
)
if on_conflict == "keep":
return original_model
else:
raise ValueError("Already a model with the same name registered")
raise ValueError(
f'Already a model with the same name registered: "{cls.__name__}"'
)
if getattr(cls, "__reflected__", False):
registry.reflected[cls.__name__] = cls
else:
Expand Down Expand Up @@ -293,6 +297,7 @@ def copy_edgy_model(
name: str = "",
unlink_same_registry: bool = True,
meta_info: MetaInfo | None = None,
on_conflict: Literal["keep", "replace", "error"] = "error",
**kwargs: Any,
) -> type[Model]:
"""Copy the model class and optionally add it to another registry."""
Expand Down Expand Up @@ -342,7 +347,8 @@ def copy_edgy_model(
if isinstance(src_field, BaseManyToManyForeignKeyField):
_copy.meta.fields[field_name].through = src_field.through_original
if (
issubclass(_copy.meta.fields[field_name].through, BaseModelType)
isinstance(_copy.meta.fields[field_name].through, type)
and issubclass(_copy.meta.fields[field_name].through, BaseModelType)
and not _copy.meta.fields[field_name].through.meta.abstract
):
# unreference
Expand All @@ -366,6 +372,7 @@ def copy_edgy_model(
_copy.add_to_registry(
registry,
replace_related_field=replaceable_models,
on_conflict=on_conflict,
database="keep"
if cls.meta.registry is False or cls.database is not cls.meta.registry.database
else True,
Expand Down

0 comments on commit e9afab2

Please sign in to comment.