Last active
June 12, 2018 23:30
-
-
Save akprasad/5993519 to your computer and use it in GitHub Desktop.
Adds flask-admin support for the SQLAlchemy "Enum Recipe" on zzzeek's blog (http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/).
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
# -*- coding: utf-8 -*- | |
""" | |
flask-admin and "The Enum Recipe" | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
Adds flask-admin support for the enum recipe on zzzeek's blog_. | |
This code is specific to SQLAlchemy. | |
.. _blog http://techspot.zzzeek.org/2011/01/14/the-enum-recipe/ | |
:license: MIT | |
""" | |
from flask.ext.admin.model.form import converts | |
from flask.ext.admin.contrib.sqlamodel import ModelView | |
from flask.ext.admin.contrib.sqlamodel.form import AdminModelConverter | |
from wtforms import fields | |
from wtforms.validators import ValidationError | |
class EnumSelectField(fields.SelectField): | |
"""A special type of `SelectField`. | |
A regular `SelectField` is a dropdown box with a list of labels, | |
each of which corresponds to a value. This is basically the same | |
thing, but it handles data very differently. | |
""" | |
def __init__(self, model, **kw): | |
super(EnumSelectField, self).__init__(**kw) | |
#: A reference to the enum class | |
self.model = model | |
#: By default a field will coerce objects to unicode. This line | |
#: prevents that behavior. This seems pretty hacky to me, but | |
#: it works without any major bugs and I'm not inspired to look | |
#: into it further. | |
self.coerce = lambda x: x | |
"""Treat `data` as a property. | |
Usually `data` is just a value. By using a property instead, we | |
get some nice behavior: we can assign `self.data = some_string` | |
and still have `some_decl_enum = self.data`. `self._data` stores | |
the actual string value. | |
""" | |
@property | |
def data(self): | |
return self.model.from_string(self._data) | |
@data.setter | |
def data(self, data): | |
self._data = data.value | |
def iter_choices(self): | |
"""A list of choices to display. | |
These are 3-tuples (value, label, selected), where ``selected`` | |
is either ``True`` or ``False``. | |
""" | |
for key, description in self.model: | |
yield (key, description, key == self._data) | |
def process_formdata(self, valuelist): | |
"""Process the incoming data. | |
We have to override this method to set to `self._data` instead | |
of `self.data`. | |
""" | |
if valuelist: | |
self._data = valuelist[0] | |
def pre_validate(self, form): | |
"""Validates the form field. | |
We have to ensure that the current data is valid. To do so, we | |
can just iterate through the enum values and look for a match. | |
""" | |
if self.data is not None: | |
for key, description in self.model: | |
if self._data == key: | |
break | |
else: | |
raise ValidationError(self.gettext(u'Not a valid choice')) | |
class MyAdminModelConverter(AdminModelConverter): | |
"""An `AdminModelConverter` converts columns into form fields. | |
Most of the data logic is in the fields themselves. Here we just | |
add a converter for `DeclEnumType`. | |
""" | |
@converts('DeclEnumType') | |
def conv_DeclEnumType(self, column, field_args, **extra): | |
return EnumSelectField(model=column.type.enum, **field_args) | |
class MyModelView(ModelView): | |
"""Use this instead of `ModelView`.""" | |
model_form_converter = MyAdminModelConverter |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is giving me an error, the @DaTa setter gets a None value passed in, and fails on "data.value".
(If I check on None value first before setting it, it seems to work though, thanks!)