Hoe veel-op-veel-database-relaties gebruiken met Flask-SQLAlchemy

De auteur heeft het Free and Open Source Fund geselecteerd om een donatie te ontvangen als onderdeel van het Write for Donations-programma.

Introductie

Flask is een lichtgewicht Python-webframework dat handige tools en functies biedt voor het maken van webapplicaties in de Python-taal. SQLAlchemy is een SQL-toolkit die efficiënte en hoogwaardige toegang tot databases biedt voor relationele databases. Het biedt manieren om te communiceren met verschillende database-engines zoals SQLite, MySQL en PostgreSQL. Het geeft je toegang tot de SQL-functionaliteiten van de database. Het geeft je ook een Object Relational Mapper (ORM), waarmee je queries kunt maken en gegevens kunt verwerken met behulp van Python-objecten en -methoden. Flask-SQLAlchemy is een Flask-extensie die het gebruik van SQLAlchemy met Flask vergemakkelijkt, waardoor je tools en methoden krijgt om met je database te communiceren in je Flask-toepassingen via 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 deze tutorial ga je een applicatie die is gebouwd met Flask en Flask-SQLAlchemy aanpassen door een many-to-many relatie eraan toe te voegen. Je zult een relatie hebben tussen berichten en tags, waarbij elk blogbericht meerdere tags kan hebben en elke tag meerdere berichten kan hebben die ermee zijn getagd.

Hoewel je deze tutorial zelfstandig kunt volgen, is het ook een voortzetting van de tutorial Hoe u One-to-Many Database-relaties gebruikt met Flask-SQLAlchemy, waarin je een database met meerdere tabellen bouwt met een one-to-many relatie tussen berichten en opmerkingen in een blogtoepassing.

Aan het einde van de tutorial zal je applicatie een nieuwe functie hebben voor het toevoegen van tags aan berichten. Berichten kunnen worden getagd met meerdere tags, en elke tagpagina zal alle berichten weergeven die ermee zijn getagd.

Vereisten

Stap 1 — Het opzetten van de webapplicatie

In deze stap zult u de blogapplicatie gereed maken voor aanpassing. U zult ook de Flask-SQLAlchemy databasemodellen en de Flask-routes bekijken om de structuur van de applicatie te begrijpen. Als u de tutorial in de sectie met vereisten hebt gevolgd en nog steeds de code en de virtuele omgeving op uw lokale machine hebt, kunt u deze stap overslaan.

Om een ​​veel-op-veel-relatie toe te voegen aan een Flask-webapplicatie met Flask-SQLAlchemy, zult u de toepassingscode van de vorige tutorial gebruiken, die een blogsysteem is met de mogelijkheid om berichten toe te voegen en weer te geven, opmerkingen te plaatsen bij berichten, en bestaande opmerkingen te lezen en te verwijderen.

Kloon het repository en hernoem het van flask-slqa-bloggy naar flask_app met het volgende commando:

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

Navigeer naar flask_app:

  1. cd flask_app

Maak vervolgens een nieuwe virtuele omgeving aan:

  1. python -m venv env

Activeer de omgeving:

  1. source env/bin/activate

Installeer Flask en Flask-SQLAlchemy:

  1. pip install Flask Flask-SQLAlchemy

Vervolgens stelt u de volgende omgevingsvariabelen in:

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

FLASK_APP geeft aan welke toepassing u momenteel ontwikkelt, dat wil zeggen app.py in dit geval. FLASK_ENV specificeert de modus. U zet deze op development voor de ontwikkelingsmodus; hiermee kunt u de toepassing debuggen. Onthoud dat u deze modus niet in een productieomgeving moet gebruiken.

Open vervolgens de Flask-shell om de databasetabellen te maken:

  1. flask shell

Importeer vervolgens het db Flask-SQLAlchemy-databaseobject, het Post-model en het Comment-model, en maak de databasetabellen met de functie db.create_all():

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

Populeer vervolgens de database met het programma init_db.py:

  1. python init_db.py

Dit voegt drie berichten en vier opmerkingen toe aan de database.

Start de ontwikkelingsserver:

  1. flask run

Als u naar uw browser gaat, heeft u de toepassing draaiend op de volgende URL:

http://127.0.0.1:5000/

Je ziet een pagina die vergelijkbaar is met de volgende:

Als je een foutmelding krijgt, zorg er dan voor dat je de bovenstaande stappen correct hebt gevolgd.

Om de ontwikkelingsserver te stoppen, gebruik CTRL + C.

Vervolgens ga je door de Flask-SQLAlchemy database-modellen om de huidige relaties tussen tabellen te begrijpen. Als je bekend bent met de inhoud van het bestand app.py, kun je naar de volgende stap gaan.

Open het bestand app.py:

  1. nano app.py

De inhoud van het bestand is als volgt:

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))

Hier heb je twee database-modellen die twee tabellen vertegenwoordigen:

  • Post: die een ID-kolom heeft, een titel, inhoud, en een Eén-op-Veel relatie met de comments-tabel.

  • Comment: die een ID-kolom heeft, een kolom voor inhoud, en een post_id kolom om te verwijzen naar de post waar de reactie bij hoort.

Onder de modellen heb je de volgende routes:

  • /: De startpagina, die alle berichten in de database weergeeft.
  • /<int:post_id>/: De individuele berichtpagina. Bijvoorbeeld, de link http://127.0.0.1:5000/2/ toont de details van het tweede bericht in de database en zijn reacties.
  • /comments/: Een pagina die alle opmerkingen in de database weergeeft en koppelingen naar de post waarop elke opmerking is geplaatst.
  • /comments/<int:comment_id>/delete: Een route die een opmerking verwijdert via een Opmerking verwijderen knop.

Sluit het app.py bestand.

In de volgende stap zul je een many-to-many relatie gebruiken om een link tussen twee tabellen te maken.

Stap 2 — Instellen van Database Modellen voor een Many-to-Many Relatie

In deze stap voeg je een database model toe dat de tags tabel zal vertegenwoordigen. Je zult het koppelen met de bestaande posts tabel met behulp van een associatietabel, wat een tabel is die je twee tabellen verbindt in een many-to-many relatie. Een many-to-many relatie verbindt twee tabellen waar elk item in een tabel veel gerelateerde items heeft in de andere tabel. In de associatietabel zal elke post verwijzen naar zijn tags en elke tag verwijst naar de posts die ermee zijn getagd. Je zult ook een paar posts en tags in je database invoegen, posts met hun tags afdrukken, en tags en hun gerelateerde posts afdrukken.

Laten we zeggen dat je een eenvoudige tabel hebt voor blogposts zoals volgt:

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

En een tabel voor tags als volgt:

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

Laten we zeggen dat je Een bericht over leven en dood wilt taggen met de tags leven en dood. Dit kun je doen door een nieuwe rij toe te voegen in de berichtentabel zoals hier:

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

Deze aanpak werkt niet, omdat elke kolom slechts één waarde mag hebben. Als je meerdere waarden hebt, worden basisbewerkingen zoals het toevoegen en bijwerken van gegevens omslachtig en traag. In plaats daarvan moet er een derde tabel zijn die verwijst naar primaire sleutels van gerelateerde tabellen – deze tabel wordt vaak een associatietabel of een koppeltabel genoemd, en het slaat ID’s op van elk item uit elke tabel.

Hier is een voorbeeld van een associatietabel die koppelingen legt tussen berichten en tags:

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

In de eerste rij heeft het bericht met de ID 1 (dat wil zeggen, Een bericht over leven en dood) betrekking op de tag met de ID 1 (leven). In de tweede rij heeft hetzelfde bericht ook betrekking op de tag met de ID 2 (dood). Dit betekent dat het bericht is getagd met zowel de leven– als de dood-tags. Op dezelfde manier kun je elk bericht taggen met meerdere tags.

Nu ga je het bestand app.py aanpassen om een nieuw databasemodel toe te voegen dat de tabel vertegenwoordigt die je zult gebruiken voor het opslaan van tags. Je voegt ook een associatietabel toe die post_tag heet en berichten met tags verbindt.

Open eerst app.py om een relatie tussen berichten en tags vast te stellen:

  1. nano app.py

Voeg een post_tag tabel en een Tag model toe onder de db object en boven het Post model, en voeg dan een tags relatie pseudo-kolom toe aan het Post model zodat je toegang hebt tot de tags van een post via post.tags en toegang hebt tot de posts van een tag via 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}">'

Opslaan en sluit het bestand.

Hier gebruik je de db.Table() functie om een tabel met twee kolommen te maken. Voor associatietabellen is de beste praktijk om een tabel te gebruiken in plaats van een database model.

