Skip to content

Instantly share code, notes, and snippets.

@akprasad
Last active June 12, 2018 23:30
Show Gist options
  • Save akprasad/5993519 to your computer and use it in GitHub Desktop.
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/).
# -*- 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
@Busata
Copy link

Busata commented Dec 19, 2014

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!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment