Come utilizzare le relazioni database molti a molti con Flask-SQLAlchemy

L’autore ha selezionato il Fondo per il Software Libero e Open Source per ricevere una donazione come parte del programma Scrivi per le Donazioni.

Introduzione

Flask è un framework web leggero per Python che fornisce strumenti e funzionalità utili per creare applicazioni web nel linguaggio Python. SQLAlchemy è un toolkit SQL che fornisce un accesso efficiente e ad alte prestazioni ai database relazionali. Fornisce modi per interagire con diversi motori di database come SQLite, MySQL e PostgreSQL. Ti dà accesso alle funzionalità SQL del database. Ti fornisce anche un Object Relational Mapper (ORM), che ti consente di eseguire query e gestire i dati utilizzando oggetti e metodi Python. Flask-SQLAlchemy è un’estensione di Flask che semplifica l’uso di SQLAlchemy con Flask, fornendo strumenti e metodi per interagire con il database nelle tue applicazioni Flask tramite SQLAlchemy.

A many-to-many database relationship is a relationship between two database tables where a record in each table can reference several records in the other table. For example, in a blog, a table for posts can have a many-to-many relationship with a table for storing authors. Each post can have many authors, and each author can write many posts. Therefore, there is a many-to-many relationship between posts and authors. For another example, in a social media application, each post may have many hashtags, and each hashtag may have many posts.

In questo tutorial, modificherai un’applicazione costruita utilizzando Flask e Flask-SQLAlchemy aggiungendo una relazione molti-a-molti. Avrai una relazione tra post e tag, dove ogni post del blog può avere diversi tag e ogni tag può avere più post taggati con esso.

Sebbene tu possa seguire questo tutorial in modo indipendente, è anche un continuo del tutorial Come utilizzare le relazioni one-to-many del database con Flask-SQLAlchemy, nel quale costruisci un database multi-tabella con una relazione one-to-many tra i post e i commenti in un’applicazione di blogging.

Alla fine del tutorial, la tua applicazione avrà una nuova funzionalità per aggiungere tag ai post. I post possono essere contrassegnati con più tag, e ogni pagina di tag mostrerà tutti i post contrassegnati con esso.

Prerequisiti

Passo 1 — Configurazione dell’Applicazione Web

In questo passo, configurerai l’applicazione di blogging per essere pronta per le modifiche. Inoltre, esaminerai i modelli di database Flask-SQLAlchemy e le route di Flask per comprendere la struttura dell’applicazione. Se hai seguito il tutorial nella sezione dei prerequisiti e hai ancora il codice e l’ambiente virtuale sulla tua macchina locale, puoi saltare questo passaggio.

Per dimostrare l’aggiunta di una relazione molti a molti a un’applicazione web Flask con Flask-SQLAlchemy, utilizzerai il codice dell’applicazione del tutorial precedente, che è un sistema di blogging con la capacità di aggiungere e visualizzare post, commentare i post e leggere ed eliminare commenti esistenti.

Clona il repository e rinominalo da flask-slqa-bloggy a flask_app con il seguente comando:

  1. git clone https://github.com/do-community/flask-slqa-bloggy flask_app

Naviga fino a flask_app:

  1. cd flask_app

Quindi crea un nuovo ambiente virtuale:

  1. python -m venv env

Attiva l’ambiente:

  1. source env/bin/activate

Installa Flask e Flask-SQLAlchemy:

  1. pip install Flask Flask-SQLAlchemy

Successivamente, imposta le seguenti variabili d’ambiente:

  1. export FLASK_APP=app
  2. export FLASK_ENV=development

FLASK_APP indica l’applicazione che stai attualmente sviluppando, che è app.py in questo caso. FLASK_ENV specifica la modalità. Impostalo su development per la modalità di sviluppo; ciò ti permetterà di eseguire il debug dell’applicazione. Ricorda di non utilizzare questa modalità in un ambiente di produzione.

Successivamente, apri la shell di Flask per creare le tabelle del database:

  1. flask shell

Quindi importa l’oggetto database db di Flask-SQLAlchemy, il modello Post e il modello Comment, e crea le tabelle del database utilizzando la funzione db.create_all():

  1. from app import db, Post, Comment
  2. db.create_all()
  3. exit()

Successivamente, popola il database utilizzando il programma init_db.py:

  1. python init_db.py

Questo aggiunge tre post e quattro commenti al database.

Avvia il server di sviluppo:

  1. flask run

Se vai sul tuo browser, avrai l’applicazione in esecuzione all’URL seguente:

http://127.0.0.1:5000/

Vedrai una pagina simile alla seguente:

Se ricevi un errore, assicurati di aver seguito correttamente i passaggi sopra.

Per interrompere il server di sviluppo, usa CTRL + C.

Successivamente, passerai attraverso i modelli di database Flask-SQLAlchemy per comprendere le relazioni attuali tra le tabelle. Se sei familiare con i contenuti del file app.py, puoi saltare al passaggio successivo.

Apri il file app.py:

  1. nano app.py

I contenuti del file sono i seguenti:

flask_app/app.py
import os
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy

basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
           'sqlite:///' + os.path.join(basedir, 'database.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False


db = SQLAlchemy(app)


class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100))
    content = db.Column(db.Text)
    comments = db.relationship('Comment', backref='post')

    def __repr__(self):
        return f'<Post "{self.title}">'


class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'))

    def __repr__(self):
        return f'<Comment "{self.content[:20]}...">'


@app.route('/')
def index():
    posts = Post.query.all()
    return render_template('index.html', posts=posts)


@app.route('/<int:post_id>/', methods=('GET', 'POST'))
def post(post_id):
    post = Post.query.get_or_404(post_id)
    if request.method == 'POST':
        comment = Comment(content=request.form['content'], post=post)
        db.session.add(comment)
        db.session.commit()
        return redirect(url_for('post', post_id=post.id))

    return render_template('post.html', post=post)


@app.route('/comments/')
def comments():
    comments = Comment.query.order_by(Comment.id.desc()).all()
    return render_template('comments.html', comments=comments)


@app.post('/comments/<int:comment_id>/delete')
def delete_comment(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    post_id = comment.post.id
    db.session.delete(comment)
    db.session.commit()
    return redirect(url_for('post', post_id=post_id))

Qui, hai due modelli di database che rappresentano due tabelle:

  • Post: che ha una colonna ID, un titolo, contenuto e una relazione One-to-Many con la tabella dei commenti.

  • Comment: che ha una colonna ID, una colonna per il contenuto e una colonna post_id per fare riferimento al post a cui il commento appartiene.

Sotto i modelli hai le seguenti route:

  • /: La pagina di indice, che mostra tutti i post nel database.
  • /<int:post_id>/: La pagina del singolo post. Ad esempio, il link http://127.0.0.1:5000/2/ mostra i dettagli del secondo post nel database e i suoi commenti.
  • /comments/: Una pagina che visualizza tutti i commenti nel database e collega ogni commento al post su cui è stato pubblicato.
  • /comments/<int:comment_id>/delete: Un percorso che elimina un commento tramite un pulsante Elimina Commento.

Chiudi il file app.py.

Nel prossimo passaggio, utilizzerai una relazione many-to-many per creare un collegamento tra due tabelle.

Passaggio 2 — Configurazione dei Modelli di Database per una Relazione Many-to-Many

In questo passaggio, aggiungerai un modello di database che rappresenterà la tabella dei tag. Lo collegherai con la tabella dei post esistente utilizzando una tabella di associazione, che è una tabella che collega le due tabelle in una relazione many-to-many. Una relazione many-to-many collega due tabelle in cui ogni elemento in una tabella ha molti elementi correlati nell’altra tabella. Nella tabella di associazione, ogni post farà riferimento ai suoi tag e ogni tag farà riferimento ai post taggati con esso. Inoltre inserirai alcuni post e tag nel tuo database, stampa i post con i loro tag e stampa i tag e i loro post correlati.

Supponiamo tu abbia una semplice tabella per i post del blog come segue:

Posts
+----+-----------------------------------+
| id | content                           |
+----+-----------------------------------+
| 1  | A post on life and death          |
| 2  | A post on joy                     |
+----+-----------------------------------+

E una tabella per i tag come segue:

Tags
+----+-------+
| id | name  |
+----+-------+
| 1  | life  |
| 2  | death |
| 3  | joy   |
+----+-------+

Supponiamo che tu voglia etichettare Un post sulla vita e la morte con le etichette vita e morte. Puoi farlo aggiungendo una nuova riga nella tabella dei post in questo modo:

Posts
+----+-----------------------------------+------+
| id | content                           | tags |
+----+-----------------------------------+------+
| 1  | A post on life and death          | 1, 2 |
| 2  | A post on joy                     |      |
+----+------------------------------------------+

Questo approccio non funziona, perché ogni colonna dovrebbe avere solo un valore. Se hai valori multipli, operazioni di base come l’aggiunta e l’aggiornamento dei dati diventano complesse e lente. Invece, dovrebbe esserci una terza tabella che faccia riferimento alle chiavi primarie delle tabelle correlate—questa tabella è spesso chiamata una tabella di associazione o una tabella di collegamento, e memorizza gli ID di ogni elemento di ogni tabella.

Ecco un esempio di tabella di associazione che collega i post alle etichette:

post_tag
+----+---------+-------------+
| id | post_id | tag_id      |
+----+---------+-------------+
| 1  | 1       | 1           |
| 2  | 1       | 2           |
+----+---------+-------------+

Nella prima riga, il post con l’ID 1 (ovvero Un post sulla vita e la morte) è correlato all’etichetta con l’ID 1 (vita). Nella seconda riga, lo stesso post è anche correlato all’etichetta con l’ID 2 (morte). Ciò significa che il post è etichettato con entrambe le etichette vita e morte. Allo stesso modo, puoi etichettare ogni post con più etichette.

Ora, modificherai il file app.py per aggiungere un nuovo modello di database che rappresenta la tabella che utilizzerai per memorizzare le etichette. Aggiungerai anche una tabella di associazione chiamata post_tag che collega i post alle etichette.

Per prima cosa, apri app.py per stabilire una relazione tra i post e le etichette:

  1. nano app.py

Aggiungi una tabella post_tag e un modello Tag sotto l’oggetto db e sopra il modello Post, quindi aggiungi una pseudo-colonna tags al modello Post in modo da poter accedere ai tag di un post tramite post.tags e accedere ai post di un tag tramite tag.posts:

flask_app/app.py

# ...

db = SQLAlchemy(app)


post_tag = db.Table('post_tag',
                    db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
                    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
                    )


class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))

    def __repr__(self):
        return f'<Tag "{self.name}">' 



