Exemplo n.º 1
0
    def _init_relations(cls) -> None:
        def get_related_model(related_app_name: str, related_model_name: str) -> Type["Model"]:
            """
            Test, if app and model really exist. Throws a ConfigurationError with a hopefully
            helpful message. If successfull, returns the requested model.

            :raises ConfigurationError: If no such app exists.
            """
            try:
                return cls.apps[related_app_name][related_model_name]
            except KeyError:
                if related_app_name not in cls.apps:
                    raise ConfigurationError(f"No app with name '{related_app_name}' registered.")
                raise ConfigurationError(
                    f"No model with name '{related_model_name}' registered in"
                    f" app '{related_app_name}'."
                )

        def split_reference(reference: str) -> Tuple[str, str]:
            """
            Test, if reference follow the official naming conventions. Throws a
            ConfigurationError with a hopefully helpful message. If successfull,
            returns the app and the model name.

            :raises ConfigurationError: If no model reference is invalid.
            """
            items = reference.split(".")
            if len(items) != 2:  # pragma: nocoverage
                raise ConfigurationError(
                    (
                        "'%s' is not a valid model reference Bad Reference."
                        " Should be something like <appname>.<modelname>."
                    )
                    % reference
                )

            return (items[0], items[1])

        for app_name, app in cls.apps.items():
            for model_name, model in app.items():
                if model._meta._inited:
                    continue
                model._meta._inited = True
                if not model._meta.db_table:
                    model._meta.db_table = model.__name__.lower()

                # TODO: refactor to share logic between FK & O2O
                for field in model._meta.fk_fields:
                    fk_object = cast(ForeignKeyFieldInstance, model._meta.fields_map[field])
                    reference = fk_object.model_name
                    related_app_name, related_model_name = split_reference(reference)
                    related_model = get_related_model(related_app_name, related_model_name)

                    if fk_object.to_field:
                        related_field = related_model._meta.fields_map.get(fk_object.to_field, None)
                        if related_field:
                            if related_field.unique:
                                key_fk_object = deepcopy(related_field)
                                fk_object.to_field_instance = related_field
                            else:
                                raise ConfigurationError(
                                    f'field "{fk_object.to_field}" in model'
                                    f' "{related_model_name}" is not unique'
                                )
                        else:
                            raise ConfigurationError(
                                f'there is no field named "{fk_object.to_field}"'
                                f' in model "{related_model_name}"'
                            )
                    else:
                        key_fk_object = deepcopy(related_model._meta.pk)
                        fk_object.to_field_instance = related_model._meta.pk
                        fk_object.to_field = related_model._meta.pk_attr

                    key_field = f"{field}_id"
                    key_fk_object.pk = False
                    key_fk_object.unique = False
                    key_fk_object.index = fk_object.index
                    key_fk_object.default = fk_object.default
                    key_fk_object.null = fk_object.null
                    key_fk_object.generated = fk_object.generated
                    key_fk_object.reference = fk_object
                    key_fk_object.description = fk_object.description
                    if fk_object.source_field:
                        key_fk_object.source_field = fk_object.source_field
                    else:
                        key_fk_object.source_field = key_field
                    model._meta.add_field(key_field, key_fk_object)

                    fk_object.related_model = related_model
                    fk_object.source_field = key_field
                    backward_relation_name = fk_object.related_name
                    if backward_relation_name is not False:
                        if not backward_relation_name:
                            backward_relation_name = f"{model._meta.db_table}s"
                        if backward_relation_name in related_model._meta.fields:
                            raise ConfigurationError(
                                f'backward relation "{backward_relation_name}" duplicates in'
                                f" model {related_model_name}"
                            )
                        fk_relation = BackwardFKRelation(
                            model,
                            f"{field}_id",
                            key_fk_object.source_field,
                            fk_object.null,
                            fk_object.description,
                        )
                        fk_relation.to_field_instance = fk_object.to_field_instance
                        related_model._meta.add_field(backward_relation_name, fk_relation)

                for field in model._meta.o2o_fields:
                    o2o_object = cast(OneToOneFieldInstance, model._meta.fields_map[field])
                    reference = o2o_object.model_name
                    related_app_name, related_model_name = split_reference(reference)
                    related_model = get_related_model(related_app_name, related_model_name)

                    if o2o_object.to_field:
                        related_field = related_model._meta.fields_map.get(
                            o2o_object.to_field, None
                        )
                        if related_field:
                            if related_field.unique:
                                key_o2o_object = deepcopy(related_field)
                                o2o_object.to_field_instance = related_field
                            else:
                                raise ConfigurationError(
                                    f'field "{o2o_object.to_field}" in model'
                                    f' "{related_model_name}" is not unique'
                                )
                        else:
                            raise ConfigurationError(
                                f'there is no field named "{o2o_object.to_field}"'
                                f' in model "{related_model_name}"'
                            )
                    else:
                        key_o2o_object = deepcopy(related_model._meta.pk)
                        o2o_object.to_field_instance = related_model._meta.pk
                        o2o_object.to_field = related_model._meta.pk_attr

                    key_field = f"{field}_id"
                    key_o2o_object.pk = o2o_object.pk
                    key_o2o_object.index = o2o_object.index
                    key_o2o_object.default = o2o_object.default
                    key_o2o_object.null = o2o_object.null
                    key_o2o_object.unique = o2o_object.unique
                    key_o2o_object.generated = o2o_object.generated
                    key_o2o_object.reference = o2o_object
                    key_o2o_object.description = o2o_object.description
                    if o2o_object.source_field:
                        key_o2o_object.source_field = o2o_object.source_field
                    else:
                        key_o2o_object.source_field = key_field
                    model._meta.add_field(key_field, key_o2o_object)

                    o2o_object.related_model = related_model
                    o2o_object.source_field = key_field
                    backward_relation_name = o2o_object.related_name
                    if backward_relation_name is not False:
                        if not backward_relation_name:
                            backward_relation_name = f"{model._meta.db_table}"
                        if backward_relation_name in related_model._meta.fields:
                            raise ConfigurationError(
                                f'backward relation "{backward_relation_name}" duplicates in'
                                f" model {related_model_name}"
                            )
                        o2o_relation = BackwardOneToOneRelation(
                            model,
                            f"{field}_id",
                            key_o2o_object.source_field,
                            null=True,
                            description=o2o_object.description,
                        )
                        o2o_relation.to_field_instance = o2o_object.to_field_instance
                        related_model._meta.add_field(backward_relation_name, o2o_relation)

                    if o2o_object.pk:
                        model._meta.pk_attr = key_field

                for field in list(model._meta.m2m_fields):
                    m2m_object = cast(ManyToManyFieldInstance, model._meta.fields_map[field])
                    if m2m_object._generated:
                        continue

                    backward_key = m2m_object.backward_key
                    if not backward_key:
                        backward_key = f"{model._meta.db_table}_id"
                        if backward_key == m2m_object.forward_key:
                            backward_key = f"{model._meta.db_table}_rel_id"
                        m2m_object.backward_key = backward_key

                    reference = m2m_object.model_name
                    related_app_name, related_model_name = split_reference(reference)
                    related_model = get_related_model(related_app_name, related_model_name)

                    m2m_object.related_model = related_model

                    backward_relation_name = m2m_object.related_name
                    if not backward_relation_name:
                        backward_relation_name = (
                            m2m_object.related_name
                        ) = f"{model._meta.db_table}s"
                    if backward_relation_name in related_model._meta.fields:
                        raise ConfigurationError(
                            f'backward relation "{backward_relation_name}" duplicates in'
                            f" model {related_model_name}"
                        )

                    if not m2m_object.through:
                        related_model_table_name = (
                            related_model._meta.db_table
                            if related_model._meta.db_table
                            else related_model.__name__.lower()
                        )

                        m2m_object.through = f"{model._meta.db_table}_{related_model_table_name}"

                    m2m_relation = ManyToManyFieldInstance(
                        f"{app_name}.{model_name}",
                        m2m_object.through,
                        forward_key=m2m_object.backward_key,
                        backward_key=m2m_object.forward_key,
                        related_name=field,
                        field_type=model,
                        description=m2m_object.description,
                    )
                    m2m_relation._generated = True
                    model._meta.filters.update(get_m2m_filters(field, m2m_object))
                    related_model._meta.add_field(backward_relation_name, m2m_relation)
