Last active
August 29, 2015 14:00
-
-
Save ThiefMaster/31b992dae57f4a82e2fe to your computer and use it in GitHub Desktop.
Utility to use MongoKit i18n fields with WTForms
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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