I had a similar requirement and I put together a minimal example.
Please note the following:
The TestView class defines three routes; view get, view post and view Ajax.
The get_loader_by_name function takes a string name and returns a QueryAjaxModelLoader . This function is used both in an Ajax search call and in TestForm field TestForm .
The text displayed in Select2 widgets is the value returned by the __unicode__ custom model __unicode__ .
I used Faker to create a sample user data.
App.py file:
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')
File 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>
File 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>
July 2018 Update
Added stand-alone Flask extension - Flask-Select2 - WIP on Github.