I created a custom Ajax selector in a Flask-Admin view to optimize search in the birth_city_id
field (over 36000 items) using form_ajax_refs
(based on How to do an Ajax form field on Flask Admin implementation). While the Ajax selector works correctly, I have an issue with how fields are displayed in the form.
The birth_city_id
field only appears if I add it explicitly in the scaffold_form
method (I tried with form_overrides
attribute too, but it's fail). However, this results in the field being placed at the end of the form, whereas it should appear before personal_information
and professional_information
fields.
Here’s my code:
from flask_admin.model.fields import AjaxSelectField
from flask_admin.model.ajax import AjaxModelLoader, DEFAULT_PAGE_SIZE
from ..models import *
class CityAjaxModelLoader(AjaxModelLoader):
def __init__(self, name, **options):
super(CityAjaxModelLoader, self).__init__(name, options)
def format(self, model):
if model is None:
return None
return (model.id, repr(model))
def get_one(self, pk):
return session.query(City).get(pk)
def get_list(self, query, offset=0, limit=DEFAULT_PAGE_SIZE):
search_term = query.strip().lower()
return session.query(City).filter(City.label.ilike(f"%{search_term}%")).offset(offset).limit(limit).all()
class PrinterView(GlobalModelView):
# ...
form_columns = [
"lastname",
"firstnames",
"birth_date",
"birth_city_label",
"birth_city_id",
"personal_information",
"professional_information",
]
form_ajax_refs = {
'birth_city_id': CityAjaxModelLoader('birth_city_id')
}
def scaffold_form(self):
form_class = super(PrinterView, self).scaffold_form()
form_class.birth_city_id = AjaxSelectField(
CityAjaxModelLoader('birth_city_id'),
label='Birth City (reference)',
blank_text="Select a city..."
)
# reorder fields ?
return form_class
The birth_city_id
field is always placed at the end of the form, even if I explicitly include it in the form_columns
list (I expected to force the fields position like this but it's change nothing).
I would like to know:
Is there a way to reorder fields through scaffold_form
method?
Is there a better strategy to integrate an Ajax selector while maintaining the field order in the form?
Thanks in advance for your help!
[Update 1]
My Person model (linked to PrinterView
) does have a relationship birth_cities
linked to the birth_city_id
foreign key. Here is the relevant part of the model definition:
class Person(AbstractVersion):
__tablename__ = "persons"
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False, unique=True)
birth_city_id = Column(Integer, ForeignKey("cities.id"), nullable=True, unique=False)
# Relations
birth_cities = relationship("City", foreign_keys=[birth_city_id], back_populates="persons")
class City(AbstractVersion):
__tablename__ = "cities"
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False, unique=True)
# Relations
persons = relationship("Person", back_populates="birth_cities")
Use the relation in the form_columns
definition and not the foreign key. Also, you can use the Flask-Admin supplied QueryAjaxModelLoader
to do your Ajax lookups. Something like the following (untested):
from flask_admin.contrib.sqla.ajax import QueryAjaxModelLoader
class PrinterView(GlobalModelView):
# ...
form_columns = (
"lastname",
"firstnames",
"birth_date",
"birth_cities",
"personal_information",
"professional_information",
)
form_ajax_refs = {
'birth_cities': QueryAjaxModelLoader(
'birth_cities',
db.session,
City,
fields=['label'],
),
}