Exemplo n.º 1
0
    def add_field(self, name: str, value: Field):
        if name in self.fields_map:
            raise ConfigurationError(
                "Field {} already present in meta".format(name))
        setattr(self._model, name, value)
        value.model = self._model
        self.fields_map[name] = value
        self._fields = None

        if value.has_db_field:
            self.fields_db_projection[name] = value.source_field or name
            self._fields_db_projection_reverse = None

        if isinstance(value, fields.ManyToManyField):
            self.m2m_fields.add(name)
            self._fetch_fields = None
        elif isinstance(value, fields.BackwardFKRelation):
            self.backward_fk_fields.add(name)
            self._fetch_fields = None

        field_filters = get_filters_for_field(field_name=name,
                                              field=value,
                                              source_field=value.source_field
                                              or name)
        self._filters.update(field_filters)
        self.generate_filters()
Exemplo n.º 2
0
    def add_field(self, name: str, value: Field):
        if name in self.fields_map:
            raise ConfigurationError(f"Field {name} already present in meta")
        value.model = self._model
        self.fields_map[name] = value

        if value.has_db_field:
            self.fields_db_projection[name] = value.source_field or name

        if isinstance(value, fields.ManyToManyField):
            self.m2m_fields.add(name)
        elif isinstance(value, fields.BackwardFKRelation):
            self.backward_fk_fields.add(name)

        field_filters = get_filters_for_field(
            field_name=name, field=value, source_field=value.source_field or name
        )
        self._filters.update(field_filters)
        self.finalise_fields()
Exemplo n.º 3
0
    def __new__(mcs, name: str, bases, attrs: dict, *args, **kwargs):
        fields_db_projection: Dict[str, str] = {}
        fields_map: Dict[str, fields.Field] = {}
        filters: Dict[str, Dict[str, dict]] = {}
        fk_fields: Set[str] = set()
        m2m_fields: Set[str] = set()
        o2o_fields: Set[str] = set()
        meta_class = attrs.get("Meta", type("Meta", (), {}))
        pk_attr: str = "id"

        # Searching for Field attributes in the class hierarchy
        def __search_for_field_attributes(base, attrs: dict):
            """
            Searching for class attributes of type fields.Field
            in the given class.

            If an attribute of the class is an instance of fields.Field,
            then it will be added to the fields dict. But only, if the
            key is not already in the dict. So derived classes have a higher
            precedence. Multiple Inheritance is supported from left to right.

            After checking the given class, the function will look into
            the classes according to the MRO (method resolution order).

            The MRO is 'natural' order, in which python traverses methods and
            fields. For more information on the magic behind check out:
            `The Python 2.3 Method Resolution Order
            <https://www.python.org/download/releases/2.3/mro/>`_.
            """
            for parent in base.__mro__[1:]:
                __search_for_field_attributes(parent, attrs)
            meta = getattr(base, "_meta", None)
            if meta:
                # For abstract classes
                for key, value in meta.fields_map.items():
                    attrs[key] = value
            else:
                # For mixin classes
                for key, value in base.__dict__.items():
                    if isinstance(value, fields.Field) and key not in attrs:
                        attrs[key] = value

        # Start searching for fields in the base classes.
        inherited_attrs: dict = {}
        for base in bases:
            __search_for_field_attributes(base, inherited_attrs)
        if inherited_attrs:
            # Ensure that the inherited fields are before the defined ones.
            attrs = {**inherited_attrs, **attrs}

        if name != "Model":
            custom_pk_present = False
            for key, value in attrs.items():
                if isinstance(value, fields.Field):
                    if value.pk:
                        if custom_pk_present:
                            raise ConfigurationError(
                                f"Can't create model {name} with two primary keys,"
                                " only single pk are supported")
                        if value.generated and not isinstance(
                                value, (fields.SmallIntField, fields.IntField,
                                        fields.BigIntField)):
                            raise ConfigurationError(
                                "Generated primary key allowed only for IntField and BigIntField"
                            )
                        custom_pk_present = True
                        pk_attr = key

            if not custom_pk_present and not getattr(meta_class, "abstract",
                                                     None):
                if "id" not in attrs:
                    attrs = {"id": fields.IntField(pk=True), **attrs}

                if not isinstance(attrs["id"],
                                  fields.Field) or not attrs["id"].pk:
                    raise ConfigurationError(
                        f"Can't create model {name} without explicit primary key if field 'id'"
                        " already present")

            for key, value in attrs.items():
                if isinstance(value, fields.Field):
                    if getattr(meta_class, "abstract", None):
                        value = deepcopy(value)

                    fields_map[key] = value
                    value.model_field_name = key

                    if isinstance(value, fields.ForeignKeyField):
                        fk_fields.add(key)
                    elif isinstance(value, fields.OneToOneField):
                        o2o_fields.add(key)
                    elif isinstance(value, fields.ManyToManyFieldInstance):
                        m2m_fields.add(key)
                    else:
                        fields_db_projection[key] = value.source_field or key
                        filters.update(
                            get_filters_for_field(
                                field_name=key,
                                field=fields_map[key],
                                source_field=fields_db_projection[key],
                            ))
                        if value.pk:
                            filters.update(
                                get_filters_for_field(
                                    field_name="pk",
                                    field=fields_map[key],
                                    source_field=fields_db_projection[key],
                                ))

        # Clean the class attributes
        for slot in fields_map:
            attrs.pop(slot, None)
        attrs["_meta"] = meta = MetaInfo(meta_class)

        meta.fields_map = fields_map
        meta.fields_db_projection = fields_db_projection
        meta._filters = filters
        meta.fk_fields = fk_fields
        meta.backward_fk_fields = set()
        meta.o2o_fields = o2o_fields
        meta.backward_o2o_fields = set()
        meta.m2m_fields = m2m_fields
        meta.default_connection = None
        meta.pk_attr = pk_attr
        meta._inited = False
        if not fields_map:
            meta.abstract = True

        new_class: "Model" = super().__new__(mcs, name, bases,
                                             attrs)  # type: ignore
        for field in meta.fields_map.values():
            field.model = new_class

        meta._model = new_class
        meta.finalise_fields()
        return new_class
Exemplo n.º 4
0
    def __new__(mcs, name: str, bases, attrs: dict, *args, **kwargs):
        fields_db_projection = {}  # type: Dict[str,str]
        fields_map = {}  # type: Dict[str, fields.Field]
        filters = {}  # type: Dict[str, Dict[str, dict]]
        fk_fields = set()  # type: Set[str]
        m2m_fields = set()  # type: Set[str]

        if 'id' not in attrs:
            attrs['id'] = fields.IntField(pk=True)

        for key, value in attrs.items():
            if isinstance(value, fields.Field):
                fields_map[key] = value
                value.model_field_name = key
                if isinstance(value, fields.ForeignKeyField):
                    key_field = '{}_id'.format(key)
                    value.source_field = key_field
                    fields_db_projection[key_field] = key_field
                    fields_map[key_field] = fields.IntField(
                        reference=value,
                        null=value.null,
                        default=value.default,
                    )
                    filters.update(
                        get_filters_for_field(
                            field_name=key_field,
                            field=fields_map[key_field],
                            source_field=key_field,
                        ))
                    fk_fields.add(key)
                elif isinstance(value, fields.ManyToManyField):
                    m2m_fields.add(key)
                else:
                    fields_db_projection[
                        key] = value.source_field if value.source_field else key
                    filters.update(
                        get_filters_for_field(
                            field_name=key,
                            field=fields_map[key],
                            source_field=fields_db_projection[key]))

        attrs['_meta'] = meta = MetaInfo(attrs.get('Meta'))

        meta.fields_map = fields_map
        meta.fields_db_projection = fields_db_projection
        meta.fields_db_projection_reverse = {
            value: key
            for key, value in fields_db_projection.items()
        }
        meta.fields = set(fields_map.keys())
        meta.db_fields = set(fields_db_projection.values())
        meta._filters = filters
        meta.fk_fields = fk_fields
        meta.backward_fk_fields = set()
        meta.m2m_fields = m2m_fields
        meta.fetch_fields = fk_fields | m2m_fields
        meta.default_connection = None
        meta._inited = False
        if not fields_map:
            meta.abstract = True

        new_class = super().__new__(mcs, name, bases, attrs)

        return new_class
Exemplo n.º 5
0
    def __new__(mcs, name: str, bases, attrs: dict, *args, **kwargs):
        fields_db_projection = {}  # type: Dict[str,str]
        fields_map = {}  # type: Dict[str, fields.Field]
        filters = {}  # type: Dict[str, Dict[str, dict]]
        fk_fields = set()  # type: Set[str]
        m2m_fields = set()  # type: Set[str]
        meta_class = attrs.get("Meta", type("Meta", (), {}))
        pk_attr = "id"

        if name != "Model":
            custom_pk_present = False
            for key, value in attrs.items():
                if isinstance(value, fields.Field):
                    if value.pk:
                        if custom_pk_present:
                            raise ConfigurationError(
                                "Can't create model {} with two primary keys, "
                                "only single pk are supported".format(name)
                            )
                        elif value.generated and not isinstance(
                            value, (fields.IntField, fields.BigIntField)
                        ):
                            raise ConfigurationError(
                                "Generated primary key allowed only for IntField and BigIntField"
                            )
                        custom_pk_present = True
                        pk_attr = key

            if not custom_pk_present:
                if "id" not in attrs:
                    attrs["id"] = fields.IntField(pk=True)

                if not isinstance(attrs["id"], fields.Field) or not attrs["id"].pk:
                    raise ConfigurationError(
                        "Can't create model {} without explicit primary key "
                        "if field 'id' already present".format(name)
                    )

            for key, value in attrs.items():
                if isinstance(value, fields.Field):
                    if getattr(meta_class, "abstract", None):
                        value = deepcopy(value)

                    fields_map[key] = value
                    value.model_field_name = key

                    if isinstance(value, fields.ForeignKeyField):
                        fk_fields.add(key)
                    elif isinstance(value, fields.ManyToManyField):
                        m2m_fields.add(key)
                    else:
                        fields_db_projection[key] = value.source_field or key
                        filters.update(
                            get_filters_for_field(
                                field_name=key,
                                field=fields_map[key],
                                source_field=fields_db_projection[key],
                            )
                        )
                        if value.pk:
                            filters.update(
                                get_filters_for_field(
                                    field_name="pk",
                                    field=fields_map[key],
                                    source_field=fields_db_projection[key],
                                )
                            )

        attrs["_meta"] = meta = MetaInfo(meta_class)

        meta.fields_map = fields_map
        meta.fields_db_projection = fields_db_projection
        meta._filters = filters
        meta.fk_fields = fk_fields
        meta.backward_fk_fields = set()
        meta.m2m_fields = m2m_fields
        meta.default_connection = None
        meta.pk_attr = pk_attr
        meta._inited = False
        if not fields_map:
            meta.abstract = True

        new_class = super().__new__(mcs, name, bases, attrs)  # type: "Model"  # type: ignore
        for field in meta.fields_map.values():
            field.model = new_class

        meta._model = new_class
        return new_class