def test_multiple_field(self): mask = Mask('field1, field2, field3') assert_data(mask, { 'field1': True, 'field2': True, 'field3': True, })
def get_all(self, filter_by={}, order_by=None, default_mask=None): # sqlalchemy stopped supporting string arguments for sorting # this small hack makes sure our legacy code keeps working if type(order_by) == str: order_by = sa.text(order_by) # the x-fields header gives us the exact properies we want to fetch # preload relationships via a joined query if we find them in the mask preload_relationships = [] load_columns = [] if request.headers.has_key('X-FIELDS') or default_mask is not None: # get the relationships this class has relationships = inspect(self.cls).relationships relationships = [x.key for x in relationships] # parse the mask using the presupplied class from Flask-RestPlus mask = Mask(mask=request.headers.get('X-FIELDS') if request. headers.has_key('X-FIELDS') else default_mask) # add non-relationship columns to the column filter for col_name in mask: if col_name not in relationships: if hasattr(self.cls, col_name): prop = getattr(self.cls, col_name) if type(prop) == InstrumentedAttribute: load_columns.append(load_only(prop)) # add relationship columns to the preloading list for relationship in relationships: if relationship in mask: if hasattr(self.cls, relationship): preload_relationships.append( selectinload(getattr(self.cls, relationship))) if len(load_columns) == 0: query = self.cls.query.options(*preload_relationships) else: query = self.cls.query.options(*load_columns, *preload_relationships) # run or filter query once or_run_once = False for attr, value in filter_by.items(): attr_split = attr.split(" ") if len(attr_split) == 1: query = query.filter(getattr(self.cls, attr) == value) else: if attr_split[1] == 'not': query = query.filter( getattr(self.cls, attr_split[0]) != value) if attr_split[1] == '>': query = query.filter( getattr(self.cls, attr_split[0]) > value) if attr_split[1] == '<': query = query.filter( getattr(self.cls, attr_split[0]) < value) if attr_split[1] == 'or': if (or_run_once == False): query = query.filter( sa.or_(v for v in self.split_or(filter_by))) or_run_once = True return query.order_by(order_by).all()
def test_nested_fields(self): parsed = Mask('nested{field1,field2}') expected = { 'nested': { 'field1': True, 'field2': True, } } assert parsed == expected
def test_star(self): parsed = Mask('nested{field1,field2},*') expected = { 'nested': { 'field1': True, 'field2': True, }, '*': True, } assert_data(parsed, expected)
def test_order(self): parsed = Mask('f_3, nested{f_1, f_2, f_3}, f_2, f_1') expected = OrderedDict([ ('f_3', True), ('nested', OrderedDict([ ('f_1', True), ('f_2', True), ('f_3', True), ])), ('f_2', True), ('f_1', True), ]) assert parsed == expected
def test_complex(self): parsed = Mask('field1, nested{field, sub{subfield}}, field2') expected = { 'field1': True, 'nested': { 'field': True, 'sub': { 'subfield': True, } }, 'field2': True, } assert_data(parsed, expected)
def get_first(self, filter_by={}): # the x-fields header gives us the exact properies we want to fetch # preload relationships via a joined query if we find them in the mask preload_relationships = [] if request.headers.has_key('X-FIELDS'): mask = Mask(mask=request.headers.get('X-FIELDS')) # get the relationships this class has relationships = inspect(self.cls).relationships for relationship in relationships: if relationship.key in mask: preload_relationships.append( joinedload(getattr(self.cls, relationship.key))) return self.cls.query.options(*preload_relationships).filter_by( **filter_by).first()
def __init__(self, fields, metadata=None, envelope=None, skip_none=False, mask=None, ordered=False): """ :param fields: a dict of whose keys will make up the final serialized response output :param envelope: optional key that will be used to envelop the serialized response """ self.fields = fields self.metadata = metadata self.envelope = envelope self.skip_none = skip_none self.ordered = ordered self.mask = Mask(mask, skip=True)
def test_support_colons(self): assert Mask('field:name') == {'field:name': True}
def test_coma_after_bracket(self): with pytest.raises(mask.ParseError): Mask('nested{,}')
def test_coma_before_bracket(self): with pytest.raises(mask.ParseError): Mask('field,{}')
def test_consecutive_coma(self): with pytest.raises(mask.ParseError): Mask('field,,')
def test_missing_closing_bracket(self): with pytest.raises(mask.ParseError): Mask('nested{')
def test_support_dash(self): assert Mask('field-name') == {'field-name': True}
def test_unexpected_closing_bracket(self): with pytest.raises(mask.ParseError): Mask('{field}}')
def test_support_underscore(self): assert Mask('field_name') == {'field_name': True}
def test_one_field(self): assert Mask('field_name') == {'field_name': True}
def test_empty_mask(self): assert Mask('') == {}
def parse(self, value): return Mask('{' + value + '}')
def parse(self, value): return Mask(value)