De post_tag tabel heeft twee kolommen die twee externe sleutels vertegenwoordigen, welke sleutels worden gebruikt om te verwijzen naar primaire sleutelkolommen in een andere tabel:

  • post_id: Een integer externe sleutel die het post-ID vertegenwoordigt en verwijst naar de ID kolom in de post tabel.
  • tag_id: Een integer externe sleutel die het tag-ID vertegenwoordigt en verwijst naar de ID kolom in de tag tabel.

Deze sleutels leggen de relaties tussen tabellen vast.

Onder de post_tag tabel, maak je een Tag model aan, dat de tabel vertegenwoordigt waarin je tags zult opslaan. Deze tags tabel heeft twee kolommen:

  • id: Het ID van de tag.
  • name: De naam van de tag.

Je gebruikt de naam van de tag in de speciale __repr__() methode om elk tag object een duidelijke stringrepresentatie te geven voor debugdoeleinden.

Je voegt een tags klassevariabele toe aan het Post model. Je gebruikt de db.relationship() methode en geeft het de naam van het tags model (Tag in dit geval).

Je geeft de post_tag associatietabel door aan de secondary parameter om een veel-op-veel relatie tussen posts en tags tot stand te brengen.

Je gebruikt de backref parameter om een achterwaartse referentie toe te voegen die zich gedraagt als een kolom in het Tag model. Op deze manier kun je toegang krijgen tot de posts van een tag via tag.posts en de tags van een post via post.tags. Een voorbeeld hiervan wordt later getoond.

Vervolgens bewerk je het Python programma init_db.py om de database te wijzigen door de post_tag associatietabel en de tags tabel toe te voegen die gebaseerd zal zijn op het Tag model:

  1. nano init_db.py

Bewerk het bestand zodat het er als volgt uitziet:

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)  # Tag de eerste post met 'dieren'
post1.tags.append(tag4)  # Tag de eerste post met 'schrijven'
post3.tags.append(tag3)  # Tag de derde post met 'koken'
post3.tags.append(tag2)  # Tag de derde post met 'technologie'
post3.tags.append(tag4)  # Tag de derde post met 'schrijven'


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()

Sla het bestand op en sluit het.

Hier importeer je het Tag model. Je verwijdert alles in de database met behulp van de db.drop_all() functie om de tags en post_tag tabellen veilig toe te voegen en eventuele veelvoorkomende problemen te vermijden die verband houden met het toevoegen van nieuwe tabellen aan een database. Vervolgens maak je alle tabellen opnieuw aan met behulp van de db.create_all() functie.

Na de code uit de vorige handleiding waarin de posts en reacties worden verklaard, gebruik je het Tag model om vier tags te maken.

Vervolgens voeg je tags toe aan berichten met behulp van het tags attribuut dat is toegevoegd via de regel tags = db.relationship('Tag', secondary=post_tag, backref='posts') in het app.py bestand. Je kent tags toe aan berichten met behulp van een append() methode vergelijkbaar met Python-lijsten.

Vervolgens voeg je de tags die je hebt gemaakt toe aan de database sessie met behulp van de db.session.add_all() functie.

Opmerking:

De db.create_all() functie maakt geen tabel opnieuw aan of werkt deze niet bij als deze al bestaat. Bijvoorbeeld, als je je model aanpast door een nieuwe kolom toe te voegen en de db.create_all() functie uitvoert, zal de wijziging die je aanbrengt in het model niet worden toegepast op de tabel als deze al bestaat in de database. De oplossing is om alle bestaande databasetabellen te verwijderen met de db.drop_all() functie en ze vervolgens opnieuw aan te maken met de db.create_all() functie, zoals gedemonstreerd in het init_db.py bestand.

Deze procedure zal de wijzigingen toepassen die u aanbrengt in uw modellen, maar zal ook alle bestaande gegevens in de database verwijderen. Om de database bij te werken en bestaande gegevens te behouden, moet u schema migratie gebruiken, waarmee u uw tabellen kunt wijzigen en gegevens kunt behouden. U kunt de Flask-Migrate extensie gebruiken om SQLAlchemy schema migraties uit te voeren via de Flask-commandoregelinterface.

Voer het programma init_db.py uit om wijzigingen toe te passen op de database:

  1. python init_db.py

Het programma moet succesvol worden uitgevoerd zonder enige uitvoer. Als u een foutmelding ziet, zorg er dan voor dat u de wijzigingen correct hebt aangebracht in het bestand init_db.py.

Om een kijkje te nemen bij de berichten en tags die momenteel in de database staan, opent u de Flask-shell:

  1. flask shell