class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100))
    content = db.Column(db.Text)
    comments = db.relationship('Comment', backref='post')
    tags = db.relationship('Tag', secondary=post_tag, backref='posts')

    def __repr__(self):
        return f'<Post "{self.title}">'

Salva e chiudi il file.

Qui usi la funzione db.Table() per creare una tabella con due colonne. Per le tabelle di associazione, la pratica migliore è utilizzare una tabella anziché un modello di database.

La tabella post_tag ha due colonne che rappresentano due chiavi esterne, che sono chiavi utilizzate per fare riferimento alle colonne delle chiavi primarie in un’altra tabella:

  • post_id: Una chiave esterna di tipo intero che rappresenta l’ID del post e fa riferimento alla colonna ID nella tabella post.
  • tag_id: Una chiave esterna di tipo intero che rappresenta l’ID del tag e fa riferimento alla colonna ID nella tabella tag.

Queste chiavi stabiliscono le relazioni tra le tabelle.

Sotto la tabella post_tag, crei un modello Tag, che rappresenta la tabella in cui verranno memorizzati i tag. Questa tabella dei tag ha due colonne:

  • id: L’ID del tag.
  • name: Il nome del tag.

Utilizzi il nome del tag nel metodo speciale __repr__() per dare ad ogni oggetto tag una rappresentazione stringa chiara per scopi di debug.

Si aggiunge una variabile di classe tags al modello Post. Si utilizza il metodo db.relationship(), passando ad esso il nome del modello dei tag (Tag in questo caso).

Si passa la tabella di associazione post_tag al parametro secondary per stabilire una relazione many-to-many tra i post e i tag.

Si utilizza il parametro backref per aggiungere un riferimento inverso che si comporta come una colonna al modello Tag. In questo modo, è possibile accedere ai post del tag tramite tag.posts e ai tag di un post tramite post.tags. Vedrai un esempio che dimostra questo in seguito.

Successivamente, si modifica il programma Python init_db.py per modificare il database aggiungendo la tabella di associazione post_tag e la tabella dei tag che sarà basata sul modello Tag:

  1. nano init_db.py

