Maxime Arthaud basé sur les slides de V. Angladon et V. Duvert Jeudi 7 novembre 2013 http://www.bde.enseeiht.fr/clubs/net7/ supportformations/django/2013 Maxime Arthaud basé sur les slides de V. Angladon et V. Duvert Jeudi 7 novembre 2013 http://www.bde.enseeiht.fr/clubs/net7/ supportformations/django/2013
Bibliothèque =/= Framework : bibliothèque : collection de fonctions + classes spécialisées framework : ensemble de fonctions + classes + méthodologie. En général on ne peut pas changer de framework sans repartir à zéro.
Tout le monde sait ce qu est une base de donnée? BD = entité dans laquelle on peut stocker des données structurées selon un modèle prédéfini. Base = ensemble de tables. Table = tableau avec des colonnes prédéfinies (nom + type). permettent de faire des opérations complexes sur ces données : tri, sélection/filtrage à l aide d un langage,... permet accès concurrents, protection contre la corruption, récupération de données si requête interrompue, sécurité,... C est beaucoup mieux que de gérer des fichiers
CMS = gestionnaire de contenu (SPIP, Joomla, Drupal,...) site dynamique tout prêt (personnalisable) ou il suffit de rajouter du contenu via une interface d administration.
Attention Version actuelle : Django 1.6 (Python2 ou Python3) Utilisez Django 1.5 avec python 2 >> Architecture Attention Version actuelle : Django 1.6 (Python2 ou Python3)
Attention Version actuelle : Django 1.6 (Python2 ou Python3) Utilisez Django 1.5 avec python 2 >> Architecture Attention Version actuelle : Django 1.6 (Python2 ou Python3)
Architecture Architecture Architecture Requête URL Vue Modèle Template Réponse (HTML) Requête URL Vue Modèle Template Réponse (HTML) Fonctionnement de Django : slide le plus important de la formation. l utilisateur charge une page de votre serveur serveur HTTP a plusieurs instance de Django prêtes à traiter des requêtes Django créé un objet Requête représentant votre requête qui passe à travers divers couches d intergiciels modifiant éventuellement cet objet Maintenant j en viens à ce qui est important et figure sur le schéma Django parse l url et cherche la vue lui correspondant Toute l essences du site réside dans les vues qui peuvent récupérer des données de bases de données et effectuer un rendu HTML avec des templates éventuellement. La vue précédemment sélectionnée renvoie un objet RéponseHTTP Cet objet repasse à travers les couches d intergiciels. >> 1e projet
Créer un site Django Créer un site Django Créer un site Django $ django-admin.py startproject monsite $ tree monsite/ monsite +-- manage.py +-- monsite +-- init.py +-- settings.py +-- urls.py +-- wsgi.py $ cd monsite $ python2 manage.py runserver [...] $ django-admin.py startproject monsite $ tree monsite/ monsite +-- manage.py +-- monsite +-- init.py +-- settings.py +-- urls.py +-- wsgi.py $ cd monsite $ python2 manage.py runserver [...] >> 1e vue
Votre 1e vue! Votre 1e vue! Votre 1e vue! 1 Créez un fichier monsite/views.py 2 Copiez y le code suivant : # -*- coding: utf-8 -*- from django.http import HttpResponse def home(request): return HttpResponse("Hello") Vue renvoie un objet RéponseHTTP 1 Créez un fichier monsite/views.py 2 Copiez y le code suivant : Oui mais il faut dire quelque part qu il faut utiliser cette vue quand une url donnée est demandée. >> urls # -*- coding: utf-8 -*- from django.http import HttpResponse def home(request): return HttpResponse("Hello")
La gestion des URLs La gestion des URLs La gestion des URLs 1 Ouvrez le fichier monsite/urls.py 2 Modifiez le code pour avoir : from django.conf.urls.defaults import patterns, \ include, url urlpatterns = patterns(, url(r ^$, monsite.views.home ), ) 1 Ouvrez le fichier monsite/urls.py 2 Modifiez le code pour avoir : from django.conf.urls.defaults import patterns, \ include, url urlpatterns = patterns(, url(r ^$, monsite.views.home ), ) C est là qu on fait l aiguillage des urls (selon regexp matchée) vers une vue. Pas besoin de / a la fin des urls le r signifie que l on considere la chaine de l expression reguliere en mode "raw" ie \t n est pas interprete comme le caractere de tabulation mais comme \ suivi de t, permet d écrire \b au lieu de \\b (car \ doit être échappé en Python) M? signifie M est optionnel Formation ou ;. tout Django char sauf \n ; \w = mot alphanum La gestion des URLs M0,3 : on peut reconnaitre 0 ou 1 ou 2 ou 3 M consécutifs 1 Ouvrez le fichier monsite/urls.py 2 M3 : reconnaitre exactement 3 M consécutifs Modifiez le code pour avoir : M+ : 1 ou 2 ou... M consécutifs, M* : 0 ou 1 ou 2... urlpatterns = patterns(, La gestion des URLs from django.conf.urls.defaults import patterns, \ include, url ) url(r ^$, monsite.views.home ),
URLs et expressions régulières URLs et expressions régulières URLs et expressions régulières #... suite de l urlpattern dans urls.py (r ^annee/(\d{4})$, monsite.views.annee ), (r ^aff/(?p<texte>.+)/$, monsite.views.aff ), # vues associees dans views.py def annee(request, year): return HttpResponse("On est en "+year) def aff(request, texte): return HttpResponse("Bonjour "+texte) #... suite de l urlpattern dans urls.py (r ^annee/(\d{4})$, monsite.views.annee ), (r ^aff/(?p<texte>.+)/$, monsite.views.aff ), Si on continue comme ça : urls.py de 500 lignes. Donc découper projet en modules/apps >> 1e app # vues associees dans views.py def annee(request, year): return HttpResponse("On est en "+year) def aff(request, texte): return HttpResponse("Bonjour "+texte)
Première application Première application Première application $ python2 manage.py startapp monapp $ ls monapp init.py models.py tests.py views.py 1 ajouter l application dans le tuple INSTALLED_APPS de settings.py 2 bonne pratique : monapp/urls.py >> config des urls $ python2 manage.py startapp monapp $ ls monapp init.py models.py tests.py views.py 1 ajouter l application dans le tuple INSTALLED_APPS de settings.py 2 bonne pratique : monapp/urls.py
Découplage des URLs Découplage des URLs Découplage des URLs # Rajouter cet urlpattern dans monsite/urls.py url(r ^monapp/, include( monapp.urls )) # monapp/urls.py from django.conf.urls import patterns, include, url urlpatterns = patterns( monapp.views, url(r ^$, index ), url(r ^info$, info ), ) # Rajouter cet urlpattern dans monsite/urls.py url(r ^monapp/, include( monapp.urls )) # monapp/urls.py from django.conf.urls import patterns, include, url Le premier paramètre de patterns est un prefix pour les noms des vues. Pour le moment on sait afficher des chaines de caractère et gérer des urls. Avant de passer aux templates, regardons les modèles. >> modèle urlpatterns = patterns( monapp.views, url(r ^$, index ), url(r ^info$, info ), )
Création d un modèle Création d un modèle Création d un modèle 1 Configurer la base de donnée dans settings.py 2 Créer les modèles 3 Générer les tables à partir du modèle Page de référence sur la configuration des bases de données : https://docs.djangoproject.com/en/dev/ref/settings/#databases Page de référence sur les champs des modèles : https://docs.djangoproject.com/en/dev/ref/models/fields/ 1 Configurer la base de donnée dans settings.py 2 Créer les modèles 3 Générer les tables à partir du modèle Je saute la configuration de settings.py car bien commenté possibilité configuration plusieurs DB. Modèle = classe qui définie une table. les instances de cette classe sont des lignes de la table. La génération des tables est automatique. >> 1e modèle Page de référence sur la configuration des bases de données : https://docs.djangoproject.com/en/dev/ref/settings/#databases Page de référence sur les champs des modèles : https://docs.djangoproject.com/en/dev/ref/models/fields/
Premier modèle Premier modèle Premier modèle # monapp/models.py from django.db import models class Article(models.Model): nom = models.charfield(verbose_name="nom de l article"\, max_length=128) description = models.textfield(max_length=512) prix = models.decimalfield(decimal_places=2, max_digits=6) couleurs = models.textfield(default="blue") ecole = models.foreignkey(ecole, editable=false) def unicode (self): return self.nom # monapp/models.py from django.db import models class Article(models.Model): nom = models.charfield(verbose_name="nom de l article"\, max_length=128) description = models.textfield(max_length=512) prix = models.decimalfield(decimal_places=2, max_digits=6) couleurs = models.textfield(default="blue") ecole = models.foreignkey(ecole, editable=false) def unicode (self): return self.nom Tiré du code du portail (modifié) CharField, TextField, DecimalField, ForeignKey max_length, default verbose_name : nom utilisé pour le formulaire blank : peut être laissé vide/null ou pas editable : pas apparaître dans le formulaire généré, ni dans l interface d admin Méthode unicode () : gérer comment afficher l objet, ex dans un print(). Idem que str () mais renvoie une chaîne utf-8. Les chaînes des DB sont converties en unicode par Django. >> création de la db
Création de la base de donnée Création de la base de donnée Création de la base de donnée Création de la base : $ python2 manage.py syncdb Voir les commandes SQL exécutées : $ python2 manage.py sql monapp Visualiser une base de donnée sqlite : $ sqliteman mabase.sqlite Création de la base : >> 1e requete $ python2 manage.py syncdb Voir les commandes SQL exécutées : $ python2 manage.py sql monapp Visualiser une base de donnée sqlite : $ sqliteman mabase.sqlite
Premières requêtes Premières requêtes Premières requêtes $ python2 manage.py shell Manipuler les modèles : Modele.objects.all() Modele.objects.get() Modele.objects.filter() entree.delete() entree.save() Pages de référence : https://docs.djangoproject.com/en/1.3/topics/db/queries/ https://docs.djangoproject.com/en/1.3/ref/models/querysets/ $ python2 manage.py shell Manipuler les modèles : En va faire ça ensemble. Lançons un shell particulier qui va mettre en place l environnement. >> template Modele.objects.all() Modele.objects.get() Modele.objects.filter() entree.delete() entree.save() Pages de référence : https://docs.djangoproject.com/en/1.3/topics/db/queries/ https://docs.djangoproject.com/en/1.3/ref/models/querysets/
Template = page html liée à une vue 1 configurer TEMPLATE_DIRS dans settings.py 2 définir les variables affichées/utilisées par le template et les regrouper dans un dictionnaire 3 changer la vue pour quelle renvoie render(request, montemplate.html, dictionnaire) Pages de référence : https://docs.djangoproject.com/en/dev/ref/templates/api/ https://docs.djangoproject.com/en/dev/ref/templates/builtins/?f Template = page html liée à une vue >> exemple 1 configurer TEMPLATE_DIRS dans settings.py 2 définir les variables affichées/utilisées par le template et les regrouper dans un dictionnaire 3 changer la vue pour quelle renvoie render(request, montemplate.html, dictionnaire) Pages de référence : https://docs.djangoproject.com/en/dev/ref/templates/api/ https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs
Exemple d utilisation Exemple d utilisation Exemple d utilisation La vue from monapp.models import Article from django.shortcuts import render def index(request): c = { articles : Article.objects.all()} return render(request, index.html, c) Le template index.html <h1>liste des articles</h1> <ul> {% for a in articles %} <li>{{ a.nom }}</li> {% endfor %} </ul> La vue from monapp.models import Article from django.shortcuts import render def index(request): c = { articles : Article.objects.all()} return render(request, index.html, c) Le template index.html <h1>liste des articles</h1> <ul> {% for a in articles %} <li>{{ a.nom }}</li> {% endfor %} </ul> {{ }} : accès variables {% %} : commande de template Remarquez le for comme si on était en Python Notez la syntaxe objet.attribut (f.id), idem pour accès liste/tuple : f.0, f.1... balise url, if ou else possibilité d appliquer des fonctions (filtres) à des variables via >> blocs
Etendre des blocs Etendre des blocs Etendre des blocs Page principale, main.html <html lang="fr"> <head><title>{% block title %}{% endblock %}</title></head> <body> <h1>bienvenue!</h1> {% block content %}{% endblock %} </body> </html> Page affichage.html qui va étendre main.html {% extends "main.html" %} {% block title %}Le titre de ma page{% endblock %} {% block content %} Du contenu ici. {% endblock %} Page principale, main.html <html lang="fr"> <head><title>{% block title %}{% endblock %}</title></head> <body> <h1>bienvenue!</h1> {% block content %}{% endblock %} </body> </html> Page affichage.html qui va étendre main.html {% extends "main.html" %} {% block title %}Le titre de ma page{% endblock %} {% block content %} Du contenu ici. {% endblock %} le contenu de affichage.html va etre inséré dans main.html >> formulaires
A la main A la main A la main Définition du formulaire, forms.py from django import forms class InscriptionForm(ModelForm): login = forms.charfield(required=true) password = forms.charfield(label= Mot de passe, required=t code = forms.integerfield() Son template, edit.html <form action="" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="submit" /> </form> Définition du formulaire, forms.py from django import forms class InscriptionForm(ModelForm): login = forms.charfield(required=true) >> vue du formulaire password = forms.charfield(label= Mot de passe, required=true, widget=forms.passwordinput) code = forms.integerfield() Son template, edit.html <form action="" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="submit" /> </form> On peut utiliser request.get et request.post, mais ce n est pas propre. Form = Même chose que les modèles. form.as_table, form.as_ul,..
Vue d un formulaire Vue d un formulaire Vue d un formulaire from django.shortcuts import redirect def inscription(request): if request.method == POST : form = InscriptionForm(request.POST) if form.is_valid(): # Faire qqch avec les donnees.. return redirect( monapp.views.merci ) else: form = InscriptionForm() return render(request, inscription.html, { form : form}) form.cleaned_data est un dictionnaire avec la valeur des champs from django.shortcuts import redirect >> formulaire généré à partir d un modèle def inscription(request): if request.method == POST : form = InscriptionForm(request.POST) if form.is_valid(): # Faire qqch avec les donnees.. return redirect( monapp.views.merci ) else: form = InscriptionForm() return render(request, inscription.html, { form : form})
A partir d un modèle A partir d un modèle A partir d un modèle Formulaire, forms.py class ArticleForm(models.ModelForm): class Meta: model = Article fields = ( login, password, code ) Vue, views.py objet = get_object_or_404(article, pk=1) if request.method == POST : form = ArticleForm(request.POST, instance=objet) if form.is_valid(): form.save() return redirect(objet) else: form = ArticleForm(instance=objet) return render(request, edit.html, { form : form}) Formulaire, forms.py class ArticleForm(models.ModelForm): class Meta: model = Article fields = ( login, password, code ) Vue, views.py objet = get_object_or_404(article, pk=1) if request.method == POST : form = ArticleForm(request.POST, instance=objet) if form.is_valid(): form.save() return redirect(objet) else: form = ArticleForm(instance=objet) return render(request, edit.html, { form : form}) Formulaire généré automatiquement, customisable Dans le cas de l ajout, ne pas passer en paramètre un objet get_object_or_404 permet de retourner un objet, ou erreur 404 code-snippet, pas à retenir! >> admin
1 Décommenter les lignes concernant l interface d administration dans settings.py et urls.py 2 Mettre à jour les tables (syncdb) 3 Pour chaque application, indiquer les modèles pris en charge par l interface d administration via un fichier admin.py comme suit : from monapp.models import Modele1, Modele2 from django.contrib import admin admin.site.register(modele1) admin.site.register(modele2) 1 Décommenter les lignes concernant l interface d administration dans settings.py et urls.py 2 Mettre à jour les tables (syncdb) Générée automatiquement, customisable :) >> exo 3 Pour chaque application, indiquer les modèles pris en charge par l interface d administration via un fichier admin.py comme suit : from monapp.models import Modele1, Modele2 from django.contrib import admin admin.site.register(modele1) admin.site.register(modele2)
Exercice : FaceMash Exercice : FaceMash Exercice : FaceMash Cahier des charges, fonctions à implémenter : ajouter/éditer un utilisateur (Nom, adresse mail, photo) via un formulaire visualiser une liste des utilisateurs triée selon leur score visualiser le profil d un utilisateur enregistrer les votes : date, vote, ip du votant (on est méchant) afficher la liste des votes vote : afficher les photos de 2 utilisateurs choisis aléatoirement, le vote s effectue en cliquant sur l une des photos interface d administration Cahier des charges, fonctions à implémenter : ajouter/éditer un utilisateur (Nom, adresse mail, photo) via un formulaire visualiser une liste des utilisateurs triée selon leur score visualiser le profil d un utilisateur enregistrer les votes : date, vote, ip du votant (on est méchant) afficher la liste des votes vote : afficher les photos de 2 utilisateurs choisis aléatoirement, le vote s effectue en cliquant sur l une des photos interface d administration Vous pouvez partir si vous voulez, vous avez la correction. L intérêt de faire l exo ici est qu on peut vous aider... FaceMash consiste à... Une partie du code vous est donnée. environ 200 lignes de code Python Attention, settings.py à compléter! script init.sh pour peupler votre base de donnée