Voer de volgende Python-code uit die door berichten en tags loopt:

from app import Post

posts = Post.query.all()

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

Hier importeert u het Post-model uit app.py. U bevraagt de tabel met berichten en haalt alle berichten op in de database. U loopt door de berichten en u print de titel van het bericht en de lijst met tags die aan elk bericht zijn gekoppeld.

U krijgt een uitvoer vergelijkbaar met het volgende:

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

U kunt tag-namen benaderen met tag.name zoals gedemonstreerd in het volgende voorbeeld, dat u kunt uitvoeren met de Flask-shell:

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)

Hier, naast het afdrukken van de titel van het bericht, loopt u ook door de tags van elk bericht en print u de tag-naam.

U krijgt een uitvoer vergelijkbaar met het volgende:

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

Zoals je kunt zien, zijn de tags die je hebt toegevoegd aan berichten in het programma init_db.py correct gekoppeld aan de berichten waarmee ze zijn getagd.

Om een demonstratie te zien van hoe je toegang krijgt tot berichten die zijn getagd met een specifieke tag via tag.posts, voer je de volgende code uit in de Flask shell:

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)

Je importeert het Tag-model. Vervolgens gebruik je de filter_by()-methode op het query-attribuut, waarbij je een name-parameter doorgeeft om de writing-tag op naam te krijgen, en je krijgt het eerste resultaat met behulp van de first()-methode. Je slaat het tag-object op in een variabele genaamd writing_tag. Voor meer informatie over de filter_by-methode, zie Stap 4 van de Tutorial Hoe gebruik je Flask-SQLAlchemy om te communiceren met databases in een Flask-applicatie.

Je doorloopt de berichten die zijn getagd met de writing-tag, die je bereikt via writing_tag.posts. Je drukt de titel van het bericht af, de inhoud en een lijst van tagnamen die je construeert met behulp van een list comprehension gebaseerd op de tags van het bericht, die je bereikt via post.tags.

Je krijgt een uitvoer die vergelijkbaar is met het volgende:

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

Hier zie je de twee berichten die zijn getagd met de writing-tag, en tagnamen worden weergegeven in een Python-lijst.

Je kunt nu berichten en hun tags benaderen en berichten van een specifieke tag benaderen.

U heeft een databasemodel toegevoegd dat de tagtabel voorstelt. U heeft een koppeling gemaakt tussen berichten en tags met behulp van een associatietabel en een paar tags in de database ingevoegd en berichten ermee getagd. U heeft toegang gekregen tot berichten en hun tags en de berichten van een individuele tag. Vervolgens zult u de Flask-shell gebruiken om nieuwe berichten en nieuwe tags toe te voegen en een koppeling te maken tussen tags en berichten, en u zult leren hoe u tags van een bericht kunt verwijderen.

Stap 3 — Beheer van gegevens in een veel-op-veel relatie

In deze stap zult u de Flask-shell gebruiken om nieuwe berichten aan de database toe te voegen, tags toe te voegen en een koppeling te maken tussen berichten en tags. U zult toegang krijgen tot berichten met hun tags, en u zult zien hoe u een item van een ander kunt ontkoppelen in veel-op-veel relaties.

Eerst, met uw geactiveerde programmeeromgeving, opent u de Flask-shell als u dat nog niet heeft gedaan:

  1. flask shell

Vervolgens voegt u een paar berichten en tags toe:

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()

Dit maakt twee berichten en drie tags aan. U tagt berichten met hun gerelateerde tags, en u gebruikt de add_all()-methode om de nieuw gemaakte items toe te voegen aan de databasesessie. Vervolgens commit u de wijzigingen en past u ze toe op de database met behulp van de commit()-methode.

Gebruik vervolgens de Flask-shell om alle berichten en hun tags te krijgen zoals u in de vorige stap heeft gedaan:

posts = Post.query.all()

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

U krijgt een output die vergelijkbaar is met het volgende:

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">] ---

U kunt zien dat berichten zijn toegevoegd samen met hun tags.

Om te laten zien hoe je een relatie tussen twee items in een many-to-many database relatie kunt verbreken, laten we zeggen dat de post Post The Third niet langer over koken gaat, dus je moet de koken tag ervan verwijderen.

Eerst haal je de post en de tag die je wilt verwijderen op:

  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)

Hier haal je de post met de titel Post The Third op met behulp van de filter_by() methode. Je krijgt de koken tag. Je print de titel van de post, zijn tags, en de posts getagd met de koken tag.