Si modifica il file in questo modo:

flask_app/init_db.py
from app import db, Post, Comment, Tag

db.drop_all()
db.create_all()

post1 = Post(title='Post The First', content='Content for the first post')
post2 = Post(title='Post The Second', content='Content for the Second post')
post3 = Post(title='Post The Third', content='Content for the third post')

comment1 = Comment(content='Comment for the first post', post=post1)
comment2 = Comment(content='Comment for the second post', post=post2)
comment3 = Comment(content='Another comment for the second post', post_id=2)
comment4 = Comment(content='Another comment for the first post', post_id=1)

tag1 = Tag(name='animals')
tag2 = Tag(name='tech')
tag3 = Tag(name='cooking')
tag4 = Tag(name='writing')

post1.tags.append(tag1)  # Aggiungi il primo post con 'animali'
post1.tags.append(tag4)  # Aggiungi il primo post con 'scrittura'
post3.tags.append(tag3)  # Aggiungi il terzo post con 'cucina'
post3.tags.append(tag2)  # Aggiungi il terzo post con 'tecnologia'
post3.tags.append(tag4)  # Aggiungi il terzo post con 'scrittura'


db.session.add_all([post1, post2, post3])
db.session.add_all([comment1, comment2, comment3, comment4])
db.session.add_all([tag1, tag2, tag3, tag4])

db.session.commit()

Salva e chiudi il file.

Qui importi il modello Tag. Elimini tutto nel database usando la funzione db.drop_all() per aggiungere in modo sicuro le tabelle dei tag e post_tag e evitare eventuali problemi comuni legati all’aggiunta di nuove tabelle a un database. Quindi crei tutte le tabelle di nuovo usando la funzione db.create_all().

Dopo il codice del tutorial precedente che dichiara i post e i commenti, usi il modello Tag per creare quattro tag.

Aggiungi quindi i tag ai post utilizzando l’attributo tags che è stato aggiunto tramite la riga tags = db.relationship('Tag', secondary=post_tag, backref='posts') nel file app.py. Assegni i tag ai post utilizzando un metodo append() simile alle liste Python.

Successivamente, aggiungi i tag creati alla sessione del database usando la funzione db.session.add_all().

Nota:

La funzione db.create_all() non ricrea o aggiorna una tabella se esiste già. Ad esempio, se modifichi il tuo modello aggiungendo una nuova colonna e esegui la funzione db.create_all(), la modifica apportata al modello non verrà applicata alla tabella se la tabella esiste già nel database. La soluzione è eliminare tutte le tabelle del database esistenti con la funzione db.drop_all() e quindi ricrearle con la funzione db.create_all(), come dimostrato nel file init_db.py.

Questo processo applicherà le modifiche che apporti ai tuoi modelli ma eliminerà anche tutti i dati esistenti nel database. Per aggiornare il database e conservare i dati esistenti, dovrai utilizzare la migrazione dello schema, che ti permette di modificare le tue tabelle e conservare i dati. Puoi utilizzare l’estensione Flask-Migrate per eseguire le migrazioni dello schema SQLAlchemy attraverso l’interfaccia della riga di comando di Flask.

Esegui il programma init_db.py per applicare le modifiche al database:

  1. python init_db.py

Il programma dovrebbe eseguire con successo senza nessun output. Se visualizzi un errore, assicurati di aver apportato correttamente le modifiche al file init_db.py.

Per dare un’occhiata ai post e ai tag attualmente presenti nel database, apri la shell di Flask:

  1. flask shell

Esegui il seguente codice Python che cicla attraverso i post e i tag:

from app import Post

posts = Post.query.all()

for post in posts:
    print(post.title)
    print(post.tags)
    print('---')

Qui, importi il modello Post da app.py. Interroghi la tabella dei post e recuperi tutti i post nel database. Cicli attraverso i post e stampi il titolo del post e l’elenco dei tag associati a ciascun post.

Otterrai un output simile al seguente:

Output
Post The First [<Tag "animals">, <Tag "writing">] --- Post The Third [<Tag "cooking">, <Tag "tech">, <Tag "writing">] --- Post The Second [] ---

Puoi accedere ai nomi dei tag utilizzando tag.name come dimostrato nel seguente esempio, che puoi eseguire utilizzando la shell di Flask:

