7

我正在尝试在我的一个烧瓶视图中实现 Select2 字段。基本上,我希望在我的烧瓶应用程序视图(不是烧瓶管理模型视图)中使用与 Flask-admin 模型创建视图中相同的 select2 字段。目前我的解决方案QuerySelectField来自看起来像这样的 wtforms

class TestForm(Form):
    name= QuerySelectField(query_factory=lambda: models.User.query.all())

这允许我加载和选择我需要的所有数据,但它不提供 select2 搜索框等。目前我发现的只是Select2Field来自Select2Widgetflask/admin/form/fields 和 flask/admin/form/widgets 的类似这篇文章https://stackoverflow.com/questions/24644960/how-to-steal-flask-admin-tag-form-field 以及http://ivaynberg.github.io/select2/上的 select2 文档 据我了解可以重复使用,这意味着不需要其他自定义小部件、自定义字段。

如果有人可以提供有关烧瓶应用程序中 select2 字段实现的更多信息(包括视图、模板、表单文件以及如何正确“连接”必要的 js 和 css 文件,以及如何使用数据库模型加载字段我将不胜感激)需要)。

4

4 回答 4

5

这对我有用:

...
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from flask_admin.form.widgets import Select2Widget
...

class TestForm(Form):
    name= QuerySelectField(query_factory=lambda: models.User.query.all(),
                           widget=Select2Widget())

在您的模板中:

{% extends "admin/master.html" %}
{% import 'admin/lib.html' as lib with context %}

{% block head %}
    {{ super() }}
    {{ lib.form_css() }}
{% endblock %}

{% block body %}
...
{% endblock %}

{% block tail %}
    {{ super() }}
    {{ lib.form_js() }}
{% endblock %}

如有必要,我可以尝试整理一个最小的工作示例。

于 2015-07-19T02:10:27.180 回答
4

我有类似的要求,并整理了一个最小的例子。

请注意以下事项:

TestView定义了三个路由;获取视图、发布视图和 Ajax 查找视图。

函数get_loader_by_name接受一个字符串名称并返回一个QueryAjaxModelLoader. 此函数用于 Ajax 查找调用和TestForm字段定义。

Select2 小部件中显示的文本是 User 模型的__unicode__方法返回的值。

我使用Faker生成示例用户数据。

文件app.py

from flask import Flask, render_template, url_for, request, abort, json, Response
from flask.ext.admin import Admin, BaseView, expose, babel
from flask.ext.admin.contrib.sqla.ajax import QueryAjaxModelLoader
from flask.ext.admin.model.fields import AjaxSelectField, AjaxSelectMultipleField
from flask.ext.sqlalchemy import SQLAlchemy
from wtforms import Form
from faker import Factory

app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)

try:
    from flask_debugtoolbar import DebugToolbarExtension
    DebugToolbarExtension(app)
except:
    pass


class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)

    first_name = db.Column(db.Unicode(length=255), nullable=False)
    last_name = db.Column(db.Unicode(length=255), nullable=False)
    email = db.Column(db.Unicode(length=254), nullable=False, unique=True)

    def __unicode__(self):
        return u"{first} {last}; {email}".format(first=self.first_name, last=self.last_name, email=self.email)


def get_loader_by_name(name):
    _dicts = {
        'user': QueryAjaxModelLoader(
            'user',
            db.session, User,
            fields=['first_name', 'last_name', 'email'],
            page_size=10,
            placeholder="Select a user"
        )
    }
    return _dicts.get(name, None)


class TestView(BaseView):

    def __init__(self, name=None, category=None,
                 endpoint=None, url=None,
                 template='admin/index.html',
                 menu_class_name=None,
                 menu_icon_type=None,
                 menu_icon_value=None):
        super(TestView, self).__init__(name or babel.lazy_gettext('Home'),
                                             category,
                                             endpoint or 'admin',
                                             url or '/admin',
                                             'static',
                                             menu_class_name=menu_class_name,
                                             menu_icon_type=menu_icon_type,
                                             menu_icon_value=menu_icon_value)
        self._template = template

    @expose('/', methods=('GET',))
    def index_view(self):
        _form = TestForm()
        return self.render(self._template, form=_form)

    @expose('/', methods=('POST',))
    def post_view(self):
        pass

    @expose('/ajax/lookup/')
    def ajax_lookup(self):
        name = request.args.get('name')
        query = request.args.get('query')
        offset = request.args.get('offset', type=int)
        limit = request.args.get('limit', 10, type=int)

        loader = get_loader_by_name(name)

        if not loader:
            abort(404)

        data = [loader.format(m) for m in loader.get_list(query, offset, limit)]
        return Response(json.dumps(data), mimetype='application/json')

