Flask 管理员需要 HTML5 个颜色选择器字段
Flask Admin need HTML5 color picker field
我正在使用 Flask-Admin 创建我的数据库 table 的视图。我们的 table 之一有一个“颜色”列,它是“editable”,意思是如果你点击它,您可以直接编辑文本,它会用 AJAX 更新数据库。这是一个文本字段,我期待像“#3CA4FF”这样的十六进制颜色。
我希望这个“editable”颜色列有一个 HTML5 颜色选择器小部件帮助选择颜色(即 <input type="color">
)。
这是我的 Python 代码:
- SQLAlchemy table 模型 class
- 自定义 ColorField wtforms 字段
- Flask-Admin 模型视图class
以下方法可以很好地将我的自定义 ColorField 添加到“编辑”表单,但我真正想要的是使用 column_editable_list
选项对其进行编辑。
from flask_admin.contrib.sqla import ModelView
from wtforms.widgets.html5 import ColorInput
from wtforms.widgets import TextInput
from wtforms import StringField
from wtforms.fields import TextAreaField
from app import db
class UnitType(db.Model):
"""Create a SQLAlchemy model for the our manufactured units"""
__tablename__ = "unit_types"
id = db.Column(INTEGER, primary_key=True)
unit = db.Column(TEXT, unique=True, nullable=False)
color = db.Column(TEXT, nullable=True)
class ColorField(TextAreaField):
"""Create special ColorField for the color picker"""
# Set the widget for this custom field
widget = ColorInput()
class UnitModelView(ModelView):
"""The Flask-Admin view for the UnitModel model class"""
# List of columns to display in the view
column_list = (
"unit",
"color",
)
# List of columns that are immediately "editable"
# without clicking "edit" first (super-nice feature)
column_editable_list = (
"unit",
# If I uncomment the following, I get an error...
# "color",
)
# Specify my own custom color picker form field type
form_overrides = {
'color': ColorField
}
# form_args = {
# 'color': {
# 'type': "color",
# },
# }
# form_widget_args = {
# 'color': {
# 'type': "color",
# },
# }
当我将“颜色”字段添加到 column_editable_list
时,出现以下错误:
Exception: Unsupported field type: <class 'app.admin.admin.ColorField'>
我不确定接下来要尝试什么...
经过反复试验,我至少想出了如何添加自定义 select2 下拉菜单,该菜单显示在十六进制颜色值左侧选择的实际颜色。
首先创建一个自定义小部件,这样我们就不会出现以下错误:
Exception: Unsupported field type: <class 'flask_admin.model.fields.AjaxSelectField'>
这是自定义小部件:
from flask_admin.contrib.sqla.ajax import QueryAjaxModelLoader
from flask_admin.model.widgets import XEditableWidget
from wtforms.widgets import html_params
from flask_admin.helpers import get_url
from flask_admin.babel import gettext
from flask_admin._backwards import Markup
from jinja2 import escape
class CustomWidget(XEditableWidget):
"""WTForms widget that provides in-line editing for the list view"""
def get_kwargs(self, field, kwargs):
"""Return extra kwargs based on the field type"""
if field.type == "ColorField":
# A select2 list of pre-defined colors, formatted to display the actual color
kwargs["data-role"] = "x-editable-color"
kwargs["data-type"] = "select2"
else:
super().get_kwargs(field, kwargs)
return kwargs
然后在您的模型视图中覆盖 get_list_form()
方法,以使用您的 CustomWidget。
from flask_admin.contrib.sqla import ModelView
class MyModelView(ModelView):
"""
Customized model view for Flask-Admin page (for database tables)
https://flask-admin.readthedocs.io/en/latest/introduction/#
"""
# Custom templates to include custom JavaScript and override the {% block tail %}
list_template = 'admin/list_custom.html'
can_create = True
can_edit = True
def get_list_form(self):
"""Override this function and supply my own CustomWidget with AJAX
for lazy-loading dropdown options"""
if self.form_args:
# get only validators, other form_args can break FieldList wrapper
validators = dict(
(key, {'validators': value["validators"]})
for key, value in iteritems(self.form_args)
if value.get("validators")
)
else:
validators = None
# Here's where I supply my custom widget!
return self.scaffold_list_form(validators=validators, widget=CustomWidget())
这是我的 list_custom.html
模板,用于使用我自己的 flask_admin_form.js
自定义小部件脚本覆盖 {% block tail %}
。
{% extends 'admin/model/list.html' %}
{% block tail %}
{% if filter_groups %}
<div id="filter-groups-data" style="display:none;">{{ filter_groups|tojson|safe }}</div>
<div id="active-filters-data" style="display:none;">{{ active_filters|tojson|safe }}</div>
{% endif %}
<script src="{{ admin_static.url(filename='vendor/bootstrap-daterangepicker/daterangepicker.js', v='1.3.22') }}"></script>
{% if editable_columns %}
<script src="{{ admin_static.url(filename='vendor/x-editable/js/bootstrap3-editable.min.js', v='1.5.1.1') }}"></script>
{% endif %}
<!-- Custom JavaScript -->
<script src="{{ url_for('static', filename='js/flask_admin_form.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/filters.js', v='1.0.0') }}"></script>
{{ actionlib.script(_gettext('Please select at least one record.'),
actions,
actions_confirmation) }}
{% endblock %}
最后,在 flask_admin_form.js
(我对默认 filename='admin/js/form.js'
的替换)中,我为 x-editable-color
(我的自定义角色)添加了以下情况。为了简洁起见,我没有在此处包含整个 JavaScript 文件。您可以在源代码中找到它here。
注意我添加到 $el.editable(
选项的 select2
:
...
switch (name) {
case "select2-ajax":
processAjaxWidget($el, name);
return true;
case "x-editable":
$el.editable({
params: overrideXeditableParams,
combodate: {
// prevent minutes from showing in 5 minute increments
minuteStep: 1,
maxYear: 2030,
},
});
return true;
case "x-editable-color":
// Nice pastel colors
var colorsource = [
{value: "#f7f7f7", text: "#f7f7f7"},
{value: "#292b2c", text: "#292b2c"},
{value: "#87CEEB", text: "#87CEEB"},
{value: "#32CD32", text: "#32CD32"},
{value: "#BA55D3", text: "#BA55D3"},
{value: "#F08080", text: "#F08080"},
{value: "#4682B4", text: "#4682B4"},
{value: "#9ACD32", text: "#9ACD32"},
{value: "#40E0D0", text: "#40E0D0"},
{value: "#FF69B4", text: "#FF69B4"},
{value: "#F0E68C", text: "#F0E68C"},
{value: "#D2B48C", text: "#D2B48C"},
{value: "#8FBC8B", text: "#8FBC8B"},
{value: "#6495ED", text: "#6495ED"},
{value: "#DDA0DD", text: "#DDA0DD"},
{value: "#5F9EA0", text: "#5F9EA0"},
{value: "#FFDAB9", text: "#FFDAB9"},
{value: "#FFA07A", text: "#FFA07A"},
{value: "#fce38a", text: "#fce38a"},
{value: "#eaffd0", text: "#eaffd0"},
{value: "#95e1d3", text: "#95e1d3"},
]
var optsSelect2 = {
placeholder: $el.attr("data-value"),
minimumInputLength: $el.attr("data-minimum-input-length"),
allowClear: $el.attr("data-allow-blank") == "1",
multiple: $el.attr("data-multiple") == "1",
// Display the actual color next to the hex value of the color
formatResult: function (item) { return "<span style='padding-left: 20px; border-left: 20px solid " + item.text + "'>" + item.text + "</span>"; },
formatSelection: function (item){ return item.text; },
};
$el.editable({
// Source data for list of colors, as array of objects
source: colorsource,
params: overrideXeditableParams,
// select2-specific options
select2: optsSelect2,
});
return true;
...
我正在使用 Flask-Admin 创建我的数据库 table 的视图。我们的 table 之一有一个“颜色”列,它是“editable”,意思是如果你点击它,您可以直接编辑文本,它会用 AJAX 更新数据库。这是一个文本字段,我期待像“#3CA4FF”这样的十六进制颜色。
我希望这个“editable”颜色列有一个 HTML5 颜色选择器小部件帮助选择颜色(即 <input type="color">
)。
这是我的 Python 代码:
- SQLAlchemy table 模型 class
- 自定义 ColorField wtforms 字段
- Flask-Admin 模型视图class
以下方法可以很好地将我的自定义 ColorField 添加到“编辑”表单,但我真正想要的是使用 column_editable_list
选项对其进行编辑。
from flask_admin.contrib.sqla import ModelView
from wtforms.widgets.html5 import ColorInput
from wtforms.widgets import TextInput
from wtforms import StringField
from wtforms.fields import TextAreaField
from app import db
class UnitType(db.Model):
"""Create a SQLAlchemy model for the our manufactured units"""
__tablename__ = "unit_types"
id = db.Column(INTEGER, primary_key=True)
unit = db.Column(TEXT, unique=True, nullable=False)
color = db.Column(TEXT, nullable=True)
class ColorField(TextAreaField):
"""Create special ColorField for the color picker"""
# Set the widget for this custom field
widget = ColorInput()
class UnitModelView(ModelView):
"""The Flask-Admin view for the UnitModel model class"""
# List of columns to display in the view
column_list = (
"unit",
"color",
)
# List of columns that are immediately "editable"
# without clicking "edit" first (super-nice feature)
column_editable_list = (
"unit",
# If I uncomment the following, I get an error...
# "color",
)
# Specify my own custom color picker form field type
form_overrides = {
'color': ColorField
}
# form_args = {
# 'color': {
# 'type': "color",
# },
# }
# form_widget_args = {
# 'color': {
# 'type': "color",
# },
# }
当我将“颜色”字段添加到 column_editable_list
时,出现以下错误:
Exception: Unsupported field type: <class 'app.admin.admin.ColorField'>
我不确定接下来要尝试什么...
经过反复试验,我至少想出了如何添加自定义 select2 下拉菜单,该菜单显示在十六进制颜色值左侧选择的实际颜色。
首先创建一个自定义小部件,这样我们就不会出现以下错误:
Exception: Unsupported field type: <class 'flask_admin.model.fields.AjaxSelectField'>
这是自定义小部件:
from flask_admin.contrib.sqla.ajax import QueryAjaxModelLoader
from flask_admin.model.widgets import XEditableWidget
from wtforms.widgets import html_params
from flask_admin.helpers import get_url
from flask_admin.babel import gettext
from flask_admin._backwards import Markup
from jinja2 import escape
class CustomWidget(XEditableWidget):
"""WTForms widget that provides in-line editing for the list view"""
def get_kwargs(self, field, kwargs):
"""Return extra kwargs based on the field type"""
if field.type == "ColorField":
# A select2 list of pre-defined colors, formatted to display the actual color
kwargs["data-role"] = "x-editable-color"
kwargs["data-type"] = "select2"
else:
super().get_kwargs(field, kwargs)
return kwargs
然后在您的模型视图中覆盖 get_list_form()
方法,以使用您的 CustomWidget。
from flask_admin.contrib.sqla import ModelView
class MyModelView(ModelView):
"""
Customized model view for Flask-Admin page (for database tables)
https://flask-admin.readthedocs.io/en/latest/introduction/#
"""
# Custom templates to include custom JavaScript and override the {% block tail %}
list_template = 'admin/list_custom.html'
can_create = True
can_edit = True
def get_list_form(self):
"""Override this function and supply my own CustomWidget with AJAX
for lazy-loading dropdown options"""
if self.form_args:
# get only validators, other form_args can break FieldList wrapper
validators = dict(
(key, {'validators': value["validators"]})
for key, value in iteritems(self.form_args)
if value.get("validators")
)
else:
validators = None
# Here's where I supply my custom widget!
return self.scaffold_list_form(validators=validators, widget=CustomWidget())
这是我的 list_custom.html
模板,用于使用我自己的 flask_admin_form.js
自定义小部件脚本覆盖 {% block tail %}
。
{% extends 'admin/model/list.html' %}
{% block tail %}
{% if filter_groups %}
<div id="filter-groups-data" style="display:none;">{{ filter_groups|tojson|safe }}</div>
<div id="active-filters-data" style="display:none;">{{ active_filters|tojson|safe }}</div>
{% endif %}
<script src="{{ admin_static.url(filename='vendor/bootstrap-daterangepicker/daterangepicker.js', v='1.3.22') }}"></script>
{% if editable_columns %}
<script src="{{ admin_static.url(filename='vendor/x-editable/js/bootstrap3-editable.min.js', v='1.5.1.1') }}"></script>
{% endif %}
<!-- Custom JavaScript -->
<script src="{{ url_for('static', filename='js/flask_admin_form.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/filters.js', v='1.0.0') }}"></script>
{{ actionlib.script(_gettext('Please select at least one record.'),
actions,
actions_confirmation) }}
{% endblock %}
最后,在 flask_admin_form.js
(我对默认 filename='admin/js/form.js'
的替换)中,我为 x-editable-color
(我的自定义角色)添加了以下情况。为了简洁起见,我没有在此处包含整个 JavaScript 文件。您可以在源代码中找到它here。
注意我添加到 $el.editable(
选项的 select2
:
...
switch (name) {
case "select2-ajax":
processAjaxWidget($el, name);
return true;
case "x-editable":
$el.editable({
params: overrideXeditableParams,
combodate: {
// prevent minutes from showing in 5 minute increments
minuteStep: 1,
maxYear: 2030,
},
});
return true;
case "x-editable-color":
// Nice pastel colors
var colorsource = [
{value: "#f7f7f7", text: "#f7f7f7"},
{value: "#292b2c", text: "#292b2c"},
{value: "#87CEEB", text: "#87CEEB"},
{value: "#32CD32", text: "#32CD32"},
{value: "#BA55D3", text: "#BA55D3"},
{value: "#F08080", text: "#F08080"},
{value: "#4682B4", text: "#4682B4"},
{value: "#9ACD32", text: "#9ACD32"},
{value: "#40E0D0", text: "#40E0D0"},
{value: "#FF69B4", text: "#FF69B4"},
{value: "#F0E68C", text: "#F0E68C"},
{value: "#D2B48C", text: "#D2B48C"},
{value: "#8FBC8B", text: "#8FBC8B"},
{value: "#6495ED", text: "#6495ED"},
{value: "#DDA0DD", text: "#DDA0DD"},
{value: "#5F9EA0", text: "#5F9EA0"},
{value: "#FFDAB9", text: "#FFDAB9"},
{value: "#FFA07A", text: "#FFA07A"},
{value: "#fce38a", text: "#fce38a"},
{value: "#eaffd0", text: "#eaffd0"},
{value: "#95e1d3", text: "#95e1d3"},
]
var optsSelect2 = {
placeholder: $el.attr("data-value"),
minimumInputLength: $el.attr("data-minimum-input-length"),
allowClear: $el.attr("data-allow-blank") == "1",
multiple: $el.attr("data-multiple") == "1",
// Display the actual color next to the hex value of the color
formatResult: function (item) { return "<span style='padding-left: 20px; border-left: 20px solid " + item.text + "'>" + item.text + "</span>"; },
formatSelection: function (item){ return item.text; },
};
$el.editable({
// Source data for list of colors, as array of objects
source: colorsource,
params: overrideXeditableParams,
// select2-specific options
select2: optsSelect2,
});
return true;
...