from app import Post

posts = Post.query.all()

for post in posts:
    print('TITLE: ', post.title)
    print('-')
    print('TAGS:')
    for tag in post.tags:
        print('> ', tag.name)
    print('-'*30)

Qui, insieme alla stampa del titolo del post, cicli anche attraverso i tag di ciascun post e stampi il nome del tag.

Otterrai un output simile al seguente:

Output
TITLE: Post The First - TAGS: > animals > writing ------------------------------ TITLE: Post The Third - TAGS: > cooking > tech > writing ------------------------------ TITLE: Post The Second - TAGS: ------------------------------

Come puoi vedere, i tag che hai aggiunto ai post nel programma init_db.py sono correttamente collegati ai post con cui sono stati contrassegnati.

Per vedere una dimostrazione su come accedere ai post contrassegnati con un tag specifico tramite tag.posts, esegui il seguente codice nella shell di Flask:

from app import Tag

writing_tag = Tag.query.filter_by(name='writing').first()

for post in writing_tag.posts:
    print(post.title)
    print('-'*6)
    print(post.content)
    print('-')
    print([tag.name for tag in post.tags])
    print('-'*20)

Importi il modello Tag. Poi utilizzi il metodo filter_by() sull’attributo query passandogli un parametro name per ottenere il tag writing dal suo nome, e ottieni il primo risultato usando il metodo first(). Conservi l’oggetto del tag in una variabile chiamata writing_tag. Per ulteriori informazioni sul metodo filter_by, consulta Passaggio 4 del tutorial Come Utilizzare Flask-SQLAlchemy per Interagire con i Database in un’Applicazione Flask.

Scorri i post contrassegnati con il tag writing, a cui accedi tramite writing_tag.posts. Stampi il titolo del post, il contenuto e una lista di nomi di tag che costruisci usando una list comprehension basata sui tag del post, a cui accedi tramite post.tags.

Otterrai un output simile al seguente:

Output
Post The Third ------ Content for the third post - ['cooking', 'tech', 'writing'] -------------------- Post The First ------ Content for the first post - ['animals', 'writing'] --------------------

Qui vedi i due post che sono stati contrassegnati con il tag writing, e i nomi dei tag sono visualizzati in una lista Python.

Ora puoi accedere ai post e ai loro tag e accedere ai post di un tag specifico.

Hai aggiunto un modello di database che rappresenta la tabella dei tag. Hai collegato tra post e tag usando una tabella di associazione, e hai inserito alcuni tag nel database e li hai collegati ai post. Hai acceduto ai post e ai loro tag e ai post di un singolo tag. Successivamente, userai la shell di Flask per aggiungere nuovi post e nuovi tag e collegarli tra loro e imparerai come rimuovere i tag da un post.

Passaggio 3 — Gestione dei Dati in una Relazione Many-to-Many

In questo passaggio, userai la shell di Flask per aggiungere nuovi post al database, aggiungere tag e collegarli tra post e tag. Accederai ai post con i loro tag, e vedrai come disassociare un elemento da un altro nelle relazioni Many-to-Many.

Prima, con il tuo ambiente di programmazione attivato, apri la shell di Flask se non l’hai già fatto:

  1. flask shell

Successivamente, aggiungi alcuni post e tag:

from app import db, Post, Tag

life_death_post = Post(title='A post on life and death', content='life and death')
joy_post = Post(title='A post on joy', content='joy')

life_tag = Tag(name='life')
death_tag = Tag(name='death')
joy_tag = Tag(name='joy')

life_death_post.tags.append(life_tag)
life_death_post.tags.append(death_tag)
joy_post.tags.append(joy_tag)

db.session.add_all([life_death_post, joy_post, life_tag, death_tag, joy_tag])

db.session.commit()

Questo crea due post e tre tag. Etichetti i post con i loro tag correlati e utilizzi il metodo add_all() per aggiungere gli elementi appena creati alla sessione del database. Poi commetti le modifiche e le applichi al database usando il metodo commit().

Successivamente, usa la shell di Flask per ottenere tutti i post e i loro tag come hai fatto nel passaggio precedente:

posts = Post.query.all()

for post in posts:
    print(post.title)
    print(post.tags)
    print('---')