Exemplo n.º 2
0
    def _init_relations(cls) -> None:
        for app_name, app in cls.apps.items():
            for model_name, model in app.items():
                if model._meta._inited:
                    continue
                else:
                    model._meta._inited = True
                if not model._meta.table:
                    model._meta.table = model.__name__.lower()

                for field in model._meta.fk_fields:
                    field_object = cast(fields.ForeignKeyField,
                                        model._meta.fields_map[field])
                    reference = field_object.model_name
                    related_app_name, related_model_name = reference.split('.')
                    related_model = cls.apps[related_app_name][
                        related_model_name]
                    field_object.type = related_model
                    backward_relation_name = field_object.related_name
                    if not backward_relation_name:
                        backward_relation_name = '{}s'.format(
                            model._meta.table)
                    if backward_relation_name in related_model._meta.fields:
                        raise ConfigurationError(
                            'backward relation "{}" duplicates in model {}'.
                            format(backward_relation_name, related_model_name))
                    fk_relation = fields.BackwardFKRelation(
                        model, '{}_id'.format(field))
                    setattr(related_model, backward_relation_name, fk_relation)
                    related_model._meta.filters.update(
                        get_backward_fk_filters(backward_relation_name,
                                                fk_relation))

                    related_model._meta.backward_fk_fields.add(
                        backward_relation_name)
                    related_model._meta.fetch_fields.add(
                        backward_relation_name)
                    related_model._meta.fields_map[
                        backward_relation_name] = fk_relation
                    related_model._meta.fields.add(backward_relation_name)

                for field in model._meta.m2m_fields:
                    field_mobject = cast(fields.ManyToManyField,
                                         model._meta.fields_map[field])
                    if field_mobject._generated:
                        continue

                    backward_key = field_mobject.backward_key
                    if not backward_key:
                        backward_key = '{}_id'.format(model._meta.table)
                        field_mobject.backward_key = backward_key

                    reference = field_mobject.model_name
                    related_app_name, related_model_name = reference.split('.')
                    related_model = cls.apps[related_app_name][
                        related_model_name]

                    field_mobject.type = related_model

                    backward_relation_name = field_mobject.related_name
                    if not backward_relation_name:
                        backward_relation_name = field_mobject.related_name = \
                            '{}_through'.format(model._meta.table)
                    if backward_relation_name in related_model._meta.fields:
                        raise ConfigurationError(
                            'backward relation "{}" duplicates in model {}'.
                            format(backward_relation_name, related_model_name))

                    if not field_mobject.through:
                        related_model_table_name = (
                            related_model._meta.table
                            if related_model._meta.table else
                            related_model.__name__.lower())

                        field_mobject.through = '{}_{}'.format(
                            model._meta.table,
                            related_model_table_name,
                        )

                    m2m_relation = fields.ManyToManyField(
                        '{}.{}'.format(app_name, model_name),
                        field_mobject.through,
                        forward_key=field_mobject.backward_key,
                        backward_key=field_mobject.forward_key,
                        related_name=field,
                        type=model)
                    m2m_relation._generated = True
                    setattr(
                        related_model,
                        backward_relation_name,
                        m2m_relation,
                    )
                    model._meta.filters.update(
                        get_m2m_filters(field, field_mobject))
                    related_model._meta.filters.update(
                        get_m2m_filters(backward_relation_name, m2m_relation))
                    related_model._meta.m2m_fields.add(backward_relation_name)
                    related_model._meta.fetch_fields.add(
                        backward_relation_name)
                    related_model._meta.fields_map[
                        backward_relation_name] = m2m_relation
                    related_model._meta.fields.add(backward_relation_name)
Exemplo n.º 3
0
    def _init_relations(cls) -> None:
        def get_related_model(related_app_name: str, related_model_name: str):
            """
            Test, if app and model really exist. Throws a ConfigurationError with a hopefully
            helpful message. If successfull, returns the requested model.
            """
            try:
                return cls.apps[related_app_name][related_model_name]
            except KeyError:
                if related_app_name not in cls.apps:
                    raise ConfigurationError(
                        "No app with name '{}' registered.".format(
                            related_app_name))
                raise ConfigurationError(
                    "No model with name '{}' registered in app '{}'.".format(
                        related_model_name, related_app_name))

        def split_reference(reference: str) -> Tuple[str, str]:
            """
            Test, if reference follow the official naming conventions. Throws a
            ConfigurationError with a hopefully helpful message. If successfull,
            returns the app and the model name.
            """
            items = reference.split(".")
            if len(items) != 2:  # pragma: nocoverage
                raise ConfigurationError(
                    ("'%s' is not a valid model reference Bad Reference."
                     " Should be something like <appname>.<modelname>.") %
                    reference)

            return (items[0], items[1])

        for app_name, app in cls.apps.items():
            for model_name, model in app.items():
                if model._meta._inited:
                    continue
                model._meta._inited = True
                if not model._meta.table:
                    model._meta.table = model.__name__.lower()

                for field in model._meta.fk_fields:
                    fk_object = cast(fields.ForeignKeyField,
                                     model._meta.fields_map[field])
                    reference = fk_object.model_name
                    related_app_name, related_model_name = split_reference(
                        reference)
                    related_model = get_related_model(related_app_name,
                                                      related_model_name)

                    key_field = "{}_id".format(field)
                    key_fk_object = deepcopy(related_model._meta.pk)
                    key_fk_object.pk = False
                    key_fk_object.index = fk_object.index
                    key_fk_object.default = fk_object.default
                    key_fk_object.null = fk_object.null
                    key_fk_object.generated = fk_object.generated
                    key_fk_object.reference = fk_object
                    key_fk_object.description = fk_object.description
                    if fk_object.source_field:
                        key_fk_object.source_field = fk_object.source_field
                        fk_object.source_field = key_field
                    else:
                        fk_object.source_field = key_field
                        key_fk_object.source_field = key_field
                    model._meta.add_field(key_field, key_fk_object)

                    fk_object.type = related_model
                    backward_relation_name = fk_object.related_name
                    if not backward_relation_name:
                        backward_relation_name = "{}s".format(
                            model._meta.table)
                    if backward_relation_name in related_model._meta.fields:
                        raise ConfigurationError(
                            'backward relation "{}" duplicates in model {}'.
                            format(backward_relation_name, related_model_name))
                    fk_relation = fields.BackwardFKRelation(
                        model, "{}_id".format(field), fk_object.null,
                        fk_object.description)
                    related_model._meta.add_field(backward_relation_name,
                                                  fk_relation)

                for field in list(model._meta.m2m_fields):
                    m2m_object = cast(fields.ManyToManyField,
                                      model._meta.fields_map[field])
                    if m2m_object._generated:
                        continue

                    backward_key = m2m_object.backward_key
                    if not backward_key:
                        backward_key = "{}_id".format(model._meta.table)
                        if backward_key == m2m_object.forward_key:
                            backward_key = "{}_rel_id".format(
                                model._meta.table)
                        m2m_object.backward_key = backward_key

                    reference = m2m_object.model_name
                    related_app_name, related_model_name = split_reference(
                        reference)
                    related_model = get_related_model(related_app_name,
                                                      related_model_name)

                    m2m_object.type = related_model

                    backward_relation_name = m2m_object.related_name
                    if not backward_relation_name:
                        backward_relation_name = m2m_object.related_name = "{}_through".format(
                            model._meta.table)
                    if backward_relation_name in related_model._meta.fields:
                        raise ConfigurationError(
                            'backward relation "{}" duplicates in model {}'.
                            format(backward_relation_name, related_model_name))

                    if not m2m_object.through:
                        related_model_table_name = (
                            related_model._meta.table
                            if related_model._meta.table else
                            related_model.__name__.lower())

                        m2m_object.through = "{}_{}".format(
                            model._meta.table, related_model_table_name)

                    m2m_relation = fields.ManyToManyField(
                        "{}.{}".format(app_name, model_name),
                        m2m_object.through,
                        forward_key=m2m_object.backward_key,
                        backward_key=m2m_object.forward_key,
                        related_name=field,
                        type=model,
                        description=m2m_object.description,
                    )
                    m2m_relation._generated = True
                    model._meta.filters.update(
                        get_m2m_filters(field, m2m_object))
                    related_model._meta.add_field(backward_relation_name,
                                                  m2m_relation)
Exemplo n.º 4
0
    def _init_relations(cls) -> None:
        for app_name, app in cls.apps.items():
            for model_name, model in app.items():
                if model._meta._inited:
                    continue
                else:
                    model._meta._inited = True
                if not model._meta.table:
                    model._meta.table = model.__name__.lower()

                for field in model._meta.fk_fields:
                    fk_object = cast(fields.ForeignKeyField,
                                     model._meta.fields_map[field])
                    reference = fk_object.model_name
                    related_app_name, related_model_name = reference.split(".")
                    related_model = cls.apps[related_app_name][
                        related_model_name]

                    key_field = "{}_id".format(field)
                    fk_object.source_field = key_field
                    key_fk_object = deepcopy(related_model._meta.pk)
                    key_fk_object.pk = False
                    key_fk_object.index = fk_object.index
                    key_fk_object.default = fk_object.default
                    key_fk_object.null = fk_object.null
                    key_fk_object.generated = fk_object.generated
                    key_fk_object.reference = fk_object
                    model._meta.add_field(key_field, key_fk_object)

                    fk_object.type = related_model
                    backward_relation_name = fk_object.related_name
                    if not backward_relation_name:
                        backward_relation_name = "{}s".format(
                            model._meta.table)
                    if backward_relation_name in related_model._meta.fields:
                        raise ConfigurationError(
                            'backward relation "{}" duplicates in model {}'.
                            format(backward_relation_name, related_model_name))
                    fk_relation = fields.BackwardFKRelation(
                        model, "{}_id".format(field))
                    related_model._meta.add_field(backward_relation_name,
                                                  fk_relation)

                for field in model._meta.m2m_fields:
                    m2m_object = cast(fields.ManyToManyField,
                                      model._meta.fields_map[field])
                    if m2m_object._generated:
                        continue

                    backward_key = m2m_object.backward_key
                    if not backward_key:
                        backward_key = "{}_id".format(model._meta.table)
                        m2m_object.backward_key = backward_key

                    reference = m2m_object.model_name
                    related_app_name, related_model_name = reference.split(".")
                    related_model = cls.apps[related_app_name][
                        related_model_name]

                    m2m_object.type = related_model

                    backward_relation_name = m2m_object.related_name
                    if not backward_relation_name:
                        backward_relation_name = m2m_object.related_name = "{}_through".format(
                            model._meta.table)
                    if backward_relation_name in related_model._meta.fields:
                        raise ConfigurationError(
                            'backward relation "{}" duplicates in model {}'.
                            format(backward_relation_name, related_model_name))

                    if not m2m_object.through:
                        related_model_table_name = (
                            related_model._meta.table
                            if related_model._meta.table else
                            related_model.__name__.lower())

                        m2m_object.through = "{}_{}".format(
                            model._meta.table, related_model_table_name)

                    m2m_relation = fields.ManyToManyField(
                        "{}.{}".format(app_name, model_name),
                        m2m_object.through,
                        forward_key=m2m_object.backward_key,
                        backward_key=m2m_object.forward_key,
                        related_name=field,
                        type=model,
                    )
                    m2m_relation._generated = True
                    model._meta.filters.update(
                        get_m2m_filters(field, m2m_object))
                    related_model._meta.add_field(backward_relation_name,
                                                  m2m_relation)