Beispiel #1
0
    def _unpack(self, name, attrib, load_collections):
        "Helper method to unpack SQLalchemy objects"
        if attrib is None:
            return

        if name == "metatags":
            return self._unpack_metatags(attrib)

        #log.d("name:", name, "attrib:", attrib)
        # beware lots of recursion
        if db.is_instanced(attrib):
            msg_obj = None

            exclude = (db.NameMixin.__name__, )

            for cls_name, cls_obj in self._db_clsmembers:
                if cls_name not in exclude:
                    if isinstance(attrib, cls_obj):
                        if cls_name in self._clsmembers:
                            msg_obj = self._clsmembers[cls_name](attrib)
                            break

            if not msg_obj:
                if isinstance(attrib, db.NameMixin):
                    msg_obj = NameMixin(attrib, name)
                else:
                    raise NotImplementedError(
                        "Message encapsulation for this database object does not exist ({})"
                        .format(type(attrib)))

            return msg_obj.data() if msg_obj else None

        elif db.is_list(attrib) or isinstance(attrib, list):
            return [self._unpack(name, x, load_collections) for x in attrib]

        elif db.is_query(attrib):
            if load_collections:
                return [
                    self._unpack(name, x, load_collections)
                    for x in attrib.all()
                ]
            else:
                return []

        elif isinstance(attrib, enum.Enum):
            return attrib.name

        elif isinstance(attrib, datetime):
            return attrib.timestamp()

        elif isinstance(attrib, arrow.Arrow):
            return attrib.timestamp

        elif isinstance(attrib, (bool, int, float, str, dict)):
            return attrib
        else:
            raise NotImplementedError(
                "Unpacking method for this attribute does not exist ({})".
                format(type(attrib)))
Beispiel #2
0
 def _unpack_metatags(self, attrib):
     m_tags = {x: False for x in db.MetaTag.all_names()}
     names = []
     if db.is_query(attrib):
         names = tuple(x.name for x in attrib.all())
     elif db.is_list(attrib) or isinstance(attrib, list):
         names = tuple(x.name for x in attrib)
     for n in names:
         m_tags[n] = True
     return m_tags
Beispiel #3
0
    def _unpack(self, name, attrib, load_values, load_collections, propagate_bypass):
        "Helper method to unpack SQLalchemy objects"
        if attrib is None:
            return

        if name == "metatags":
            return self._unpack_metatags(attrib)

        #log.d("name:", name, "attrib:", attrib)
        # beware lots of recursion
        if db.is_instanced(attrib):
            msg_obj = self._get_message_object(self, name, attrib)
            if msg_obj is False:  # recursive checked
                return None

            msg_obj._indirect = True
            msg_obj._properties['force_value_load'] = utils.dict_merge(
                msg_obj._properties['force_value_load'], self._properties['force_value_load'].get(name, {}))
            msg_obj._properties['exclusions'] = self._properties['exclusions'].get(name, {})
            if self._detached:
                msg_obj._detached = self._detached
            msg_obj._msg_path = self._msg_path.copy()
            # note: don't pass load_collections here. use the force_value_load param
            return msg_obj.data(load_values=load_values, bypass_exclusions=propagate_bypass,
                                propagate_bypass=propagate_bypass) if msg_obj else None

        elif db.is_list(attrib) or isinstance(attrib, list):
            return [self._unpack(name, x, load_values, load_collections, propagate_bypass) for x in attrib]

        elif db.is_query(attrib):
            can_load = False
            if name in self._properties['force_value_load']:
                can_load = True
            if can_load or (load_collections and not self._detached):
                return [self._unpack(name, x, load_values, load_collections, propagate_bypass)
                        for x in attrib.all()]
            else:
                raise DatabaseMessage.NoUnpack

        elif isinstance(attrib, enum.Enum):
            return attrib.name

        elif isinstance(attrib, datetime):
            return attrib.timestamp()

        elif isinstance(attrib, arrow.Arrow):
            return attrib.timestamp

        elif isinstance(attrib, (bool, int, float, str, dict)):
            return attrib
        else:
            raise NotImplementedError(
                "Unpacking method for this attribute does not exist ({})".format(
                    type(attrib)))
Beispiel #4
0
    def from_json(cls,
                  msg,
                  ignore_empty=True,
                  skip_updating_existing=True,
                  skip_descriptors=False,
                  _type=None):
        db_obj = None
        with db.no_autoflush(constants.db_session()) as sess:
            if not cls.db_type and _type is None:
                raise ValueError("A database type has not been set")
            db_type = _type or cls.db_type
            item_attrs = db.table_attribs(db_type, id=None, descriptors=True)

            if msg and not all((k in item_attrs.keys() for k in msg.keys())):
                m_keys = set(msg.keys()).difference(set(item_attrs.keys()))
                raise exceptions.InvalidMessage(
                    utils.this_function(),
                    f"Message object mismatch. Message contains keys that are not present in the corresponding database item: {m_keys}"
                )

            if msg:
                obj_id = msg.get('id', False)
                if obj_id:
                    db_obj = sess.query(db_type).get(obj_id)
                else:
                    db_obj = db_type()

                if not (obj_id and db_obj and skip_updating_existing):
                    for attr, value in msg.items():
                        if ignore_empty:
                            if value is None:
                                continue
                            elif isinstance(value, (list, dict)) and not value:
                                continue

                        cls_attr = item_attrs[attr]
                        if skip_descriptors and db.is_descriptor(cls_attr):
                            continue
                        obj_attr = getattr(db_obj, attr)
                        try:
                            col_model = db.column_model(cls_attr)
                        except TypeError:  # most likely a hybrid_property descriptor
                            col_model = None
                        if not issubclass(col_model,
                                          db.Base) if inspect.isclass(
                                              col_model) else not isinstance(
                                                  col_model, db.Base):
                            if isinstance(
                                    col_model,
                                (db.Boolean, db.Integer, db.Password,
                                 db.String, db.Text, db.LowerCaseString,
                                 db.CapitalizedString)):
                                setattr(db_obj, attr, value)
                            elif isinstance(col_model, db.ArrowType):
                                setattr(db_obj, attr,
                                        arrow.Arrow.fromtimestamp(value))
                            elif db.is_descriptor(cls_attr):
                                if db.descriptor_has_setter(cls_attr):
                                    raise NotImplementedError
                            else:
                                raise NotImplementedError(
                                    f"Value deserializing for this database type does not exist ({col_model})"
                                )
                            continue

                        msg_obj = cls._get_message_object(
                            cls,
                            attr,
                            col_model,
                            check_recursive=False,
                            class_type=True)
                        if issubclass(col_model, db.MetaTag):
                            setattr(db_obj, attr,
                                    msg_obj._pack_metatags(value))
                        elif db.is_list(cls_attr) or db.is_query(cls_attr):
                            for v in value:
                                obj_attr.append(
                                    msg_obj.from_json(
                                        v,
                                        _type=col_model,
                                        ignore_empty=ignore_empty,
                                        skip_updating_existing=
                                        skip_updating_existing,
                                        skip_descriptors=skip_descriptors))
                        elif db.is_descriptor(cls_attr):
                            if db.descriptor_has_setter(cls_attr):
                                raise NotImplementedError
                        else:
                            setattr(
                                db_obj, attr,
                                msg_obj.from_json(
                                    value,
                                    _type=col_model,
                                    ignore_empty=ignore_empty,
                                    skip_updating_existing=
                                    skip_updating_existing,
                                    skip_descriptors=skip_descriptors))

        return db_obj