De filter_by() methode retourneert een query object, en je kunt de all() methode gebruiken om een lijst van alle resultaten te krijgen. Maar omdat we in dit geval slechts één resultaat verwachten, gebruik je de first() methode om het eerste (en enige) resultaat te krijgen. Voor meer informatie over de first() en all() methodes, bekijk Stap 4 van Hoe Flask-SQLAlchemy te Gebruiken om te Interageren met Databases in een Flask Applicatie.

Je krijgt de volgende output:

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

Hier zie je de post titel, de post tags, en een lijst van de posts getagd met de koken tag.

Om de koken tag van de post te verwijderen, gebruik je de remove() methode als volgt:

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

Hier gebruik je de remove() methode om de koken tag te dissociëren van de post. Dan gebruik je de db.session.commit() methode om de wijzigingen toe te passen op de database.

U krijgt een uitvoer die bevestigt dat de tag uit het bericht is verwijderd:

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

Zoals u kunt zien, staat de koken tag niet langer in de bericht.tags lijst, en het bericht is verwijderd uit de tag.berichten lijst.

Verlaat de flask shell:

  1. exit()

U heeft nieuwe berichten en tags toegevoegd. U heeft berichten getagd en tags van berichten verwijderd. Vervolgens zult u de tags van elk bericht weergeven op de indexpagina van uw Flask-webblog.

Stap 4 — Tags weergeven onder elk bericht

In deze stap zult u de indexsjabloon bewerken om tags onder elk bericht weer te geven.

Kijk eerst naar de huidige startpagina van de Flask-webblog.

Met uw programmeeromgeving geactiveerd, vertel Flask over de applicatie (app.py in dit geval) met behulp van de FLASK_APP omgevingsvariabele. Stel vervolgens de FLASK_ENV omgevingsvariabele in op ontwikkeling om de toepassing in ontwikkelingsmodus uit te voeren:

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

Voer vervolgens de toepassing uit:

  1. flask run

Met de ontwikkelingsserver actief, bezoek de volgende URL in uw browser:

http://127.0.0.1:5000/

U ziet een pagina vergelijkbaar met de volgende:

Laat de ontwikkelingsserver actief en open een nieuw terminalvenster.

Je moet de tags van elk bericht weergeven op twee pagina’s: onder elk bericht op de indexpagina en onder de berichtinhoud op de berichtpagina. Je zult dezelfde code gebruiken om tags weer te geven. Om herhaling van code te vermijden, zul je een Jinja-macro gebruiken, die zich gedraagt als een Python-functie. Een macro bevat dynamische HTML-code die overal kan worden weergegeven waar je de macro oproept, en het bewerken ervan brengt wijzigingen aan waar de macro is opgeroepen, wat de code herbruikbaar maakt.

Open eerst een nieuw bestand met de naam macros.html in je templates-map:

  1. nano templates/macros.html

Voeg de volgende code eraan toe:

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 %}

Bewaar en sluit het bestand.

Hier gebruik je het macro-sleutelwoord om een macro genaamd display_tags() te declareren met een parameter genaamd post. Je gebruikt een <div>-tag, waarin je een <h4>-kop weergeeft. Je gebruikt een for-lus om door de tags van het berichtobject te gaan dat als argument aan de macro zal worden doorgegeven bij het oproepen ervan, vergelijkbaar met hoe een argument wordt doorgegeven in een Python-functieoproep. Je krijgt tags via post.tags. Je geeft de tagnaam weer binnen een <a>-tag. Je zult later de waarde van het href-attribuut bewerken om te linken naar een tagpagina die je zult maken waar alle berichten met een bepaalde tag worden weergegeven. Je geeft het einde van de macro aan met het endmacro-sleutelwoord.

Vervolgens, om tags weer te geven onder elk bericht op de indexpagina, open je het index.html-sjabloonbestand:

  1. nano templates/index.html

Eerst moet je de display_tags()-macro importeren uit het macros.html-bestand. Voeg de volgende import toe bovenaan vóór de regel {% extends 'base.html' %}:

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

Vervolgens bewerk je de for post in posts-loop door de display_tags()-macro als volgt aan te roepen:

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 %}

Sla het bestand op en sluit het.

Je roept de display_tags()-macro aan en geeft het de post-object door. Hierdoor worden tagnamen onder elke post weergegeven.

Vernieuw de indexpagina in je browser en je zult tags onder elke post zien, zoals weergegeven in de volgende afbeelding:

