Fonctionnalité de favoris et gestion d'administration

Dans ce chapitre, vous apprendrez comment implémenter une fonctionnalité de favoris en utilisant une relation plusieurs-à-plusieurs, et utiliser sqladmin pour mettre en place un back-end d'administration.


Conception de relation plusieurs-à-plusieurs

Exemple

# Chemin de fichier : models.py - ajouter une table d'association et modifier le modèle Post
from sqlalchemy import Table

# Table d'association : table intermédiaire plusieurs-à-plusieurs entre User et Post
favorites = Table(
    "favorites",
    Base.metadata,
    Column("user_id", Integer, ForeignKey("users.id"), primary_key=True),
    Column("post_id", Integer, ForeignKey("posts.id"), primary_key=True),
    Column("created_at", DateTime, default=datetime.utcnow)
)

Exécuter la migration :

(venv) $ alembic revision --autogenerate -m "新增收藏关联表"
(venv) $ alembic upgrade head

Routes d'ajout et de retrait des favoris

Exemple

# Chemin du fichier : routers/favorites.py
from fastapi import APIRouter, Depends, HTTPException, Demande
from sqlalchemy.orm import Session
from database import get_db
from models import Post, favorites
from routers.users import get_current_user
from fastapi.templating import Jinja2Templates

router = APIRouter(prefix="/favorites", tags=["Favorites"])
templates = Jinja2Templates(directory="templates")

@router.post("/toggle/{post_id}")
def toggle_favorite(
    post_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user = Depends(get_current_user)    # Connexion requise
):
    """Basculer le statut de favori"""
    post = db.query(Post).filter(Post.id == post_id).first()
    if not post:
        raise HTTPException(status_code=404, detail=“L'article n'existe pas”)

    # Vérifier si déjà en favori
    is_faved = db.query(favorites).filter(
        favorites.c.user_id == current_user.id,
        favorites.c.post_id == post.id
    ).first() is not None

    if is_faved:
        # Retirer des favoris
        db.execute(
            favorites.delete().where(
                (favorites.c.user_id == current_user.id) &
                (favorites.c.post_id == post.id)
            )
        )
        db.commit()
    else:
        # Ajouter aux favoris
        db.execute(
            favorites.insert().values(user_id=current_user.id, post_id=post.id)
        )
        db.commit()

    from fastapi.responses import RedirectResponse
    return RedirectResponse(url=request.headers.get("referer", "/"), status_code=303)


@router.get("/", name="favorites_list")
def favorites_list(
    request: Request,
    db: Session = Depends(get_db),
    current_user = Depends(get_current_user)
):
    """Ma liste de favoris"""
    faved_posts = db.query(Post).join(
        favorites, (favorites.c.post_id == Post.id)
    ).filter(
        favorites.c.user_id == current_user.id
    ).order_by(favorites.c.created_at.desc()).all()

    return templates.TemplateResponse("favorites.html", {
        "request": request,
        "posts": faved_posts
    })

Ajouter un bouton Favori sur la page de détail

Exemple

Les paramètres initiaux ont été définis.
<div class="post-header">
    <h1>{{ post.title }}</h1>
    <form method="post" action="/favorites/toggle/{{ post.id }}">
        <button type="submit" class="fav-btn">♡ Favori</bouton>
    </forme>
</div>

sqladmin — Gestion de l'administration

sqladminC'est l'extension d'administration backend de l'écosystème FastAPI qui est l'équivalent de Django Admin.

Il est conçu spécifiquement pour SQLAlchemy et s'intègre parfaitement à FastAPI.

(venv) $ pip install sqladmin

Exemple

# Chemin du fichier : configurer sqladmin dans main.py
from sqladmin import Admin, ModelView
from database import engine
from models import Post, Category, User

class PostAdmin(ModelView, model=Post):
    column_list = [Post.id, Post.title, Post.category, Post.created_at]
    column_searchable_list = [Post.title, Post.summary]
    column_sortable_list = [Post.created_at, Post.title]
    form_columns = [Post.title, Post.slug, Post.summary, Post.content, Post.category_id]

class CategoryAdmin(ModelView, model=Category):
    column_list = [Category.id, Category.name, Category.slug]
    form_columns = [Category.name, Category.slug]

class UserAdmin(ModelView, model=User):
    column_list = [User.id, User.username, User.email]
    column_searchable_list = [User.username, User.email]
    # Ne pas afficher le champ mot de passe (pour des raisons de sécurité)
    form_excluded_columns = [User.hashed_password]

# Créer une instance Admin et la lier à l'application FastAPI
admin = Admin(app, engine, title=EXEMPLE Admin du blog)
admin.add_view(PostAdmin)
admin.add_view(CategoryAdmin)
admin.add_view(UserAdmin)

Visitez /admin pour accéder à l'interface d'administration.

sqladmin vs Flask-Admin vs Django Admin

FonctionnalitésDjango AdminFlask-Adminsqladmin
Méthode d'inscriptionadmin.site.register()admin.add_view(ModelView())Admin + add_view
Prise en charge de l'ORMORM DjangoSQLAlchemy / MongoEngineSQLAlchemy (uniquement)
Prise en charge de l'authentificationIntégréNécessite une intégration manuelleauthentication_backend intégré
Prise en charge de l'asynchroneNon pris en chargeNon pris en chargeasynchrone natif

sqladmin est accessible sans connexion par défaut. Il doit être configuré pour les environnements de production.authentication_backendPour ajouter une protection, consultez la documentation officielle de sqladmin.


Résumé du chapitre

Dans ce chapitre, vous avez implémenté deux fonctionnalités importantes : utiliser une table d'association Table pour implémenter la relation plusieurs-à-plusieurs des favoris ; et utiliser sqladmin pour mettre rapidement en place un système de gestion d'administration backend.

Le blog dispose désormais de fonctionnalités complètes d'interaction utilisateur (inscription, connexion, favoris) et de fonctions de gestion (backend d'administration).

autres extensions