【问题标题】:Select2 field implementation in flask/flask-admin在 flask/flask-admin 中实现 Select2 字段
【发布时间】:2014-10-31 22:00:26
【问题描述】:

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

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

这允许我加载和选择我需要的所有数据,但它不提供 select2 搜索框等。目前我找到的只是来自 flask/admin/form/fields 和 flask 的 Select2Field 和 Select2Widget /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 文件,以及如何使用我需要的数据库模型)。

【问题讨论】:

    标签: python flask wtforms flask-wtforms flask-admin


    【解决方案1】:

    这对我有用:

    ...
    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 %}
    

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

    【讨论】:

    • 我正在为此苦苦挣扎。你能做一个最小的工作示例吗?
    • @glebocog 我已经有一段时间没有使用这个了......如果我有时间我会尝试,目前不能保证。
    【解决方案2】:

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

    注意以下几点:

    TestView 类定义了三个路由;一个 get 视图、一个 post 视图和一个 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)
    

    文件templates/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>
    

    文件templates/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 扩展 - Flask-Select2 - WIP。

    【讨论】:

    • 你知道如何通过 juqery 预填充 AjaxSelectMultipleField 吗?
    【解决方案3】:

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

    https://github.com/Jaza/flasktaggingtest

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

    https://flasktaggingtest.herokuapp.com/

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

    【讨论】:

      【解决方案4】:

      其他答案似乎不再有效。我所做的是:

      在后端:

          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。

      【讨论】:

        猜你喜欢
        • 2014-03-08
        • 2017-06-23
        • 1970-01-01
        • 2017-09-15
        • 1970-01-01
        • 2017-02-18
        • 2020-08-03
        • 1970-01-01
        • 2022-01-01
        相关资源
        最近更新 更多