Esempio n. 1
0
    def __init__(self, spec=None):

        if spec is None:
            spec = ''

        if not isinstance(spec, str):
            raise Error('merge argument must be a string')

        merge_operator, merge_count, exclude_operator, exclude_count = '=', 0, '=', 0
        match = re.match(r'([><]?=?)(\d+)(,([><]?=?)(\d+))?', spec)
        if match:
            merge_operator, merge_count, _, exclude_operator, exclude_count = match.groups(
            )

        self.merge_operator = MODIFIERS[
            merge_operator] if merge_operator else MODIFIERS['=']
        self.exclude_operator = MODIFIERS[
            exclude_operator] if exclude_operator else MODIFIERS['=']

        try:
            self.merge_count = int(merge_count) if merge_count else 0
            self.exclude_count = int(exclude_count) if exclude_count else 0
        except ValueError:
            raise Error('invalid merge argument: {!r}'.format(spec))

        if self.merge_count < 0 or self.exclude_count < 0:
            raise Error('invalid merge argument: {!r}'.format(spec))
Esempio n. 2
0
    def __init__(self, size, number):

        self.limit = None
        self.offset = 0

        if size is None and number is not None:
            raise Error('please provide page[size]'.format(size))

        if size is not None:
            try:
                self.limit = int(size)
            except ValueError:
                raise Error('invalid value for page[size]: {!r}'.format(size))
            else:
                if self.limit <= 0:
                    raise Error(
                        'invalid value for page[size]: {!r}'.format(size))

        if number is not None:
            try:
                self.offset = (int(number) - 1) * self.limit
            except ValueError:
                raise Error(
                    'invalid value for page[number]: {!r}'.format(number))
            else:
                if int(number) <= 0:
                    raise Error(
                        'invalid value for page[number]: {!r}'.format(number))
Esempio n. 3
0
    def get(self, expr, op, val):

        #
        # multiple values
        #
        if ',' in val:
            if not self.has_operator(op, multiple=True):
                raise Error('invalid operator: {}'.format(op))

            values = self.parse_values(val)
            if all(mod == '=' for mod, _ in values):
                if op in ('', 'eq'):
                    return expr.in_(val for _, val in values)
                elif op == 'ne':
                    return expr.notin_(val for _, val in values)
                else:
                    raise Error('invalid operator: {}')
            else:
                expressions = list()
                for i, (mod, val) in enumerate(values):
                    if val is True or val is False or val is None:
                        if mod == '=':
                            expressions.append(expr.is_(val))
                        elif mod in ('!=', '<>'):
                            expressions.append(expr.isnot(val))
                        else:
                            raise Error('invalid modifier: {}'.format(mod))
                    else:
                        and_expr = list()
                        and_expr.append(MODIFIERS[mod](expr, val))

                        if i > 0:
                            val_before = values[i - 1][1]
                            if val_before is not None:
                                and_expr.append(MODIFIERS['>'](expr, values[i - 1][1]))
                        if i < len(values) - 1:
                            val_after = values[i + 1][1]
                            if val_after is not None:
                                and_expr.append(MODIFIERS['<'](expr, values[i + 1][1]))
                        expressions.append(and_(*and_expr))
                return or_(*expressions)

        #
        # single values
        #
        else:
            op = 'eq' if not op else op
            if not self.has_operator(op):
                raise Error('invalid operator: {}'.format(op))

            v = self.data_type.parse(val)
            if v is False or v is True or v is None:
                if op == 'eq':
                    return expr.is_(v)
                if op == 'ne':
                    return expr.isnot(v)
                raise Error('invalid operator: {}'.format(op))
            return getattr(operators, op)(expr, v)
Esempio n. 4
0
    def check_refs(self, refs):
        for ref in refs:
            if not isinstance(ref, Column):
                raise Error('invalid "ref" value: {!r}'.format(ref))

        if self.cardinality == Cardinality.MANY_TO_MANY and len(refs) != 2:
            raise Error('two "ref" columns required: {}'.format(', '.join(r.name) for r in refs))

        if self.cardinality in (Cardinality.MANY_TO_ONE, Cardinality.ONE_TO_MANY) and len(refs) != 1:
            raise Error('one "ref" column required: {}'.format(', '.join(r.name) for r in refs))

        if self.cardinality == Cardinality.ONE_TO_ONE and len(refs) > 1:
            raise Error('too many "ref" columns: {}'.format(', '.join(r.name) for r in refs))
Esempio n. 5
0
 def get_type(cls):
     if cls.type_ is None:
         type_ = dasherize(underscore(cls.__name__))
         return type_.replace('model', '').strip('-')
     if not isinstance(cls.type_, str):
         raise Error('"type_" must be a string')
     return cls.type_
Esempio n. 6
0
 def __init__(self, args):
     """
     :param args: a dictionary representing the request query string
     """
     args = args if args else dict()
     try:
         self.include = tuple(
             AttributePath(path) for path in args['include'].split(
                 ',')) if 'include' in args else ()
         self.fields = {
             f.type: f
             for f in (FieldArgument(k, args[k]) for k in args.keys()
                       if k.startswith('fields'))
         }
         self.sort = tuple(
             SortArgument(spec)
             for spec in args['sort'].split(',')) if 'sort' in args else ()
         self.filter = self._group_filter_args(
             FilterArgument(k, args[k]) for k in args.keys()
             if k.startswith('filter'))
         self.page = PageArgument(args.get('page[size]', None),
                                  args.get('page[number]', None))
         self.merge = MergeArgument(
             args['merge']) if 'merge' in args else None
         self.options = {
             o.name: o.value
             for o in (OptionArgument(k, v) for k, v in args.items()
                       if k.startswith('option'))
         }
     except (AttributeError, TypeError):
         raise Error(
             'argument parser | invalid dictionary: {!r}'.format(args))
Esempio n. 7
0
    def __init__(self, table, **kwargs):
        """
        :param table: an SQLAlchemy Table or Alias object
        :param onclause: an onclause join expression (optional)
        :param left: if set perform outer left join (optional)
        """
        self.table = table
        self.onclause = kwargs.get('onclause', None)
        self.left = bool(kwargs.get('left', False))

        if not isinstance(self.table, Selectable):
            raise Error('[FromItem] invalid "table" argument: {}'.format(self.table))

        if self.onclause is not None:
            if not is_clause(self.onclause):
                raise Error('[FromItem] invalid "onclause" argument: {}'.format(
                    self.onclause))
Esempio n. 8
0
 def __init__(self, value):
     try:
         order, dot_path = re.search(r'([-+]?)(.+)', value).groups()
     except AttributeError:
         raise Error('invalid sort argument: {!r}'.format(value))
     else:
         self.desc = order == '-'
         self.path = AttributePath(dot_path)
Esempio n. 9
0
    def __init__(self, spec, value):

        try:
            name = re.match(r'option\[([-_\w]+)\]', spec).group(1)
        except AttributeError:
            raise Error('invalid option parameter: {!r}'.format(spec))
        else:
            self.name = camelize(name, False)
            self.value = value
Esempio n. 10
0
    def __init__(self, spec, value):

        try:
            self.type = re.search(r'^fields\[([-_\w]+)\]$', spec).group(1)
        except AttributeError:
            raise Error('invalid fieldset spec: {!r}'.format(spec))
        else:
            self.names = tuple(
                set(underscore(x) for x in value.split(',') + ['id']))
Esempio n. 11
0
    def __init__(self, name, data_type=None):

        if data_type is not None and not isinstance(data_type, DataType):
            raise Error('invalid data type provided: "{}"'.format(data_type))

        self.name = name
        self.data_type = data_type
        self.expr = None
        self.exclude = False
        self.sort_by = False
        self.filter_clause = None
Esempio n. 12
0
    def __init__(self, *models, searchable=False):

        if len(models) < 2:
            raise Error('at least two models are required')

        models_uniq = list(set(models))
        if len(models) > len(models_uniq):
            raise Error('models must be unique')

        for i, model in enumerate(models_uniq):
            if isinstance(model, type) and issubclass(model, Model):
                models_uniq[i] = model()
            elif not isinstance(model, Model):
                raise Error('invalid model: {!r}'.format(model))

        if searchable:
            for model in models_uniq:
                if model.search is None:
                    raise Error('model must be searchable: {!r}'.format(model))

        self._models = set(models_uniq)
Esempio n. 13
0
    def __init__(self, spec, value):

        try:
            group, dot_path, op = re.match(
                r'filter(\[\w+\])?\[([-_.\w]+)(:[-_\w]+)?\]', spec).groups()
        except AttributeError:
            raise Error('invalid filter parameter: {!r}'.format(spec))
        else:
            self.path = AttributePath(dot_path)
            self.operator = op.strip(':') if op else 'eq'
            self.value = value
            self.group = group
Esempio n. 14
0
 def add_custom(self, name, custom_clause):
     if is_clause(custom_clause):
         self.where.append(custom_clause)
     else:
         try:
             if len(custom_clause) == 2 and is_clause(custom_clause[0]) \
                     and all(is_from_item(fi for fi in custom_clause[1])):
                 self.where.append(custom_clause[0])
                 self.from_items.extend(custom_clause[1])
             else:
                 raise TypeError
         except TypeError:
             raise Error('filter:{} | expected a where clause and a sequence of from items'.format(name))
Esempio n. 15
0
    def get_from_items(self, related=False):

        if self.model is None and self.parent is None:
            raise Error('relationship: {!r} not loaded')

        if self.where is not None and not is_clause(self.where):
            raise ModelError('{!r} | invalid "where" clause'.format(self), self.model)

        from_items = list()

        if self.cardinality == Cardinality.ONE_TO_ONE:
            if self.refs:
                from_items.append(FromItem(
                    self.refs[0].table,
                    onclause=get_primary_key(self.refs[0].table) == self.parent.primary_key,
                    left=True))
            from_items.append(FromItem(
                self.model.from_clause(),
                onclause=self.model.primary_key == (self.refs[0] if self.refs else self.parent.primary_key),
                left=True))

        elif self.cardinality == Cardinality.MANY_TO_ONE:
            ref = self.parent.from_clause.get_column(self.refs[0])
            if ref is not None:
                from_items.append(FromItem(
                    self.parent.from_clause() if related else self.model.from_clause(),
                    onclause=self.model.primary_key == ref,
                    left=True))

            else:
                if related:
                    from_items.append(FromItem(
                        self.refs[0].table,
                        onclause=self.model.primary_key == self.refs[0],
                        left=True))
                    from_items.append(FromItem(
                        self.parent.from_clause(),
                        onclause=get_primary_key(self.refs[0].table) == self.parent.primary_key,
                        left=True))
                else:
                    from_items.append(FromItem(
                        self.refs[0].table,
                        onclause=self.parent.primary_key == get_primary_key(self.refs[0].table),
                        left=True))
                    from_items.append(FromItem(
                        self.model.from_clause(),
                        onclause=self.refs[0] == self.model.primary_key,
                        left=True))

        elif self.cardinality == Cardinality.ONE_TO_MANY:
            ref = self.model.from_clause.get_column(self.refs[0])
            if ref is not None:
                from_items.append(FromItem(
                    ref.table,
                    onclause=self.parent.primary_key == ref,
                    left=True))
                if self.model.primary_key.table != ref.table:
                    from_items.append(FromItem(
                        self.model.primary_key.table,
                        onclause=get_primary_key(ref.table) == self.model.primary_key,
                        left=True))
            else:
                from_items.append(FromItem(
                    self.refs[0].table,
                    onclause=self.model.primary_key == get_primary_key(self.refs[0]),
                    left=True))
                from_items.append(FromItem(
                    self.parent.from_clause(),
                    onclause=self.refs[0] == self.parent.primary_key,
                    left=True))

        else:
            if related:
                onclause = self.model.primary_key == self.refs[1]
                if self.where is not None:
                    onclause = and_(onclause, self.where)
                from_items.append(FromItem(self.refs[1].table, onclause=onclause, left=True))
                from_items.append(FromItem(
                    self.parent.from_clause(),
                    onclause=self.parent.primary_key == self.refs[0],
                    left=True))
            else:
                onclause = self.refs[0] == self.parent.primary_key
                if self.where is not None:
                    onclause = and_(onclause, self.where)
                from_items.append(FromItem(self.refs[0].table, onclause=onclause, left=True))
                from_items.append(FromItem(
                    self.model.from_clause(),
                    onclause=self.model.primary_key == self.refs[1],
                    left=True))

        return tuple(from_items)
Esempio n. 16
0
 def value(item):
     if not isinstance(item, (Table, Alias, FromItem)):
         raise Error('FromClause | invalid item: {!r}')
     return item if isinstance(item, FromItem) else FromItem(item)