Introduzione al modello-View-Controller Pattern in Django

Il modello Model-View-Controller (MVC) è un progetto architettonico che ha rappresentato la prova del tempo per la costruzione di applicazioni web modulari, manutenbili e testabili. Mentre Django, il framework web Python di alto livello, utilizza la sua terminologia - Modelli, Visite e Modelli - i principi sottostanti si allineano strettamente con MVC.

Questo articolo esplora il modello MVC in profondità, mostrando come Django implementa ogni componente e come è possibile applicare ulteriori strati di astrazione per mantenere il codice base pulito e scalabile.

Decodifica la Triade MVC a Django

Il modello MVC separa un'applicazione in tre componenti interconnessi:

  • Modello[] – gestisce i dati e la logica aziendale.
  • Visualizza] — gestisce la logica di presentazione e l'interfaccia utente.
  • Controller[] — Elabora l'ingresso dell'utente e le coordinate tra Modello e Vista.

Django reinterpreta leggermente questi ruoli. La sua “model” corrisponde direttamente al modello MVC. La “Visualizza” in Django agisce come controller – riceve richieste HTTP, interroga il Modello e restituisce una risposta. Il “Template” serve come MVC View, rendering HTML (o altri formati di output) per il cliente. Questa mappatura è fondamentale per evitare confusione quando si parla di architettura di Django.

Layer modello di Django

Django fornisce un potente sistema di mappatura oggetti-relazionale (ORM) che consente di definire i modelli di dati come classi Python. Ogni modello mappa a una tabella di database, e le istanze corrispondono alle righe. Utilizzando i campi di modello di Django, è possibile definire colonne, relazioni (ForeignKey, ManyToManyField, OneToOneField), e vincoli.