# Create admin and Test View
admin = Admin(app, name='Admin', template_mode='bootstrap3')
admin.add_view(TestView(template='test.html', name="Test", url='/test', endpoint='test'))


class TestForm(Form):

    single_user = AjaxSelectField(
        loader=get_loader_by_name('user')
    )

    multiple_users = AjaxSelectMultipleField(
        loader=get_loader_by_name('user')
    )


@app.route('/')
def index():
    return render_template("index.html", link=url_for('test.index_view'))


def build_db():

    db.drop_all()
    db.create_all()
    fake = Factory.create()
    for index in range(0, 1000):
        _first_name = fake.first_name()
        _last_name = fake.last_name()
        _user_db = User(
            first_name=_first_name,
            last_name=_last_name,
            email="{first}.{last}{index}@example.com".format(first=_first_name.lower(), last=_last_name.lower(), index=index)
        )
        db.session.add(_user_db)

    db.session.commit()


@app.before_first_request
def before_first_request():
    build_db()


if __name__ == '__main__':
    app.debug = True
    app.run(port=5000, debug=True)

文件模板/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test Select2</title>
</head>
<body>
    <a href="{{ link }}">Go to the test form</a>
</body>
</html>

文件模板/test.html

{% import 'admin/static.html' as admin_static with context %}
{% import 'admin/lib.html' as lib with context %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test Select2</title>
    <link href="{{ admin_static.url(filename='bootstrap/bootstrap3/css/bootstrap.min.css') }}" rel="stylesheet">
    <link href="{{ admin_static.url(filename='bootstrap/bootstrap3/css/bootstrap-theme.min.css') }}" rel="stylesheet">
    <link href="{{ admin_static.url(filename='admin/css/bootstrap3/admin.css') }}" rel="stylesheet">
    {{ lib.form_css() }}
</head>
<body>

    <div class="container">
        <div class="row">
            <div class="col-sm-10 col-sm-offset-2">
                <form>
                    {{ lib.render_form_fields(form) }}
                </form>
            </div>
        </div>
    </div>

    <script src="{{ admin_static.url(filename='vendor/jquery-2.1.1.min.js') }}" type="text/javascript"></script>
    <script src="{{ admin_static.url(filename='bootstrap/bootstrap3/js/bootstrap.min.js') }}" type="text/javascript"></script>
    <script src="{{ admin_static.url(filename='vendor/moment-2.8.4.min.js') }}" type="text/javascript"></script>
    <script src="{{ admin_static.url(filename='vendor/select2/select2.min.js') }}" type="text/javascript"></script>
    {{ lib.form_js() }}

</body>
</html>

2018 年 7 月更新

在 Github - Flask-Select2 - WIP上添加了一个独立的 Flask 扩展。

于 2016-02-19T20:54:56.197 回答
1

我最近使用 Select2 和 WTForms 在 Flask 应用程序的前端实现了一个“标签”字段。我编写了一个示例应用程序,演示了它是如何工作的(用于填充选择选项和动态保存新选项的视图代码是大部分工作发生的地方):

https://github.com/Jaza/flasktaggingtest

您可以在以下位置查看此应用程序的演示:

https://flasktaggingtest.herokuapp.com/

我的示例中没有 AJAX 自动完成功能(它只是在初始化表单时使用所有可用标签填充选择字段)。除此之外,应该是您通常想要的 Flask 视图/模板中的标记小部件的所有内容。

于 2015-09-15T05:12:41.850 回答
0

其他答案似乎不再起作用。我为使其工作所做的工作是:

在后端:

    from wtforms.ext.sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField

    ...
    users =  users = QuerySelectMultipleField('users', query_factory=lambda: User.query.order_by(User.fullname).all(),
                                 get_label=lambda u: u.fullname, get_pk=lambda u: u.id)

然后在前端:

    $("#users").attr("multiple", "multiple").select2();

select2需要手动添加css和js。

于 2019-10-16T07:19:33.580 回答