Beispiel #5
0
    def from_json(cls, msg, ignore_empty=True, skip_updating_existing=True,
                  skip_descriptors=False, _type=None,
                  ignore_private=True, _from_attr=''):
        db_obj = None
        if not msg:
            return db_obj

        if not isinstance(msg, dict):
            raise exceptions.InvalidMessage(
                utils.this_function(),
                f"Expected message object on '{_from_attr}' not '{type(msg).__name__}'")

        with db.no_autoflush(constants.db_session()) as sess:
            if not cls.db_type and _type is None:
                raise ValueError("A database type has not been set")
            db_type = _type or cls.db_type
            item_attrs = db.table_attribs(db_type, id=None, descriptors=True)

            if msg and not all((k in item_attrs.keys() for k in msg.keys())):
                m_keys = set(msg.keys()).difference(set(item_attrs.keys()))
                raise exceptions.InvalidMessage(
                    utils.this_function(),
                    f"Message object mismatch. '{_from_attr}' contains keys that are not present in the corresponding database item: {m_keys}")

            if msg:
                new_obj = True
                obj_id = msg.get('id', False)
                if obj_id:
                    db_obj = sess.query(db_type).get(obj_id)
                    if not db_obj:
                        raise exceptions.CoreError(utils.this_function(),
                                                   f"Existing item from message object with id '{obj_id}' was not found")
                    new_obj = False
                else:
                    db_obj = db_type()

                if new_obj and isinstance(db_obj, db.UniqueMixin):
                    if isinstance(db_obj, db.NameMixin) and msg.get('name') is not None:
                        db_obj = db_type.as_unique(name=msg.get('name'))
                        new_obj = bool(db_obj.id)
                    elif isinstance(db_obj, db.NamespaceTags):
                        ns_name = None
                        tag_name = None
                        try:
                            ns_name = utils.get_dict_value('namespace.name', msg)
                            tag_name = utils.get_dict_value('tag.name', msg)
                        except KeyError:
                            pass
                        if ns_name is not None and tag_name is not None:
                            db_obj = db_type.as_unique(ns=ns_name, tag=tag_name)
                            new_obj = bool(db_obj.id)
                    elif isinstance(db_obj, (db.Artist, db.Parody)):
                        a_names = set()
                        if msg.get('preferred_name') and msg['preferred_name'].get('name') is not None:
                            a_names.add(msg['preferred_name']['name'])
                        if msg.get('names'):
                            for n in msg['names']:
                                if n.get('name') is not None:
                                    a_names.add(n['name'])
                        if a_names:
                            db_obj = db_type.as_unique(names=a_names)
                            new_obj = bool(db_obj.id)

                if db_obj and not (obj_id and db_obj and skip_updating_existing):
                    for attr, value in msg.items():
                        if attr == 'id':
                            continue
                        if ignore_private and attr.startswith('_'):
                            continue
                        if ignore_empty:
                            if value is None:
                                continue
                            elif isinstance(value, (list, dict)) and not value:
                                continue

                        cls_attr = item_attrs[attr]
                        if skip_descriptors and db.is_descriptor(cls_attr):
                            continue
                        obj_attr = getattr(db_obj, attr) # noqa: F841
                        try:
                            col_model = db.column_model(cls_attr)
                        except TypeError:  # most likely a hybrid_property descriptor
                            col_model = None
                        if not issubclass(col_model, db.Base) if inspect.isclass(
                                col_model) else not isinstance(col_model, db.Base):
                            if isinstance(col_model, (db.Boolean, db.Integer, db.Password,
                                                      db.String, db.Text, db.LowerCaseString,
                                                      db.CapitalizedString)):
                                setattr(db_obj, attr, value)
                            elif isinstance(col_model, db.ArrowType):
                                setattr(db_obj, attr, arrow.Arrow.fromtimestamp(value))
                            elif db.is_descriptor(cls_attr):
                                if db.descriptor_has_setter(cls_attr):
                                    raise NotImplementedError
                            else:
                                raise NotImplementedError(
                                    f"Value deserializing for this database type does not exist ({col_model})")
                            continue

                        msg_obj = cls._get_message_object(cls, attr, col_model, check_recursive=False, class_type=True)
                        if col_model == db.Taggable and isinstance(value, dict):
                            if db_obj.taggable and db_obj.taggable.id:
                                value['id'] = db_obj.taggable.id
                        if issubclass(col_model, db.MetaTag):
                            setattr(db_obj, attr, msg_obj._pack_metatags(value))
                        elif db.is_list(cls_attr) or db.is_query(cls_attr):
                            n_l = []
                            for v in value:
                                o = msg_obj.from_json(
                                    v,
                                    _type=col_model,
                                    ignore_empty=ignore_empty,
                                    skip_updating_existing=skip_updating_existing,
                                    skip_descriptors=skip_descriptors,
                                    _from_attr=_from_attr + '.' + attr if _from_attr else attr,
                                )
                                if o is not None:
                                    n_l.append(o)
                            setattr(db_obj, attr, n_l)
                        elif db.is_descriptor(cls_attr):
                            if db.descriptor_has_setter(cls_attr):
                                raise NotImplementedError
                        else:
                            setattr(
                                db_obj,
                                attr,
                                msg_obj.from_json(
                                    value,
                                    _type=col_model,
                                    _from_attr=_from_attr + '.' + attr if _from_attr else attr,
                                    ignore_empty=ignore_empty,
                                    skip_updating_existing=skip_updating_existing,
                                    skip_descriptors=skip_descriptors))

        return db_obj