Per mantenere i modelli puliti e concentrati, seguire queste pratiche:

  • Tenere la logica aziendale fuori dai modelli quando possibile[[] – I modelli dovrebbero principalmente definire la struttura e le relazioni dei dati, non algoritmi complessi o regole di validazione che abbracciano più entità.
  • Utilizzare i responsabili dei modelli e i queryset[] — Incapsulare le domande comuni in manager personalizzati, che mantiene le opinioni magra e promuove il riutilizzo.
  • I metodi di modello di leva per operazioni semplici[[] — I metodi come o il calcolo di una proprietà sono accettabili, ma evitano la logica pesante che interagisce con altri modelli.
  • ]Migrazioni di scrittura per i cambiamenti di schema[[ – Il sistema di migrazione di Django traccia modifiche e consente la distribuzione sicura in ambienti.

Django Visualizza come Controllers

Le viste di Django gestiscono il ruolo del controller: accettano una richiesta HTTP, effettuano operazioni necessarie (spesso interrogando il database attraverso i modelli), e restituiscono una risposta HTTP. Django supporta due modelli di vista principali: viste basate sulle funzioni (FBV) e viste basate sulla classe (CBV).

Le opinioni basate sulla frequenza[ sono semplici ed esplicite, sono ideali per i punti di estremità piccoli e singoli. Tuttavia, mentre la vostra applicazione cresce, potete trovare voi stessi il codice di ripetizione (ad esempio, la manipolazione delle impaginazioni, gli oggetti di elenco o la convalida della forma).

Le opinioni basate sulla classe[] forniscono un comportamento generico integrato per le attività comuni (ListView, DetailView, CreateView, ecc.) incoraggiano il riutilizzo del codice attraverso l'eredità e i mixins. Ad esempio, un ListView gestisce automaticamente la paginazione, la filtrazione delle queryset e il rendering del contesto.

Indipendentemente dal modello scelto, mantenere le viste sottili. Spingere la logica aziendale in moduli di servizio, forme o funzioni di utilità. Questo rende le viste più facile da testare e riduce la duplicazione.

Modelli come Visite

Il modello di Django rende HTML combinando il markup statico con variabili di contesto dinamico. I modelli formano lo strato di presentazione (il “Visualizza” in MVC classico). Il sistema di eredità di modello Django consente di creare uno scheletro di base e di estenderlo nei modelli di bambino, riducendo la ridondanza e semplificando i cambiamenti di layout.

Le migliori pratiche per i modelli includono:

  • Keep logic minimal[[] — I modelli dovrebbero contenere solo la logica di presentazione (che si occupa di liste, elementi di visualizzazione condizionale).
  • Utilizzare tag e filtri personalizzati[ — Per la formattazione complessa o componenti UI riutilizzabili, creare i propri tag o filtri piuttosto che mettere logica nel modello.
  • Organizzare modelli per app[[] — Posizionare modelli in directory denominate dopo l'app (ad esempio, ) per evitare collisioni namespace.

Creazione di un Codebase modulare con Django Apps

Ogni progetto Django può consistere in molteplici app autocontenute, ognuna responsabile di un dominio o di una caratteristica distintiva. Ad esempio, un sito di e-commerce potrebbe avere app per , , , e . Ogni applicazione racchiude i propri modelli, le proprie visualizzazioni, i propri file statici.

Per mantenere le applicazioni veramente modulari:

  • Definire confini chiari[[] — Un'applicazione dovrebbe avere una responsabilità unica e un accoppiamento minimo ad altre applicazioni.
  • Apps riutilizzabili[[] — Progettare le tue app in modo da poter essere estratte e riutilizzate in altri progetti, evitando impostazioni codificate o riferimenti diretti agli URL del progetto.
  • Usa dipendenza iniezione[[ — Quando un'applicazione ha bisogno di qualcosa da un'altra app, passarlo come parametro o utilizzare uno strato di servizio che può essere mocked in test.

Separare la logica aziendale: il livello di servizio

Uno dei modi più efficaci per migliorare la testabilità e la modularità è quello di introdurre uno strato di servizio. Un modulo di servizio contiene una logica aziendale pura, libera dalla gestione delle richieste/risposta specifiche di Django. Questa separazione consente di testare algoritmi critici senza creare un server web o un database (a meno che non sia necessario per la persistenza dei dati).

Ad esempio, invece di scrivere logica di elaborazione all'interno di una visione che gestisce un pagamento:

def process_payment(request):
 # ... fetch user, order
 # ... call Stripe API
 # ... update order status
 return redirect('success')

È possibile spostare l'interazione Stripe in un servizio:

# services/payment_service.py
class PaymentService:
 def process(self, user, order, payment_info):
 # Stripe API calls, business rules, etc.
 # Return result and possibly new order status
 pass

Poi la vista semplicemente chiama il servizio e gestisce la risposta, questo approccio lo rende semplice per unire la logica di pagamento, mocking API esterne e le chiamate di database.

Codice Testabile di scrittura in Django

La prova è un vantaggio diretto della separazione MVC pulita. Quando i componenti sono decoupled, è possibile testarli in isolamento. Il framework di prova integrato di Django (basato su Python ) fornisce strumenti per la creazione di test per modelli, visualizzazioni, modelli e forme. Inoltre, librerie di terze parti come offrono più sintassi concisa e potenti dispositivi.

Modelli di test unità

I test di modello dovrebbero verificare che la struttura dei dati e le regole aziendali (se non sono incluse nel modello) funzionino correttamente. Utilizzare la classe [ e creare istanze di modello all'interno dei metodi di test. Ad esempio, prova che il metodo di un modello restituisce la stringa prevista, o che un metodo di manager personalizzato filtri correttamente.

from django.test import TestCase
from .models import Product

class ProductModelTest(TestCase):
 def test_string_representation(self):
 product = Product(name='Test Product')
 self.assertEqual(str(product), product.name)

Per una logica più complessa del modello, prendere in considerazione l'utilizzo per sostituire le dipendenze esterne come l'invio di e-mail o le chiamate API di terze parti.

Visualizzazioni di prova (Controllers)

Il client di test di Django consente di simulare richieste HTTP ed esaminare le risposte, ideale per i test di integrazione che controllano l’instradamento, l’autenticazione e il rendering dei modelli. Tuttavia, per testare la logica all’interno delle viste, è necessario separare quella logica in servizi o altri moduli.

Quando si verificano le opinioni basate sulla classe, è possibile istantanare la classe di visualizzazione direttamente (o utilizzare il client di prova) e affermare sui dati di contesto, i codici di stato e reindirizzamenti.

Modelli di prova

La logica del modello è meglio testata indirettamente attraverso i test di visualizzazione che controllano l'HTML reso contiene elementi attesi. Per i tag di modello complessi o filtri, scrivi test unità dedicati. Le classi di Django e ti permettono di rendere una stringa di modello e di affermare sull'output.

from django.template import Template, Context
from django.test import TestCase

class CustomTagTest(TestCase):
 def test_uppercase_filter(self):
 t = Template('{% load my_filters %}{{ value|my_upper }}')
 c = Context({'value': 'hello'})
 self.assertEqual(t.render(c), 'HELLO')

Iniezione di depilazione e dipendenza

Per scrivere test di unità veloci e focalizzati, utilizzare [] per simulare servizi esterni, richieste di database, o anche ORM di Django. Ad esempio, quando si verifica un servizio che invia una e-mail, selezionare la funzione per evitare la consegna effettiva di posta elettronica.

L'iniezione della dipendenza è un'altra tecnica: passare tutte le dipendenze ( sessioni di base dati, API esterne, configurazione) esplicitamente alle funzioni o ai costruttori di classe, in modo da poter sostituire facilmente le implementazioni reali con i mock.

Migliori Pratiche per un Codice Clean Django

Oltre alla mappatura MVC, diverse tecniche aiutano a mantenere un'applicazione Django modulare e testabile:

  • Usare i segnali integrati di Django con parsimonia[[] – I segnali possono creare dipendenze nascoste e rendere più difficile il debugging.
  • Modificare gli URL[[] — Organizzare i modelli URL per app, utilizzare namespaces, e evitare di mettere la logica complessa in .
  • I middleware di leva per le preoccupazioni di taglio trasversale[[ — Autenticazione, registrazione e validazione della richiesta sono buoni candidati per middleware.
  • Documentazione di scrittura e suggerimenti di tipo[[] — Il codice ben documentato con annotazioni di tipo è più facile da capire e refactor.
  • Adotto di uno stile di codifica coerente[[ — Seguire PEP 8, utilizzare linters (flake8, pylint) e formatters (nero, isort).

Real-World Esempio: costruire un blog con MVC modulare in Django

Procediamo ad applicare i principi a una semplice applicazione del blog. Definiremo un modello , un servizio per la creazione di post con validazione, una visione basata sulla classe per elencare e creare post, e un modello con eredità.

Modello] [[]]]]

from django.db import models
from django.urls import reverse

class Post(models.Model):
 title = models.CharField(max_length=200)
 content = models.TextField()
 published = models.BooleanField(default=False)
 created_at = models.DateTimeField(auto_now_add=True)

 def get_absolute_url(self):
 return reverse('post_detail', args=[self.pk])

Servizio[] [[[]]]]]]

from .models import Post

class PostService:
 def create_post(self, title, content, user):
 # business logic: check permissions, validate content length, etc.
 if len(content) < 50:
 raise ValueError("Content must be at least 50 characters.")
 post = Post.objects.create(title=title, content=content, author=user)
 return post

] [[]]]]] [[[]]]]]]]

from django.views.generic import ListView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .services import PostService
from .models import Post

class PostListView(ListView):
 model = Post
 template_name = 'blog/post_list.html'
 queryset = Post.objects.filter(published=True)

class PostCreateView(LoginRequiredMixin, CreateView):
 model = Post
 fields = ['title', 'content']
 template_name = 'blog/post_form.html'

 def form_valid(self, form):
 # Use service to enforce additional rules
 service = PostService()
 try:
 post = service.create_post(
 title=form.cleaned_data['title'],
 content=form.cleaned_data['content'],
 user=self.request.user
 )
 except ValueError as e:
 form.add_error(None, str(e))
 return self.form_invalid(form)
 return super().form_valid(form)

Template [[]]]]

{% extends "base.html" %}
{% block content %}
 {% for post in post_list %}
 
 {% endfor %}
{% endblock %}

This structure keeps the view lean, the model focused, and the business logic isolated in a service that can be unit-tested easily.

Testare il servizio blog

Ecco un test unitario per il [] che utilizza oggetti mock per evitare i colpi di database (puoi anche usare il database di test di Django per l'integrazione):

from django.test import TestCase
from unittest.mock import patch, MagicMock
from .services import PostService

class PostServiceTest(TestCase):
 @patch('blog.services.Post.objects.create')
 def test_create_post_short_content_raises_error(self, mock_create):
 service = PostService()
 with self.assertRaises(ValueError):
 service.create_post("Title", "Short", mock_some_user)
 mock_create.assert_not_called()

Per ulteriori test approfonditi, utilizzare Django e il database reale per verificare che [] effettivamente salva il post e che il campo dell'autore è impostato correttamente.

Raccomandazioni esterne

Per approfondire la comprensione di MVC a Django e le relative pratiche di test, esplorare queste risorse:

Conclusioni

L’implementazione del modello MVC a Django non è di rigidità seguendo le definizioni dei libri di testo, ma di adottare l’idea principale di separazione delle preoccupazioni. Trattando i modelli di Django come rappresentazione dei dati, le viste come controller e i modelli come strato di presentazione, si costruisce naturalmente un’applicazione più modulare e testabile.

Una base di codice Django ben strutturata riduce il debito tecnico, semplifica l'accesso ai nuovi sviluppatori e garantisce che la tua applicazione possa evolversi senza interruzioni di fuga. Abbraccia lo spirito di MVC e le tue applicazioni Django saranno robuste, manutenbili e pronte per le sfide della produzione.