Otterrai un output simile al seguente:

Output
Post The First [<Tag "animals">, <Tag "writing">] --- Post The Third [<Tag "cooking">, <Tag "tech">, <Tag "writing">] --- Post The Second [] --- A post on life and death [<Tag "life">, <Tag "death">] --- A post on joy [<Tag "joy">] ---

Puoi vedere che i post sono stati aggiunti insieme ai loro tag.

Per dimostrare come interrompere una relazione tra due elementi in una relazione di database many-to-many, diciamo che il post Post The Third non riguarda più la cucina, quindi dovrai rimuovere il tag cooking da esso.

Prima, ottieni il post e il tag che desideri rimuovere:

  1. from app import db, Post, Tag
  2. post = Post.query.filter_by(title='Post The Third').first()
  3. tag = Tag.query.filter_by(name='cooking').first()
  4. print(post.title)
  5. print(post.tags)
  6. print(tag.posts)

Qui recuperi il post intitolato Post The Third utilizzando il metodo filter_by(). Ottieni il tag cooking. Stampi il titolo del post, i suoi tag e i post contrassegnati con il tag cooking.

Il metodo filter_by() restituisce un oggetto di query e puoi utilizzare il metodo all() per ottenere una lista di tutti i risultati. Ma poiché ci aspettiamo solo un risultato in questo caso, utilizziamo il metodo first() per ottenere il primo (ed unico) risultato. Per ulteriori informazioni sui metodi first() e all(), controlla il Passaggio 4 di Come Utilizzare Flask-SQLAlchemy per Interagire con i Database in un’Applicazione Flask.

Otterrai il seguente output:

Output
Post The Third [<Tag "cooking">, <Tag "tech">, <Tag "writing">] [<Post "Post The Third">]

Qui vedi il titolo del post, i tag del post e una lista dei post contrassegnati con il tag cooking.

Per rimuovere il tag cooking dal post, utilizza il metodo remove() così:

  1. post.tags.remove(tag)
  2. db.session.commit()
  3. print(post.tags)
  4. print(tag.posts)

Qui usi il metodo remove() per dissociare il tag cooking dal post. Quindi utilizzi il metodo db.session.commit() per applicare le modifiche al database.

Otterrai un output che conferma che il tag è stato rimosso dal post:

Output
[<Tag "tech">, <Tag "writing">] []

Come puoi vedere, il tag cooking non è più nella lista post.tags, e il post è stato rimosso dalla lista tag.posts.

Esci dalla shell di Flask:

  1. exit()

Hai aggiunto nuovi post e tag. Hai etichettato i post e hai rimosso tag dai post. Successivamente, visualizzerai i tag di ciascun post nella pagina dell’indice del tuo blog web Flask.

Passaggio 4 — Visualizzazione dei Tag Sotto Ciascun Post

In questo passaggio, modificherai il template dell’indice per visualizzare i tag sotto ciascun post.

Prima di tutto, dai un’occhiata alla pagina attuale della home del blog web Flask.

Con il tuo ambiente di programmazione attivato, informa Flask sull’applicazione (app.py in questo caso) utilizzando la variabile d’ambiente FLASK_APP. Quindi impostare la variabile d’ambiente FLASK_ENV su development per eseguire l’applicazione in modalità sviluppo:

  1. export FLASK_APP=app
  2. export FLASK_ENV=development

Successivamente, avvia l’applicazione:

  1. flask run

Con il server di sviluppo in esecuzione, visita l’URL seguente nel tuo browser:

http://127.0.0.1:5000/

Vedrai una pagina simile alla seguente:

Lascia il server di sviluppo in esecuzione e apri una nuova finestra terminale.

Avrai bisogno di visualizzare le etichette di ciascun post su due pagine: sotto ciascun post nella pagina dell’indice e sotto il contenuto del post nella pagina del post. Utilizzerai lo stesso codice per visualizzare le etichette. Per evitare la ripetizione del codice, utilizzerai una macro Jinja, che si comporta come una funzione Python. Una macro contiene codice HTML dinamico che può essere visualizzato ovunque tu chiami la macro, e modificandolo si applicano le modifiche ovunque è stata chiamata, il che rende il codice riutilizzabile.

Innanzitutto, apri un nuovo file chiamato macros.html nella tua directory templates:

  1. nano templates/macros.html

