Skip to content

Instantly share code, notes, and snippets.

@ThiefMaster
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ThiefMaster/31b992dae57f4a82e2fe to your computer and use it in GitHub Desktop.
Save ThiefMaster/31b992dae57f4a82e2fe to your computer and use it in GitHub Desktop.
Utility to use MongoKit i18n fields with WTForms
class _UnboundLocalizedField(UnboundField):
def __init__(self, field, locale):
super(_UnboundLocalizedField, self).__init__(field.field_class, *deepcopy(field.args), **deepcopy(field.kwargs))
self.locale = locale
# Make the field optional if it's not the default locale
if locale != current_app.config['BABEL_DEFAULT_LOCALE']:
# Validators can be set via positional or keyword argument
if len(self.args) > 1:
self.args[1][:] = [x for x in self.args[1] if not isinstance(x, DataRequired)]
elif 'validators' in self.kwargs:
self.kwargs['validators'] = [x for x in self.kwargs['validators'] if not isinstance(x, DataRequired)]
def bind(self, *args, **kwargs):
"""Makes the language available on the field object."""
bound = super(_UnboundLocalizedField, self).bind(*args, **kwargs)
bound.locale = self.locale
bound.orig_short_name = bound.short_name[:-(len(self.locale) + 1)]
return bound
class _LocalizedObjectWrapper(object):
def __init__(self, form, obj):
self.form = form
self.obj = obj
def __getattr__(self, item):
try:
# Try a normal access
return getattr(self.obj, item)
except AttributeError:
field = self.form[item]
# No special handling for non-i18n fields
if not hasattr(field, 'locale'):
raise
# If we don't have a value for a given locale we simple don't have the attribute
try:
return self.obj[field.orig_short_name][field.locale]
except KeyError:
raise AttributeError(item)
class _LocalizedFormMixin(object):
def __init__(self, *args, **kwargs):
if 'obj' in kwargs:
kwargs['obj'] = _LocalizedObjectWrapper(self, kwargs['obj'])
super(_LocalizedFormMixin, self).__init__(*args, **kwargs)
# Delete the original fields from the form; we don't want them to show
# up anywhere or worse, end up being validated.
for field_name in self.Meta.localized_fields:
delattr(self, field_name)
def populate_obj(self, obj):
"""Populates an object with field data while properly handling localized fields.
This is pretty specific to how MongoKit handles l10n, i.e. normal fields are set via
`obj.field = ...` and localized fields are accessed via `obj['field']['lang']`.
"""
for name, field in self._fields.iteritems():
if hasattr(field, 'locale'):
if field.data:
obj[field.orig_short_name][field.locale] = field.data
else:
obj[field.orig_short_name].pop(field.locale, True)
else:
field.populate_obj(obj, name)
def localize_form(form_class):
"""Converts localizable fields in the form to separate fields.
The original field is removed and replaced with multiple fields named
`fieldname_XX` with `XX` being the language code. Additionally, those
fields get a new attribute `locale` containing this language code.
"""
fields = form_class.Meta.localized_fields
if not fields:
raise ValueError('Cannot localize form without localized fields')
# Subclass the original form and add our mixin
localized_class = type('Localized{}'.format(form_class.__name__), (_LocalizedFormMixin, form_class), {})
# Add the localized field
for field_name in fields:
field = getattr(localized_class, field_name)
for lang in current_app.config['LANGUAGES']:
new_field = _UnboundLocalizedField(field, lang)
setattr(localized_class, '{}_{}'.format(field_name, lang), new_field)
return localized_class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment