def test_data_and_request_conversion(self): """ Test convert request to data and convert data to request """ schema_nested = schemaish.Structure([ ("one", schemaish.Structure([ ("a", schemaish.String()), ("b", schemaish.String()), ("c", schemaish.Structure([("x", schemaish.String()),("y", schemaish.String())])), ]) ), ]) r = {'one.a':'','one.b': '','one.c.x': '','one.c.y': ''} reqr = {'one.a':None,'one.b': None,'one.c.x': None,'one.c.y': None} reqrdata = {'one.a':[''],'one.b': [''],'one.c.x': [''],'one.c.y': ['']} data = {'one.a': '', 'one.b': '', 'one.c.x': '', 'one.c.y': ''} name = "Nested Form Two" request = Request(name, r) form = formish.Form(schema_nested, name) # request to data rdtd = validation.convert_request_data_to_data(form.structure, dottedDict(copy.deepcopy(request.POST))) assert rdtd == dottedDict(reqr) # data to request dtrd = validation.convert_data_to_request_data(form.structure, dottedDict(data)) assert dtrd == reqrdata
def convert_data_to_request_data(form_structure, data, request_data=None, errors=None): """ Take a form structure and use it's widgets to convert schema data (dict) to request data :arg form_structure: a formish form :arg data: a dictionary structure to be converted using the form :arg request_data: used to accumulate request data (internal - used while recursing) :arg errors: used to accumulate conversion failures (internal - used while recursing) """ if request_data is None: request_data = dottedDict() if errors is None: errors = dottedDict() for field in form_structure.fields: try: if field.type is 'group' or \ (field.type is 'sequence' and \ (field.widget is None or \ field.widget.converttostring is False)): convert_data_to_request_data(field, data, request_data=request_data, errors=errors) else: item_data = getNestedProperty(data, field.name) request_data[field.name] = \ field.widget.pre_render(field.attr, item_data) except Invalid, e: errors[field.name] = e raise
def test_references(self): """Does it refer to the same data if you convert an existing dottedDict or plain dict """ a = DummyObject() d = {'a.a.a':1, 'a.b.a':3, 'b':a} # Check dict single level keys don't lose reference self.assertEqual( dottedDict(d).data['b'], d['b'] ) self.assertEqual( dottedDict(d).data, dottedDict(dottedDict(d)).data )
def test_get(self): tests = [ ( {'a':1, 'b':2}, ( ('a',1),('b',2) ) ), ( {'a':{'a':1}, 'b':2}, ( ('a.a',1),('b',2) ) ), ( {'a':{'a':1, 'b':3}, 'b':2}, ( ('a.a',1),('a.b',3),('b',2) ) ), ( {'a':{'a':[1,3], 'b':2}}, ( ('a.a.0',1),('a.a.1',3) ) ), ( {'a':{'a':[1, {'a':7}], 'b':2}}, ( ('a.a.0',1),('a.a.1.a',7) ) ), ( {'list': [{'a': 1, 'b': 2}, {'a': 3, 'b': 4}]}, ( ('list.0.a',1), ) ), ] for d, checkers in tests: dd = dottedDict(d) for k, v in checkers: self.assertEquals(dd.get(k), v) error_tests = [ ( {'list': {}}, ( ('list.0.a',None), ) ), ( {'list': {}}, ( ('list.0.a', 'foo'), ) ), ] # Check None's come out for d, checkers in error_tests: dd = dottedDict(d) for k, v in checkers: self.assertEquals(dd.get(k), None) # Check the default comes out for d, checkers in error_tests: dd = dottedDict(d) for k, v in checkers: self.assertEquals(dd.get(k, v), v)
def convert_sequences(data): """ Converts numeric keyed dictionaries into sequences Converts ``{'0': 'foo', '1': 'bar'}`` into ``['foo','bar']``. leaves anything else alone """ # must be a dict if not hasattr(data,'keys'): return data # if the first key cannot be converted to an int then we don't have a # sequence try: int(data.keys()[0]) except ValueError: return dottedDict(data) # collect the keys as sorted integers intkeys = [] for key in data.keys(): intkeys.append(int(key)) intkeys.sort() # construct the sequence out = [] for key in intkeys: out.append(data[str(key)]) return out
def test_integer_type(self): schema_flat = schemaish.Structure([("a", schemaish.Integer()), ("b", schemaish.String())]) name = "Integer Form" form = formish.Form(schema_flat, name) r = {'a': '3', 'b': '4'} request = Request(name, r) R = copy.deepcopy(r) reqr = {'a': ['3'], 'b': ['4']} # check scmea matches self.assert_(form.structure.attr is schema_flat) # Does the form produce an int and a string self.assertEquals(form.validate(request), {'a': 3, 'b': '4'}) # Does the convert request to data work self.assertEqual( validation.convert_request_data_to_data(form.structure, dottedDict(request.POST)) , {'a': 3, 'b': '4'}) # Does the convert data to request work self.assert_( validation.convert_data_to_request_data(form.structure, dottedDict( {'a': 3, 'b': '4'} )) == reqr)
def _set_request_data(self, request_data): """ Assign raw request data to the form :arg request_data: raw request data (e.g. request.POST) :type request_data: Dictionary (dotted or nested or dottedDict or MultiDict) """ self._request_data = dottedDict(request_data)
def get_item_data_values(self, name): """ get all of the item data values """ data = dottedDict({}) for key, value in self.item_data: if value.has_key(name): data[key] = value[name] return data
def test_datetuple_type(self): schema_flat = schemaish.Structure([("a", schemaish.Date()), ("b", schemaish.String())]) name = "Date Form" form = formish.Form(schema_flat, name) form['a'].widget = formish.DateParts() r = {'a.day': '1','a.month': '3','a.year': '1966', 'b': '4'} R = copy.deepcopy(r) request = Request(name, r) from datetime import date d = date(1966,3,1) # Check the data is converted correctly self.assertEquals(form.validate(request), {'a': d, 'b': '4'}) # Check req to data self.assertEqual( validation.convert_request_data_to_data(form.structure, dottedDict(request.POST)) , dottedDict({'a': d, 'b': '4'})) # Check data to req self.assert_( validation.convert_data_to_request_data(form.structure, dottedDict( {'a': d, 'b': '4'} )) == dottedDict({'a': {'month': [3], 'day': [1], 'year': [1966]}, 'b': ['4']}))
def _get_request_data(self): """ Retrieve previously set request_data or return the defaults in request_data format. """ if self._request_data is not None: return self._request_data self._request_data = validation.convert_data_to_request_data(self.structure, \ dottedDict(self.defaults)) return self._request_data
def test_set(self): tests = [ ( {'list': {}}, ( ('list.0.a',7, {'list':[{'a':7}]}), ) ), ( {'list': {'x':{}}}, ( ('list.x.0.a',7, {'list':{'x':[{'a':7}]}}), ) ), ] for d, checkers in tests: dd = dottedDict(d) for k, v, result in checkers: dd[k] = v self.assertEquals(dd, result)
def build_request(formname, data): d = dottedDict(data) e = {'REQUEST_METHOD': 'POST'} request = webob.Request.blank('/',environ=e) fields = [] fields.append( ('_charset)','UTF-8') ) fields.append( ('__formish_form__','form') ) for k, v in d.dotteditems(): fields.append( (k,v) ) fields.append( ('submit','Submit') ) request.body = urlencode( fields ) return request
def validate(self, request, failure_callable=None, success_callable=None): """ Get the data without raising exceptions and then validate the data. If there are errors, raise them; otherwise return the data """ # If we pass in explicit failure and success callables then do this # first if failure_callable is not None and success_callable is not None: return self._validate_and_call(request, \ failure_callable=None, success_callable=None) self.errors = {} # Check this request was POSTed by this form. if not request.method =='POST' and \ request.POST.get('__formish_form__',None) == self.name: raise Exception("request does not match form name") request_post = UnicodeMultiDict(request.POST, \ encoding=util.get_post_charset(request)) # Remove the sequence factory data from the request for k in request_post.keys(): if '*' in k: request_post.pop(k) # We need the _request_data to be populated so sequences know how many # items they have (i.e. .fields method on a sequence uses the number of # values on the _request_data) self._request_data = dottedDict(request_post) self.request_data = validation.pre_parse_request_data( \ self.structure,dottedDict(request_post)) data = self.get_unvalidated_data( \ self.request_data, raise_exceptions=False) #self._request_data = dottedDict(request_post) try: self.structure.attr.validate(data) except schemaish.attr.Invalid, e: for key, value in e.error_dict.items(): if key not in self.errors: self.errors[key] = value
def pre_parse_request_data(form_structure, request_data, data=None): """ Some widgets (at the moment only files) need to have their data massaged in order to make sure that data->request and request->data is symmetric This pre parsing is a null operation for most widgets """ if data is None: data = {} for field in form_structure.fields: if field.type is 'group' or \ (field.type == 'sequence' and \ field.widget._template is 'SequenceDefault'): pre_parse_request_data(field, request_data, data=data) else: # This needs to be cleverer... item_data = request_data.get(field.name, []) data[field.name] = field.widget.pre_parse_request(field.attr, item_data, full_request_data=request_data) return dottedDict(data)
def __init__(self, structure, name=None, defaults=None, errors=None, action_url=None, renderer=None): """ Create a new form instance :arg structure: Schema Structure attribute to bind to the the form :type structure: schemaish.Structure :arg name: Optional form name used to identify multiple forms on the same page :type name: str "valid html id" :arg defaults: Default values for the form :type defaults: dict :arg errors: Errors to store on the form for redisplay :type errors: dict :arg action_url: Use if you don't want the form to post to itself :type action_url: string "url or path" :arg renderer: Something that returns a form serialization when called :type renderer: callable """ # allow a single schema items to be used on a form if not isinstance(structure, schemaish.Structure): structure = schemaish.Structure([structure]) self.structure = Group(None, structure, self) self.item_data = {} self.name = name if defaults is None: defaults = {} if errors is None: errors = {} self.defaults = defaults self.errors = dottedDict(errors) self.error = None self._actions = [] self.action_url = action_url if renderer is not None: self.renderer = renderer
(field.widget._template is 'SequenceDefault' \ or field.widget.converttostring is False)): if field.type == 'sequence': # Make sure we have an empty field at least. If we don't do # this and there are no items in the list then this key # wouldn't appear. data[field.name] = [] convert_request_data_to_data(field, request_data, data=data, errors=errors) else: data[field.name] = field.widget.convert(field.attr, \ request_data.get(field.name,[])) except convert.ConvertError, e: errors[field.name] = e.message data = recursive_convert_sequences(dottedDict(data)) return data def pre_parse_request_data(form_structure, request_data, data=None): """ Some widgets (at the moment only files) need to have their data massaged in order to make sure that data->request and request->data is symmetric This pre parsing is a null operation for most widgets """ if data is None: data = {} for field in form_structure.fields: if field.type is 'group' or \ (field.type == 'sequence' and \ field.widget._template is 'SequenceDefault'):
def _get_defaults(self): """ Get the raw default data """ return dottedDict(self._defaults)
def test_comparing(self): """Using the special method __eq__ to compare a dotted dict with a normal dict """ for test in self.test_dict_data: self.assertEqual(dottedDict(test[0]), test[1])
def test_convert_dictlist(self): for test in self.test_dictlist_data: self.assertEqual(dottedDict(test[0]).data, test[1])
def test_convert(self): """Just checking that converting results in assigning the right dict """ for test in self.test_dict_data: self.assertEqual(dottedDict(test[0]).data, test[1])