If I have some (string) values from a GET or POST request with the associated Property
instances, one IntegerProperty
and one TextProperty
, say, is there a way to convert the values to the proper (user) types without a long tedious chain of isinstance
calls?
I'm looking to reproduce this sort of functionality (all input validation omitted for clarity):
for key, value in self.request.POST.iteritems():
prop = MyModel._properties[key]
if isinstance(prop, ndb.IntegerProperty):
value = int(value)
elif isinstance(prop, (ndb.TextProperty, ndb.StringProperty)):
pass # it's already the right type
elif ...
else
raise RuntimeError("I don't know how to deal with this property: {}"
.format(prop))
setattr(mymodelinstance, key, value)
For example, if there is a way to get the int
class from an IntegerProperty
and the bool
class from a BooleanProperty
etc., that would do the job.
The ndb metadata API doesn't really solve this elegantly, as far as I can see; with get_representations_of_kind
I can reduce the number of cases, though.
You can use a dict
to map between user-defined types to built-in types by using type
of the object as a key and a built-in type as value.
F.e.
class IntegerProperty(int):
pass
class StringProperty(str):
pass
a, b = IntegerProperty('1'), StringProperty('string')
def to_primitive(obj):
switch = {IntegerProperty: int, StringProperty: str}
return switch[type(obj)](obj)
for x in (a, b):
print(to_primitive(x))
Because here the key is type of the object instead of isinstance
check, if more than one user-defined types map to a single built-in type KeyError
will arise if the type is not in the dict
. So you have to explicitly add every user-defined type to the switch dict
.
F.e.
class TextProperty(StringProperty):
pass
switch = {IntegerProperty: int, StringProperty: str, TextProperty: str}
Above we have added the new TextProperty
to the switch
even though TextProperty
is subclass of StringProperty
. If you don't want to do that we have to get the key from isinstance
check.
Here's how to do it;
class IntegerProperty(int):
pass
class StringProperty(str):
pass
class TextProperty(StringProperty):
pass
a, b, c = IntegerProperty('1'), StringProperty('string'), TextProperty('text')
def to_primitive(obj):
switch = {IntegerProperty: int, StringProperty: str}
key = filter(lambda cls: isinstance(obj, cls), switch.keys())
if not key:
raise TypeError('Unknown type: {}'.format(repr(obj)))
key = key[0]
return switch[key](obj)
for x in (a, b, c):
print(to_primitive(x))
There is a Python package called WTForms that is enormously helpful for this, and generally makes form processing a much more pleasant experience.
Here is a really simple example:
class MyForm(wt.Form):
text = MyTextAreaField("Enter text")
n = wt.IntegerField("A number")
f = MyForm(self.request.POST)
f.validate()
print f.text.data
print f.n.data
Calling f.validate()
will automatically convert the POST data to the data type specified by the form. So f.text.data
will be a string and f.n.data
will be an int.
It also gracefully handles invalid data. If a user inputs a letter for the integer field, then f.n.data
will be None
. You can also specify error messages that are easily incorporated into your web page.
WTForms takes a huge amount of drudgery out of form processing!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With