2017-12-01 1 views
3

Gibt es eine Möglichkeit, ein Popup-Fenster von einer Flask-Funktion zu initiieren?Flask-Admin-Batch-Aktion mit Argument über Popup-modales Fenster

Ich habe eine flask-admin + flask-sqlalchemy App. Eine Tabelle in der DB enthält ein Feld foo mit einigen Werten. Ich habe eine UserAdmin Ansicht und ich versuche, eine batch action mit einigen externen Argument zu erstellen. Dh ich will:

  • mehrere Elemente aus meiner DB und
  • Ersatz die alten foo Werte für jeden das Element mit dem einem neuen benutzerdefinierten Wert auswählen und
  • , wie ich will Um diesen neuen Wert zu erhalten, ist ein modales Fenster.

So ist das Modell:

class User(db.Model): 
    # some code 
    foo = Column(Integer) 
    def change_foo(self, new_foo): 
     self.foo = int(new_foo) 
     db.session.commit() 
     return True 

class UserAdmin(sqla.ModelView): 
    # some code 
    @action('change_foo', 'Change foo', 'Are you sure you want to change foo for selected users?') 
    def action_change_foo(self, ids): 
     query = tools.get_query_for_ids(self.get_query(), self.model, ids) 
     count = 0 
     # TODO: pop-up modal window here to get new_foo_value 
     # new_foo_value = ??? 
     for user in query.all(): 
      if user.change_foo(new_foo_value): 
       count += 1 
     flash(ngettext('Foo was successfully changed.', 
         '%(count)s foos were successfully changed.', 
         count, count=count)) 
    except Exception as e: 
     flash(ngettext('Failed to change foo for selected users. %(error)s', error=str(e)), 'error') 

Ich gebe zu, dass der gesamte Ansatz nicht optimal ist, so würde ich mich freuen, mit dem man besser beraten werden.

Es gibt einige verwandte Fragen: «Batch Editing in Flask-Admin» (noch unbeantwortet) und «Flask-Admin batch action with form» (mit einigen Workaround mit WTF-Formulare).

+0

@pjcunningham Vielen Dank für Sie wirklich schöne Lösung. Ich habe es für meine Anwendung angepasst und es funktioniert super! Das einzige, was ich geändert habe, ist eine 'flash()' Nachricht anstelle von 'print change_form.errors', und ich habe' code = 307' danach entfernt. (Trotzdem sieht es immer noch wie eine Magie für mich aus.) –

Antwort

4

Hier ist eine Möglichkeit, dies zu erreichen. Ich habe das komplette in sich abgeschlossene Beispiel auf Github, flask-admin-modal gestellt.

In diesem Beispiel ist das SQLite-Datenbankmodell ein Projekt mit den Attributen name (string) und cost (Integer), und wir aktualisieren den Kostenwert für ausgewählte Zeilen in der Flask-Admin-Listenansicht. Beachten Sie, dass die Datenbank mit zufälligen Daten gefüllt wird, wenn die Flask-Anwendung gestartet wird.

Hier ist das Modell:

class Project(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(255), nullable=False, unique=True) 
    cost = db.Column(db.Integer(), nullable=False) 

    def __str__(self): 
     return unicode(self).encode('utf-8') 

    def __unicode__(self): 
     return "Name: {name}; Cost : {cost}".format(name=self.name, cost=self.cost) 

definieren ein Formular mit einem ganzzahligen Kostenfeld, das die neuen Kosten übernimmt. Dieses Formular enthält auch ein verstecktes Feld, um die ausgewählten Zeilen-IDs zu verfolgen.

Überschreiben Sie die Listenvorlage für das Projektansichtsmodell. Wir tun dies, damit wir ein Bootstrap-Modal-Formular mit einer ID changeModel innerhalb der {% block body %} einfügen können, so dass wir zuerst {{ super() }} aufrufen.

Wir fügen auch eine jQuery Dokument bereit Funktion, die das modale Formular zeigt, wenn eine Vorlage Variable (change_model) als wahr ausgewertet wird. Dieselbe Variable wird im Modellkörper verwendet, um die change_form anzuzeigen. Wir verwenden die flask-Admin lib Makros render_form, um das Formular in einem Bootstrap-Stil zu rendern.

Beachten Sie den Wert des Aktionsparameters in render_form - es ist eine Route, die wir in unserer Projektansicht definieren, wo wir die Daten des Formulars verarbeiten können. Beachten Sie auch, dass die Schaltfläche "Schließen" durch einen Link ersetzt wurde, aber immer noch als Schaltfläche formatiert ist. Der Link ist die ursprüngliche URL (einschließlich Seiten- und Filterdetails), von der aus die Aktion initiiert wurde.

Die Projektsichtklasse muss das Verhalten der Batch-Aktionsmethode ändern und einige Routen definieren, die POST-Anforderungen akzeptieren.

@action('change_cost', 'Change Cost', 'Are you sure you want to change Cost for selected projects?') 
def action_change_cost(self, ids): 
    url = get_redirect_target() or self.get_url('.index_view') 
    return redirect(url, code=307) 

Statt Verarbeitung der ids direkt die Batch-Aktion die URL erhält, der die Aktion veröffentlicht, wird diese URL enthalten keine Seitenzahl und Filterdetails. Es führt dann eine Umleitung zurück zu der Listenansicht mit einer 307 durch. Dies stellt sicher, dass die ausgewählten Zeilen-IDs in dem Hauptteil mitgerissen werden, ebenso wie die Tatsache, dass es eine POST-Anfrage war.

Definieren Sie eine POST-Route, um diese Weiterleitung zu verarbeiten, erhalten Sie die IDs und URL aus dem Anfragetext, Instanz a ChangeForm, legen Sie das ausgeblendete Formularfeld ids auf eine codierte Liste der IDs. Fügen Sie den Vorlagenargumenten die Variablen url, change_form und change_model hinzu, und rendern Sie dann die Listenansicht erneut - dieses Mal wird das modale Popup-Formular in der Ansicht angezeigt.

@expose('/', methods=['POST']) 
def index(self): 
    if request.method == 'POST': 
     url = get_redirect_target() or self.get_url('.index_view') 
     ids = request.form.getlist('rowid') 
     joined_ids = ','.join(ids) 
     encoded_ids = base64.b64encode(joined_ids) 
     change_form = ChangeForm() 
     change_form.ids.data = encoded_ids 
     self._template_args['url'] = url 
     self._template_args['change_form'] = change_form 
     self._template_args['change_modal'] = True 
     return self.index_view() 

eine POST-Route definieren die modale Form des Daten zu verarbeiten. Dies ist die Standardform/Datenbankverarbeitung, und wenn Sie fertig sind, leiten Sie zurück zur ursprünglichen URL, die die Aktion initiiert hat.

@expose('/update/', methods=['POST']) 
def update_view(self): 
    if request.method == 'POST': 
     url = get_redirect_target() or self.get_url('.index_view') 
     change_form = ChangeForm(request.form) 
     if change_form.validate(): 
      decoded_ids = base64.b64decode(change_form.ids.data) 
      ids = decoded_ids.split(',') 
      cost = change_form.cost.data 
      _update_mappings = [{'id': rowid, 'cost': cost} for rowid in ids] 
      db.session.bulk_update_mappings(Project, _update_mappings) 
      db.session.commit() 
      return redirect(url) 
     else: 
      # Form didn't validate 
      # todo need to display the error message in the pop-up 
      print change_form.errors 
      return redirect(url, code=307) 
+0

Danke für die Lösung. Das verdient mehr Upvotes! – ken

Verwandte Themen