Vervolgens voeg je tags toe onder de inhoud van de post op de pagina van de post. Open het post.html-sjabloonbestand:

  1. nano templates/post.html

Eerst importeer je de display_tags-macro bovenaan:

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

Roep vervolgens de display_tags()-macro aan, geef het de post-object door onder de postinhoud en boven de <hr>-tag:

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>

Sla het bestand op en sluit het.

Navigeer nu naar een postpagina:

http://127.0.0.1:5000/2

Je zult zien dat tags op dezelfde manier worden weergegeven als de tags die op de indexpagina worden weergegeven.

Je hebt de tags weergegeven die je aan berichten hebt toegevoegd onder elke post. Als volgende stap voeg je een nieuwe route toe aan je Flask-applicatie die alle berichten weergeeft die zijn getagd met een specifieke tag. Vervolgens maak je de taglinks functioneel die je in deze stap hebt weergegeven.

Stap 5 — Weergave van Tags en Hun Berichten

In deze stap voegt u een route en een sjabloon toe aan uw webtoepassing om de tags weer te geven die u in uw database heeft en hun berichten.

Eerst voegt u een route toe voor het weergeven van de berichten van elke tag. Bijvoorbeeld, de route /tags/tag_naam/ zal een pagina tonen die alle berichten weergeeft die getagd zijn met een tag genaamd tag_naam.

Open app.py om te bewerken:

  1. nano app.py

Voeg de volgende route toe aan het einde van het bestand:

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)

Sla het bestand op en sluit het.

Hier gebruikt u een URL-variabele genaamd tag_naam die de tag en de berichten die ermee zijn getagd bepaalt die op de tagpagina worden weergegeven. De tagnaam wordt doorgegeven aan de weergavefunctie tag() via de parameter tag_naam, die u gebruikt op de methode filter_by() om de tag op te vragen. U gebruikt first_or_404() om het tagobject te verkrijgen en op te slaan in een variabele genaamd tag, of om te reageren met een foutmelding 404 Niet gevonden in het geval dat er geen tag met de opgegeven naam in de database bestaat.

Vervolgens rendert u een sjabloonbestand genaamd tag.html, waarbij u het tag-object doorgeeft.

Open het nieuwe templates/tag.html om te bewerken:

  1. nano templates/tag.html

Voeg de volgende code eraan toe:

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 %}

Sla het bestand op en sluit het.

U importeert de display_tags() macro vanuit macros.html, en breidt de basistemplate uit.

In het inhoudsblok stelt u een kop in als titel met de tagnaam inbegrepen. Vervolgens loopt u door de berichten getagd met de opgegeven tag, die u kunt openen via tag.posts. U toont het bericht-ID, de berichttitel en de berichtinhoud. Vervolgens roept u de display_tags() macro aan om alle posttags weer te geven.

Met uw ontwikkelingsserver actief, navigeert u naar de volgende URL:

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

Dit is de pagina voor de writing-tag. Zoals u kunt zien, worden alle berichten die getagd zijn met writing weergegeven:

Wijzig nu de display_tags() macro om de taglinks functioneel te maken. Open macros.html:

  1. nano templates/macros.html

Wijzig de waarde van het href-attribuut als volgt:

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 %}

Sla het bestand op en sluit het.

Vernieuw de pagina’s waar de display_tags() macro is gebruikt, en u zult zien dat de taglinks nu functioneel zijn:

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

Zoals u kunt zien, stellen Jinja-macro’s u in staat om code opnieuw te gebruiken, en het bewerken van een macro past wijzigingen toe in meerdere sjablonen.

U hebt een pagina toegevoegd voor individuele tags waar gebruikers alle berichten kunnen bekijken die zijn getagd met een specifieke tag, en de tags onder de berichten linken nu naar deze nieuwe pagina.

Conclusie

De tags-functie die je hebt toegevoegd aan je blogsysteem laat zien hoe je veel-op-veel relaties beheert met behulp van de Flask-SQLAlchemy-extensie. Je hebt geleerd hoe je kunt linken tussen twee gerelateerde tabellen met behulp van een associatietabel (ook wel een koppelings-tabel genoemd), een item kunt koppelen aan een ander, het item aan de database kunt toevoegen, en toegang kunt krijgen tot en gegevens kunt ontkoppelen van een item.

Als je meer wilt lezen over Flask, bekijk dan de andere tutorials in de Hoe Webapplicaties te Bouwen met Flask serie.

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