Aggiungi il seguente codice ad esso:

flask_app/templates/macros.html
{% macro display_tags(post) %}
    <div class="tags">
        <p>
            <h4>Tags:</h4>
            {% for tag in post.tags %}
                <a href="#" style="text-decoration: none; color: #dd5b5b">
                    {{ tag.name }}
                </a>
                |
            {% endfor %}
        </p>
    </div>
{% endmacro %}

Salva e chiudi il file.

Qui, utilizzi la parola chiave macro per dichiarare una macro chiamata display_tags() con un parametro chiamato post. Utilizzi un tag <div>, in cui visualizzi un’intestazione <h4>. Utilizzi un ciclo for per passare attraverso le etichette dell’oggetto post che verrà passato come argomento alla macro quando viene chiamato, simile al modo in cui viene passato un argomento in una chiamata di funzione Python. Ottieni le etichette tramite post.tags. Visualizzi il nome dell’etichetta all’interno di un tag <a>. In seguito modificherai il valore dell’attributo href per collegare a una pagina di tag che creerai dove verranno visualizzati tutti i post contrassegnati con un particolare tag. Specifica la fine della macro utilizzando la parola chiave endmacro.

Successivamente, per visualizzare le etichette sotto ciascun post nella pagina dell’indice, apri il file del modello index.html:

  1. nano templates/index.html

Prima di tutto, dovrai importare il macro display_tags() dal file macros.html. Aggiungi l’importazione seguente nella parte superiore sopra la linea {% extends 'base.html' %}:

flask_app/templates/index.html
{% from 'macros.html' import display_tags %}
{% extends 'base.html' %}

Successivamente, modifica il ciclo for post in posts, chiamando il macro display_tags() come segue:

flask_app/templates/index.html
{% for post in posts %}
    <div class="post">
        <p><b>#{{ post.id }}</b></p>
        <b>
            <p class="title">
                <a href="{{ url_for('post', post_id=post.id)}}">
                    {{ post.title }}
                </a>
            </p>
        </b>
        <div class="content">
            <p>{{ post.content }}</p>
        </div>

        {{ display_tags(post) }}

        <hr>
    </div>
{% endfor %}

Salva e chiudi il file.

Chiami il macro display_tags(), passandogli l’oggetto post. Ciò visualizzerà i nomi dei tag sotto ogni post.

Aggiorna la pagina dell’indice nel tuo browser e vedrai i tag sotto ogni post, come mostrato nell’immagine seguente:

Successivamente, aggiungerai i tag sotto il contenuto del post nella pagina del post. Apri il file del template post.html:

  1. nano templates/post.html

Prima importa il macro display_tags nella parte superiore:

flask_app/templates/post.html
{% from 'macros.html' import display_tags %}
{% extends 'base.html' %}

Quindi chiama il macro display_tags(), passandogli l’oggetto post sotto il contenuto del post e sopra il tag <hr>:

flask_app/templates/post.html
<div class="post">
    <p><b>#{{ post.id }}</b></p>
    <b>
        <p class="title">{{ post.title }}</p>
    </b>
    <div class="content">
        <p>{{ post.content }}</p>
    </div>

    {{ display_tags(post) }}

    <hr>
    <h3>Comments</h3>

Salva e chiudi il file.

Ora, vai alla pagina di un post:

http://127.0.0.1:5000/2

Vedrai che i tag vengono visualizzati nello stesso modo dei tag visualizzati sulla pagina dell’indice.

Hai visualizzato i tag che hai aggiunto ai post sotto ogni post. Successivamente, aggiungerai una nuova route alla tua applicazione Flask che visualizza tutti i post contrassegnati con un tag specifico. Quindi renderai funzionali i collegamenti ai tag che hai visualizzato in questo passaggio.

Passaggio 5 — Visualizzazione dei tag e dei loro post

In questo passaggio, aggiungerai una route e un template alla tua applicazione web per visualizzare i tag che hai nel tuo database e i relativi post.

Per prima cosa, aggiungerai una route per visualizzare i post di ogni tag. Ad esempio, la route /tags/nome_tag/ mostrerà una pagina che visualizza tutti i post contrassegnati con un tag chiamato nome_tag.

Apri app.py per modificarlo:

  1. nano app.py

Aggiungi la seguente route alla fine del file:

