Esempio n. 1
0
    def update_forward_refs(cls, **localns: Any) -> None:
        """
        Processes fields that are ForwardRef and need to be evaluated into actual
        models.

        Expands relationships, register relation in alias manager and substitutes
        sqlalchemy columns with new ones with proper column type (null before).

        Populates Meta table of the Model which is left empty before.

        Sets self_reference flag on models that links to themselves.

        Calls the pydantic method to evaluate pydantic fields.

        :param localns: local namespace
        :type localns: Any
        :return: None
        :rtype: None
        """
        globalns = sys.modules[cls.__module__].__dict__.copy()
        globalns.setdefault(cls.__name__, cls)
        fields_to_check = cls.Meta.model_fields.copy()
        for field in fields_to_check.values():
            if field.has_unresolved_forward_refs():
                field = cast(Type[ForeignKeyField], field)
                field.evaluate_forward_ref(globalns=globalns, localns=localns)
                field.set_self_reference_flag()
                expand_reverse_relationship(model_field=field)
                register_relation_in_alias_manager(field=field)
                update_column_definition(model=cls, field=field)
        populate_meta_sqlalchemy_table_if_required(meta=cls.Meta)
        super().update_forward_refs(**localns)
        cls.Meta.requires_ref_update = False
Esempio n. 2
0
    def __new__(  # type: ignore # noqa: CCR001
        mcs: "ModelMetaclass", name: str, bases: Any, attrs: dict
    ) -> "ModelMetaclass":
        """
        Metaclass used by ormar Models that performs configuration
        and build of ormar Models.


        Sets pydantic configuration.
        Extract model_fields and convert them to pydantic FieldInfo,
        updates class namespace.

        Extracts settings and fields from parent classes.
        Fetches methods decorated with @property_field decorator
        to expose them later in dict().

        Construct parent pydantic Metaclass/ Model.

        If class has Meta class declared (so actual ormar Models) it also:

        * populate sqlalchemy columns, pkname and tables from model_fields
        * register reverse relationships on related models
        * registers all relations in alias manager that populates table_prefixes
        * exposes alias manager on each Model
        * creates QuerySet for each model and exposes it on a class

        :param name: name of current class
        :type name: str
        :param bases: base classes
        :type bases: Tuple
        :param attrs: class namespace
        :type attrs: Dict
        """
        attrs["Config"] = get_pydantic_base_orm_config()
        attrs["__name__"] = name
        attrs, model_fields = extract_annotations_and_default_vals(attrs)
        for base in reversed(bases):
            mod = base.__module__
            if mod.startswith("ormar.models.") or mod.startswith("pydantic."):
                continue
            attrs, model_fields = extract_from_parents_definition(
                base_class=base, curr_class=mcs, attrs=attrs, model_fields=model_fields
            )
        new_model = super().__new__(  # type: ignore
            mcs, name, bases, attrs
        )

        add_cached_properties(new_model)

        if hasattr(new_model, "Meta"):
            populate_default_options_values(new_model, model_fields)
            check_required_meta_parameters(new_model)
            add_property_fields(new_model, attrs)
            register_signals(new_model=new_model)
            populate_choices_validators(new_model)

            if not new_model.Meta.abstract:
                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)

                if new_model.Meta.pkname not in attrs["__annotations__"]:
                    field_name = new_model.Meta.pkname
                    attrs["__annotations__"][field_name] = Optional[int]  # type: ignore
                    attrs[field_name] = None
                    new_model.__fields__[field_name] = get_pydantic_field(
                        field_name=field_name, model=new_model
                    )
                new_model.Meta.alias_manager = alias_manager

        return new_model