4 votes

En utilisant flask-admin, comment puis-je obtenir un champ de balises prenant en charge des balises inexistantes ?

J'ai obtenu deux modèles Product et Tag, et j'ai défini une relation many to many dans SQLAlchemy. J'utilise cette méthode comme le montre l'exemple de flask-admin :

 admin.add_view(ProductAdmin(db.session))

J'obtiens un formulaire de création, et je ne peux sélectionner que les balises existantes. Je sais que le flask-admin utilise select2, et select2 obtient un style d'ajout de nouveaux tags. Et j'ai trouvé la classe Slect2TagsField dans Form.py sous le dossier flask-admin.

Alors, comment puis-je obtenir un champ de tag, qui permet d'ajouter un nouveau tag, dans mon formulaire de création de produit, en utilisant flask-admin ?

Merci d'avance.

1voto

Joes Points 1798

Malheureusement, Flask-Admin n'expose aucun point de terminaison AJAX, il n'est donc pas possible de créer de nouveaux modèles dans les champs Select2.

Cependant, il existe un moyen d'y parvenir :

  1. Exclure le champ tags du formulaire d'administration
  2. Ajouter un nouveau champ Select2TagsField au formulaire avec un nom différent (pour qu'il n'y ait pas de collision de noms)
  3. Avant d'afficher la vue new/edit, sérialiser la liste des tags sous forme de tableau de chaînes de caractères et l'intégrer au champ Select2TagsField.
  4. Avant de soumettre le modèle (on_model_change), passer en revue la liste, faire des recherches dans la base de données pour trouver les balises existantes et créer des modèles pour les balises manquantes.
  5. Contribuer à la liste des modèles de balises au modèle

1voto

ix5 Points 43

Vous pouvez créer votre propre TagField et l'ajouter à votre ModelView. Vous devrez remplacer certaines fonctions des champs.

J'ai mis au point un demande de démonstration qui utilise select2 version 4 et qui est un peu plus détaillée que cette réponse.

Le champ "tag" :

class CustomTagField(Select2TagsField):

    widget = CustomTagWidget(multiple=True)

    def pre_validate(self, form):
        pass

    def process_formdata(self, valuelist):
        if valuelist:
            self.data = []
            for tagname in valuelist:
                rv = Tag.query.filter_by(name=tagname).first()
                if rv:
                    self.data.append(rv)
                else:
                    self.data.append(Tag(name=tagname))
        else:
            self.data = []

    def iter_choices(self):

        self.blank_text = ""

        tags = list(set([str(tag.name) for tag in Tag.query.all()]))
        model_tags = [tag.name for tag in self.object_data]

        self.choices = [[tag, tag] for tag in tags]

        # Yield empty object in order to have an empty placeholder
        yield (u'__None', self.blank_text, self.data is None)

        for value, label in self.choices:
            yield (value, label, value in model_tags)

Widget personnalisé : (similaire à Select2TagsWidget mais ne fixe pas data-role donc form.js n'en fera pas un champ select2 normal)

class CustomTagWidget(widgets.Select):
    def __call__(self, field, **kwargs):
        kwargs.setdefault('data-tags', '1')
        # Or call select2 in tags mode

        allow_blank = getattr(field, 'allow_blank', False)
        if allow_blank and not self.multiple:
            kwargs['data-allow-blank'] = u'1'

        return super(CustomTagWidget, self).__call__(field, **kwargs)

Dans votre administration :

form_extra_fields = {
        'tags': CustomTagField(
            'Tags',
        ),
}

Ensuite, surchargez votre modèle d'administration pour appeler select2 en mode balise sur votre champ( admin/js/form.js tentera également de styliser le champ) :

$('#tags').select2({
  tags: true,
});

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X