flask_app/app.py

# ...

@app.route('/tags/<tag_name>/')
def tag(tag_name):
    tag = Tag.query.filter_by(name=tag_name).first_or_404()
    return render_template('tag.html', tag=tag)

Salva e chiudi il file.

Qui utilizzi una variabile di URL chiamata nome_tag che determina il tag e i post contrassegnati con esso che verranno visualizzati sulla pagina del tag. Il nome del tag viene passato alla funzione di visualizzazione tag() tramite il parametro nome_tag, che utilizzi sul metodo filter_by() per interrogare il tag. Utilizzi first_or_404() per ottenere l’oggetto tag e memorizzarlo in una variabile chiamata tag, o per rispondere con un messaggio di errore 404 Not Found nel caso in cui non esista nessun tag con il nome fornito nel database.

Successivamente renderizzi un file di template chiamato tag.html, passandogli l’oggetto tag.

Apri il nuovo file templates/tag.html per modificarlo:

  1. nano templates/tag.html

Aggiungi il seguente codice:

flask_app/templates/tag.html
{% from 'macros.html' import display_tags %}
{% extends 'base.html' %}

{% block content %}
    <span class="title">
        <h1>{% block title %} Posts Tagged with "{{ tag.name }}" {% endblock %}</h1>
    </span>
    <div class="content">
        {% for post in tag.posts %}
        <div class="post">
            <p><b>#{{ post.id }}</b></p>
            <b>
                <p class="title">
                    <a href="{{ url_for('post', post_id=post.id)}}">
                        {{ post.title }}
                    </a>
                </p>
            </b>
            <div class="content">
                <p>{{ post.content }}</p>
            </div>

            {{ display_tags(post) }}

            <hr>
        </div>
        {% endfor %}
    </div>
{% endblock %}

Salva e chiudi il file.

Importi il macro display_tags() da macros.html, e estendi il template base.

Nel blocco del contenuto, imposti un’intestazione come titolo con il nome del tag incluso. Poi fai un loop sui post contrassegnati con il tag dato, ai quali accedi tramite tag.posts. Visualizzi l’ID del post, il titolo del post e il contenuto del post. Poi chiami il macro display_tags() per visualizzare tutti i tag dei post.

Con il tuo server di sviluppo in esecuzione, vai all’URL seguente:

http://127.0.0.1:5000/tags/writing/

Questa è la pagina per il tag writing. Come puoi vedere, vengono visualizzati tutti i post contrassegnati con writing:

Ora modifica il macro display_tags() per rendere funzionali i link dei tag. Apri macros.html:

  1. nano templates/macros.html

Modifica il valore dell’attributo href come segue:

flask_app/templates/macros.html

{% macro display_tags(post) %}
    <div class="tags">
        <p>
            <h4>Tags:</h4>
            {% for tag in post.tags %}
            <a href="{{ url_for('tag', tag_name=tag.name) }}"
               style="text-decoration: none; color: #dd5b5b">
                    {{ tag.name }}
                </a>
                |
            {% endfor %}
        </p>
    </div>
{% endmacro %}

Salva e chiudi il file.

Aggiorna le pagine dove è stato usato il macro display_tags(), e vedrai che i link dei tag sono ora funzionali:

http://127.0.0.1:5000/
http://127.0.0.1:5000/2/
http://127.0.0.1:5000/tags/writing/

Come puoi vedere, l’utilizzo dei macro Jinja ti consente di riutilizzare il codice, e modificare un macro applica le modifiche su più template.

Hai aggiunto una pagina per i tag individuali dove gli utenti possono visualizzare tutti i post contrassegnati con un tag specifico, e i tag sotto i post ora linkano a questa nuova pagina.

Conclusione

I tag che hai aggiunto al tuo sistema di blogging dimostrano come gestire le relazioni molti a molti utilizzando l’estensione Flask-SQLAlchemy. Hai imparato come collegare due tabelle correlate utilizzando una tabella di associazione (anche chiamata una tabella di join), associare una voce con un’altra, aggiungere la voce al database e accedere e disassociare i dati da una voce.

Se desideri leggere di più su Flask, dai un’occhiata agli altri tutorial nella serie Come Costruire Applicazioni Web con Flask.

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-many-to-many-database-relationships-with-flask-sqlalchemy