flaskflask-admin

Custom Flask-Admin form with some select field choices set according to another select field


I am trying to use Flask-Admin to create a create/edit form for the model Matriline shown below. This model has a string field name and a field pod_id with a foreign key constraint to another model Pod, which itself has a foreign key field to a Clan model.

The default form created by Flask-Admin shows the name field and a select field for the Pod instances, but I would like to add a field Clan, which would reset the Pod list according to the Clan instance selected.

To add the Clan field I override the default ModelView for Matriline and add an extra select field Clan with all the Clan instances, as shown in the view MatrilineView below.

Now I need to add some Ajax code to the rendered form to reset the list of pods every time a new clan is selected.

Do I have to replace entirely the default form by a custom one including the Ajax code? Or is there any easier way to that with Flask-Admin?

<b>models.py</b>

...

class Matriline(db.Model):
    __tablename__ = 'matriline'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(64))
    pod_id = db.Column(db.Integer, db.ForeignKey('pod.id'))

    def __unicode__(self):
        return self.name


class Pod(db.Model):
    __tablename__ = 'pod'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(64))
    matrilines = db.relationship('Matriline', backref='pod', lazy='select')
    clan_id = db.Column(db.Integer, db.ForeignKey('clan.id'))

    def __unicode__(self):
        return self.name


class Clan(db.Model):
    __tablename__ = 'clan'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(64))
    pods = db.relationship('Pod', backref='clan', lazy='select')

    def __unicode__(self):
        return self.name

...

<b>views.py</b>

from flask_admin.contrib import sqla
from wtforms import SelectField
from orcall import models


class MatrilineView(sqla.ModelView):
    column_hide_backrefs = False
    form_extra_fields = {
        'clan': SelectField('Clan',
            choices=[ (c.id, c.name) for c in models.Clan.query.all()])
    }
    column_list = ('name', 'pod', 'clan')

...

Solution

  • You are need to use a QuerySelectField. For example:

    from flask.ext.admin.form import Select2Widget
    
    class MatrilineView(sqla.ModelView):
        column_hide_backrefs = False
        form_extra_fields = {
            'clan': sqla.fields.QuerySelectField(
                label='Clan',
                query_factory=lambda: Clan.query.all(),
                widget=Select2Widget()
            )
        }
        column_list = ('name', 'pod', 'clan')