add skip_reverse parameter, add links to related libs, fix weakref error, fix through error with extra=forbid
This commit is contained in:
@ -111,6 +111,7 @@ def register_reverse_model_fields(model_field: "ForeignKeyField") -> None:
|
||||
self_reference=model_field.self_reference,
|
||||
self_reference_primary=model_field.self_reference_primary,
|
||||
orders_by=model_field.related_orders_by,
|
||||
skip_field=model_field.skip_reverse,
|
||||
)
|
||||
# register foreign keys on through model
|
||||
model_field = cast("ManyToManyField", model_field)
|
||||
@ -125,6 +126,7 @@ def register_reverse_model_fields(model_field: "ForeignKeyField") -> None:
|
||||
owner=model_field.to,
|
||||
self_reference=model_field.self_reference,
|
||||
orders_by=model_field.related_orders_by,
|
||||
skip_field=model_field.skip_reverse,
|
||||
)
|
||||
|
||||
|
||||
@ -145,6 +147,7 @@ def register_through_shortcut_fields(model_field: "ManyToManyField") -> None:
|
||||
virtual=True,
|
||||
related_name=model_field.name,
|
||||
owner=model_field.owner,
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
model_field.to.Meta.model_fields[through_name] = Through(
|
||||
@ -153,6 +156,7 @@ def register_through_shortcut_fields(model_field: "ManyToManyField") -> None:
|
||||
virtual=True,
|
||||
related_name=related_name,
|
||||
owner=model_field.to,
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -90,6 +90,7 @@ def add_cached_properties(new_model: Type["Model"]) -> None:
|
||||
"""
|
||||
new_model._quick_access_fields = quick_access_set
|
||||
new_model._related_names = None
|
||||
new_model._through_names = None
|
||||
new_model._related_fields = None
|
||||
new_model._pydantic_fields = {name for name in new_model.__fields__}
|
||||
new_model._choices_fields = set()
|
||||
@ -536,6 +537,7 @@ class ModelMetaclass(pydantic.main.ModelMetaclass):
|
||||
new_model = populate_meta_tablename_columns_and_pk(name, new_model)
|
||||
populate_meta_sqlalchemy_table_if_required(new_model.Meta)
|
||||
expand_reverse_relationships(new_model)
|
||||
# TODO: iterate only related fields
|
||||
for field in new_model.Meta.model_fields.values():
|
||||
register_relation_in_alias_manager(field=field)
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ class RelationMixin:
|
||||
|
||||
Meta: ModelMeta
|
||||
_related_names: Optional[Set]
|
||||
_through_names: Optional[Set]
|
||||
_related_fields: Optional[List]
|
||||
get_name: Callable
|
||||
|
||||
@ -57,19 +58,23 @@ class RelationMixin:
|
||||
return related_fields
|
||||
|
||||
@classmethod
|
||||
def extract_through_names(cls) -> Set:
|
||||
def extract_through_names(cls) -> Set[str]:
|
||||
"""
|
||||
Extracts related fields through names which are shortcuts to through models.
|
||||
|
||||
:return: set of related through fields names
|
||||
:rtype: Set
|
||||
"""
|
||||
related_fields = set()
|
||||
for name in cls.extract_related_names():
|
||||
field = cls.Meta.model_fields[name]
|
||||
if field.is_multi:
|
||||
related_fields.add(field.through.get_name(lower=True))
|
||||
return related_fields
|
||||
if isinstance(cls._through_names, Set):
|
||||
return cls._through_names
|
||||
|
||||
related_names = set()
|
||||
for name, field in cls.Meta.model_fields.items():
|
||||
if isinstance(field, BaseField) and field.is_through:
|
||||
related_names.add(name)
|
||||
|
||||
cls._through_names = related_names
|
||||
return related_names
|
||||
|
||||
@classmethod
|
||||
def extract_related_names(cls) -> Set[str]:
|
||||
@ -89,6 +94,7 @@ class RelationMixin:
|
||||
isinstance(field, BaseField)
|
||||
and field.is_relation
|
||||
and not field.is_through
|
||||
and not field.skip_field
|
||||
):
|
||||
related_names.add(name)
|
||||
cls._related_names = related_names
|
||||
|
||||
@ -24,7 +24,11 @@ class Model(ModelRow):
|
||||
Meta: ModelMeta
|
||||
|
||||
def __repr__(self) -> str: # pragma nocover
|
||||
_repr = {k: getattr(self, k) for k, v in self.Meta.model_fields.items()}
|
||||
_repr = {
|
||||
k: getattr(self, k)
|
||||
for k, v in self.Meta.model_fields.items()
|
||||
if not v.skip_field
|
||||
}
|
||||
return f"{self.__class__.__name__}({str(_repr)})"
|
||||
|
||||
async def upsert(self: T, **kwargs: Any) -> T:
|
||||
|
||||
@ -81,6 +81,7 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
||||
_orm_id: int
|
||||
_orm_saved: bool
|
||||
_related_names: Optional[Set]
|
||||
_through_names: Optional[Set]
|
||||
_related_names_hash: str
|
||||
_choices_fields: Optional[Set]
|
||||
_pydantic_fields: Set
|
||||
@ -165,6 +166,11 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
||||
for field_to_nullify in excluded:
|
||||
new_kwargs[field_to_nullify] = None
|
||||
|
||||
# extract through fields
|
||||
through_tmp_dict = dict()
|
||||
for field_name in self.extract_through_names():
|
||||
through_tmp_dict[field_name] = new_kwargs.pop(field_name, None)
|
||||
|
||||
values, fields_set, validation_error = pydantic.validate_model(
|
||||
self, new_kwargs # type: ignore
|
||||
)
|
||||
@ -174,6 +180,9 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
||||
object.__setattr__(self, "__dict__", values)
|
||||
object.__setattr__(self, "__fields_set__", fields_set)
|
||||
|
||||
# add back through fields
|
||||
new_kwargs.update(through_tmp_dict)
|
||||
|
||||
# register the columns models after initialization
|
||||
for related in self.extract_related_names().union(self.extract_through_names()):
|
||||
self.Meta.model_fields[related].expand_relationship(
|
||||
@ -592,13 +601,16 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
||||
exclude=self._skip_ellipsis(exclude, field),
|
||||
)
|
||||
elif nested_model is not None:
|
||||
dict_instance[field] = nested_model.dict(
|
||||
relation_map=self._skip_ellipsis(
|
||||
relation_map, field, default_return=dict()
|
||||
),
|
||||
include=self._skip_ellipsis(include, field),
|
||||
exclude=self._skip_ellipsis(exclude, field),
|
||||
)
|
||||
try:
|
||||
dict_instance[field] = nested_model.dict(
|
||||
relation_map=self._skip_ellipsis(
|
||||
relation_map, field, default_return=dict()
|
||||
),
|
||||
include=self._skip_ellipsis(include, field),
|
||||
exclude=self._skip_ellipsis(exclude, field),
|
||||
)
|
||||
except ReferenceError:
|
||||
dict_instance[field] = None
|
||||
else:
|
||||
dict_instance[field] = None
|
||||
return dict_instance
|
||||
|
||||
Reference in New Issue
Block a user