ktongue/docker_container / bookshop /django_course.md
|
download
raw
14 kB

Cours Complet sur Django

1. Introduction

Django est un framework web Python de haut niveau qui encourage le développement rapide et une architecture propre. Il suit le pattern MTV (Model-Template-View).

Caractéristiques principales :

  • Plein de fonctionnalités : ORM, authentification, admin automatique, formulaires
  • Sécurité intégrée : Protection CSRF, XSS, SQL injection
  • Scalable : Utilisé par Instagram, Pinterest, Mozilla
  • Communauté large : Plugins et documentation abondants

2. Installation

# Créer un environnement virtuel
python -m venv env
source env/bin/activate  # Linux/Mac
# ou
env\Scripts\activate  # Windows

# Installer Django
pip install django

# Vérifier l'installation
django-admin --version

3. Créer un Projet

# Créer le projet
django-admin startproject monprojet
cd monprojet

# Lancer le serveur de développement
python manage.py runserver

# Créer une application
python manage.py startapp blog

4. Structure du Projet

monprojet/
├── manage.py
├── monprojet/
│   ├── __init__.py
│   ├── settings.py      # Configuration du projet
│   ├── urls.py          # Routes principales
│   ├── asgi.py
│   └── wsgi.py
└── blog/
    ├── __init__.py
    ├── admin.py         # Interface admin
    ├── apps.py
    ├── models.py        # Modèles de données
    ├── views.py         # Vues/Contrôleurs
    ├── urls.py          # Routes de l'app
    └── migrations/

5. Configuration (settings.py)

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',  # Votre application
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# Langue et timezone
LANGUAGE_CODE = 'fr'
TIME_ZONE = 'Europe/Paris'
USE_I18N = True
USE_TZ = True

# Fichiers statiques
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

6. Les Modèles (Models)

Les modèles définissent la structure de vos données.

# blog/models.py
from django.db import models
from django.contrib.auth.models import User

class Categorie(models.Model):
    nom = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    
    class Meta:
        verbose_name_plural = "atégories"
    
    def __str__(self):
        return self.nom

class Article(models.Model):
    titre = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    contenu = models.TextField()
    date_creation = models.DateTimeField(auto_now_add=True)
    date_modification = models.DateTimeField(auto_now=True)
    categorie = models.ForeignKey(
        Categorie, 
        on_delete=models.CASCADE,
        related_name='articles'
    )
    auteur = models.ForeignKey(
        User, 
        on_delete=models.CASCADE
    )
    image = models.ImageField(upload_to='articles/', blank=True)
    published = models.BooleanField(default=False)
    
    class Meta:
        ordering = ['-date_creation']
    
    def __str__(self):
        return self.titre

Commandes pour les modèles :

# Créer les migrations
python manage.py makemigrations

# Appliquer les migrations
python manage.py migrate

# Voir le SQL généré
python manage.py sqlmigrate blog 0001

7. L'Interface Admin

# blog/admin.py
from django.contrib import admin
from .models import Article, Categorie

@admin.register(Categorie)
class CategorieAdmin(admin.ModelAdmin):
    list_display = ['nom', 'slug']
    prepopulated_fields = {'slug': ('nom',)}

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['titre', 'auteur', 'categorie', 'date_creation', 'published']
    list_filter = ['categorie', 'published', 'date_creation']
    search_fields = ['titre', 'contenu']
    prepopulated_fields = {'slug': ('titre',)}
    date_hierarchy = 'date_creation'
    ordering = ['-date_creation']
    
    # Afficher l'auteur automatiquement
    def save_model(self, request, obj, form, change):
        if not obj.pk:
            obj.auteur = request.user
        super().save_model(request, obj, form, change)

Créer un superutilisateur :

python manage.py createsuperuser

8. Les Vues (Views)

# blog/views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from .models import Article, Categorie
from django.views.generic import ListView, DetailView

# Vue fonctionnelle
def liste_articles(request):
    articles = Article.objects.filter(published=True)
    return render(request, 'blog/liste.html', {'articles': articles})

# Vue générique CBV
class ArticleListView(ListView):
    model = Article
    template_name = 'blog/liste.html'
    context_object_name = 'articles'
    paginate_by = 10
    
    def get_queryset(self):
        return Article.objects.filter(published=True)

class ArticleDetailView(DetailView):
    model = Article
    template_name = 'blog/detail.html'
    context_object_name = 'article'
    
    def get_queryset(self):
        return Article.objects.filter(published=True)

9. Les Templates

<!-- blog/templates/blog/base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Mon Blog{% endblock %}</title>
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
    <header>
        <nav>
            <a href="{% url 'blog:accueil' %}">Accueil</a>
            {% for cat in categories %}
                <a href="{% url 'blog:categorie' cat.slug %}">{{ cat.nom }}</a>
            {% endfor %}
        </nav>
    </header>
    
    <main>
        {% block content %}{% endblock %}
    </main>
    
    <footer>
        <p>&copy; 2024 Mon Blog</p>
    </footer>
</body>
</html>

<!-- blog/templates/blog/liste.html -->
{% extends 'blog/base.html' %}

{% block content %}
<h1>Articles</h1>

{% for article in articles %}
    <article>
        <h2><a href="{% url 'blog:detail' article.slug %}">{{ article.titre }}</a></h2>
        <p>Par {{ article.auteur.username }} le {{ article.date_creation|date:"d/m/Y" }}</p>
        <p>Catégorie : {{ article.categorie.nom }}</p>
        <p>{{ article.contenu|truncatewords:50 }}</p>
    </article>
{% empty %}
    <p>Aucun article pour le moment.</p>
{% endfor %}

{% include 'blog/pagination.html' with page_obj=page_obj %}
{% endblock %}

10. Les URLs

# monprojet/urls.py (URLs principales)
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls', namespace='blog')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

# blog/urls.py (URLs de l'application)
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.ArticleListView.as_view(), name='accueil'),
    path('article/<slug:slug>/', views.ArticleDetailView.as_view(), name='detail'),
    path('categorie/<slug:slug>/', views.ArticleByCategoryView.as_view(), name='categorie'),
]

11. Les Formulaires

# blog/forms.py
from django import forms
from .models import Article, Categorie

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['titre', 'categorie', 'contenu', 'image', 'published']
        widgets = {
            'contenu': forms.Textarea(attrs={'rows': 10}),
        }

# Utilisation dans une vue
from .forms import ArticleForm

def creer_article(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST, request.FILES)
        if form.is_valid():
            article = form.save(commit=False)
            article.auteur = request.user
            article.save()
            return HttpResponseRedirect(reverse('blog:detail', args=[article.slug]))
    else:
        form = ArticleForm()
    return render(request, 'blog/creer.html', {'form': form})

12. Système d'Authentification

# Vues de login/logout
from django.contrib.auth import views as auth_views
from django.urls import path

urlpatterns = [
    path('login/', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(next_page='blog:accueil'), name='logout'),
]

# Restreindre l'accès aux vues
from django.contrib.auth.decorators import login_required

@login_required
def espace_prive(request):
    return render(request, 'blog/prive.html')

# Modèle d'utilisateur personnalisé
from django.contrib.auth.models import AbstractUser

class Utilisateur(AbstractUser):
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to='avatars/', blank=True)
    
    class Meta:
        verbose_name = 'Utilisateur'

13. Context Processors et Template Tags

# blog/templatetags/blog_extras.py
from django import template
from .models import Categorie

register = template.Library()

@register.inclusion_tag('blog/categories_sidebar.html')
def categories_sidebar():
    return {'categories': Categorie.objects.all()}

@register.simple_tag
def get_popular_articles(count=5):
    return Article.objects.filter(published=True).order_by('-views')[:count]

# Dans settings.py
TEMPLATES = [
    {
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'blog.context_processors.categories_processor',
            ],
        },
    },
]

# blog/context_processors.py
from .models import Categorie

def categories_processor(request):
    return {'categories': Categorie.objects.all()}

14. Commandes Personnalisées

# blog/management/commands/publier_articles.py
from django.core.management.base import BaseCommand
from blog.models import Article
from django.utils import timezone
from datetime import timedelta

class Command(BaseCommand):
    help = 'Publie les articles programmés'
    
    def add_arguments(self, parser):
        parser.add_argument('--days', type=int, default=1)
    
    def handle(self, *args, **options):
        days = options['days']
        date_limit = timezone.now() - timedelta(days=days)
        
        articles = Article.objects.filter(
            published=False,
            date_publication__lte=date_limit
        )
        
        count = articles.update(published=True)
        self.stdout.write(f'{count} articles publiés')

15. Tests

# blog/tests.py
from django.test import TestCase, Client
from django.urls import reverse
from .models import Article, Categorie
from django.contrib.auth.models import User

class ArticleModelTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        self.categorie = Categorie.objects.create(nom='Test', slug='test')
    
    def test_article_creation(self):
        article = Article.objects.create(
            titre='Article de test',
            contenu='Contenu de test',
            auteur=self.user,
            categorie=self.categorie,
            published=True
        )
        self.assertEqual(article.__str__(), 'Article de test')
        self.assertTrue(article.published)
    
    def test_liste_view(self):
        response = self.client.get(reverse('blog:accueil'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Article de test')

16. Déploiement avec Gunicorn + Nginx

# Installation
pip install gunicorn

# Test du serveur
gunicorn --workers 3 --bind unix:/tmp/monprojet.sock monprojet.wsgi:application

# Fichier service systemd
# /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/monprojet
ExecStart=/path/to/env/bin/gunicorn \
    --workers 3 \
    --bind unix:/tmp/monprojet.sock \
    monprojet.wsgi:application

[Install]
WantedBy=multi-user.target
# /etc/nginx/sites-available/monprojet
server {
    listen 80;
    server_name monprojet.com;
    
    location = /favicon.ico { access_log off; log_not_found off; }
    
    location /static/ {
        alias /path/to/monprojet/static/;
    }
    
    location /media/ {
        alias /path/to/monprojet/media/;
    }
    
    location / {
        include proxy_params;
        proxy_pass http://unix:/tmp/monprojet.sock;
    }
}

17. Commandes Utiles

# Développement
python manage.py runserver
python manage.py runserver 0.0.0.0:8000

# Base de données
python manage.py dbshell
python manage.py showmigrations
python manage.py migrate --fake

# Données de test
python manage.py shell
>>> from blog.models import Article
>>> Article.objects.all().count()

# Collecte des fichiers statiques
python manage.py collectstatic

# Vérification du projet
python manage.py check --deploy

18. Ressources

  • Documentation officielle : docs.djangoproject.com
  • Tutoriel officiel : docs.djangoproject.com/en/stable/intro/
  • Django Packages : djangopackages.org
  • Forum Django : forum.djangoproject.org
  • Awesome Django : github.com/wsv749/awesome-django

Ce cours couvre les bases essentielles de Django. Pour approfondir, explorez les topics avancés comme les signals, le caching, les tests, et les APIs REST avec Django REST Framework.

Xet Storage Details

Size:
14 kB
·
Xet hash:
d834b2c1e27ddfbd176f130cbecf88b2c0fa2adf2335ff3d3688f3ef6be01496

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.