Objectif Connaître les bases Django Olivier Pons / 2015
1 Django Installation Etapes qui vont suivre 1. Installation de l'environnement 2. Installation de Django 3. Création d'un projet vide 4. Création d'une application monblog 3 / 71
1 Django Installation Création de l'environnement virtuel mkdir monenv cd monenv python3 -m venv myvenv myvenv/bin/activate source bin/activate myvenv/bin/pip install --upgrade pip myvenv/bin/pip install django 4 / 71
1 Django Installation Création de l'environnement virtuel Python 2.x mkdir monenv cd monenv virtualenv myvenv source myvenv/bin/activate myvenv/bin/pip install requests myvenv/bin/pip install --upgrade pip myvenv/bin/pip install django 5 / 71
Création du projet > myvenv/bin/django-admin.py startproject monprojet. > tree monprojet monprojet/ init.py settings.py urls.py wsgi.py Editez monprojet/settings.py Et mettez le en français, avec TIMEZONE à Europe/Paris 6 / 71
Les fichiers de base monprojet/ init.py settings.py urls.py wsgi.py Dossier racine monprojet est un module configuration du site Web résolution des routes (urlresolver) Web Server Gateway Interface = serveur 7 / 71
Création du projet Le fichier manage.py est une sorte d'enveloppe de django-admin L'interface d'administration n'existe pas car elle est générée automatiquement! En attendant il faut demander à générer la base de données dont se servent les modules de base Puis générer une application. En général : un projet, qui contient plusieurs applications 8 / 71
Création de la base de données myvenv/bin/python3 manage.py migrate 9 / 71
Lancement du serveur myvenv/bin/python3 manage.py runserver 10 / 71
Nouvelle application >> myvenv/bin/python3 manage.py startapp monblog >> tree monblog/ monblog/ init.py admin.py migrations init.py models.py tests.py views.py 11 / 71
Nouvelle application Ajouter dans monprojet/settings.py l'application 'monblog' INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'monblog', ) 12 / 71
Technologies avancées Modèle exemple Mettre dans monblog/models.py : from django.db import models from django.utils import timezone class Post(models.Model): author = models.foreignkey('auth.user') title = models.charfield(max_length=200) text = models.textfield() created_date = models.datetimefield( default=timezone.now) published_date = models.datetimefield( blank=true, null=true) def publish(self): self.published_date = timezone.now() self.save() def str (self): return self.title 13 / 71
Modèle exemple >> myvenv/bin/python3 manage.py makemigrations monblog >> myvenv/bin/python3 manage.py migrate monblog 14 / 71
QuerySet >> myvenv/bin/python3 manage.py shell >> from monblog.models import Post >> from django.contrib.auth.models import User >> User.objects.all() >> User.objects.create(username='olivier') >> User.objects.all() >> User.objects.get(username='olivier') >> moi = User.objects.get(username='olivier') >> Post.objects.create( author = moi, title = 'Mon titre', text = 'Test') >> Post.objects.filter(author=moi) 15 / 71
QuerySet >> post = Post.objects.get(id=1) >> post.publish() >> Post.objects.filter(published_date isnull=false) >> Post.objects.order_by('created_date') >> Post.objects.order_by('-created_date') >> exit 16 / 71
Administration Créer un superadmin : myvenv/bin/python3 manage.py createsuperuser Mettre dans monblog/admin.py : from django.contrib import admin from.models import Post admin.site.register(post) Relancer le serveur myvenv/bin/python3 manage.py runserver Aller sur : http://127.0.0.1:8000/admin/ 17 / 71
18 / 71
Urls Ajouter l'url dans monprojet/urls.py : urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'', include('monblog.urls')), ] Créer le fichier en conséquence : monblog/urls.py from django.conf.urls import patterns, include, url from. import views urlpatterns = patterns('', url(r'^$', views.post_list), ) 19 / 71
Templating Ajouter l'url dans monblog/views.py : from django.shortcuts import render def post_list(request): return render(request, 'blog/post_list.html', {}) Créer les dossiers puis le fichier en conséquence : >>> mkdir monblog/templates >>> mkdir monblog/templates/blog >>> vim monblog/templates/blog/post_list.html 20 / 71
Comparaison Python / Php 21 / 71
Comparaison Python / Php Symfony : générer l'interface d'administration / Sonata - Installer le bundle - Rajouter / modifier le routing - Publier les assets - Décommenter le traducteur (multilangue) - Supprimer les routes inutiles - Générer toutes les classes d'admin. - Eventuellement, activer cmf_tree ( 3 actions en plus) http://symfony.com/doc/current/cmf/tutorial/sonata-admin.html Django : rien à faire. Si, si. Rien. 22 / 71
Templates / Données dynamiques Lancer python local : myvenv/bin/python3 Puis import os os.environ['django_settings_module']='monprojet.settings' from django.contrib.auth.models import User import monblog.models posts = monblog.models.post.objects\.filter(published_date isnull=false)\.order_by('published_date') posts 23 / 71
Templates / Données dynamiques Toutes les conditions possibles sont après : xxx.filter(nomchamp lte = xx) xxx.filter(nomchamp gt = xx) xxx.filter(nomchamp startwith = xx) xxx.filter(nomchamp endswith = xx) xxx.filter(nomchamp exact = xx) xxx.filter(nomchamp iexact = xx) xxx.filter(nomchamp contains = xx) xxx.get(id = xx) xxx.objects.all()[:5] 24 / 71
Templates / Données dynamiques Ajouter dans monblog/views.py : from django.shortcuts import render from.models import Post def post_list(request): posts = Post.objects\.filter(published_date isnull=false)\.order_by('published_date') return render( request, 'blog/post_list.html', {'posts': posts} ) Relancer le serveur Web 25 / 71
Templates / Données dynamiques Ajouter dans monblog/templates/blog/post_list.html : {% for post in posts %} <div> <p>publié le : {{ post.published_date }}</p> <h1><a href="">{{ post.title }}</a></h1> <p>{{ post.text linebreaks }}</p> </div> {% endfor %} Relancer le serveur Web 26 / 71
Templates / Données statiques Créez le dossier static à la racine puis css : mkdir static mkdir static/css Modifiez monprojet/settings.py : STATICFILES_DIRS = ( os.path.join(base_dir, "static"), ) Relancer le serveur Web 27 / 71
Templates / Données statiques Modifier première ligne de monblog/templates/blog/post_list.html : {% load staticfiles %} Puis dans le <head></head> ajouter : <link rel="stylesheet" href="{% static "css/blog.css" %}"> Relancer le serveur Web Intégrer totalement un boilerplate au choix (voir après) 28 / 71
Boilerplates Initializr http://www.initializr.com/ 99lime http://www.99lime.com/elements/ Alsacreation http://schnaps.it/ http://fortawesome.github.io/font-awesome/cheatsheet/ 29 / 71
Technologies avancées Templates / Héritage Fichier base.html contient toute la base avec des blocs vides : {% block content %} {% endblock %} Les fichiers qui héritent remplissent ces blocs : monblog/templates/blog/post_list.html : {% block content %} {% for post in posts %} {% endfor %} {% endblock content %} 30 / 71
Url + Template + QuerySet Editer monblog/urls.py y ajouter dans les patterns : url(r'^post/(?p<pk>[0-9]+)/$', views.post_detail), Editer monblog/views.py : from django.shortcuts import render, get_object_or_404 def post_detail(request, pk): post = get_object_or_404(post, pk=pk) return render( request, 'blog/post_detail.html', {'post': post} ) 31 / 71
Url + Template + QuerySet Editer monblog/templates/blog/post_detail.html : {% extends "blog/base.html" %} {% block content %} <div class="date"> {% if post.published_date %} Publié le : {{ post.published_date }} {% endif %} </div> <h1>{{ post.title }}</h1> <p>{{ post.text linebreaks }}</p> {% endblock %} 32 / 71
Formulaires Simples (pas liés à un modèle) Principe Déclarer une classe dérivée de forms.form dans forms.py Déclarer une vue de type FormView dans views.py Mettre la forme en visuel dans les templates (xxx.html) 33 / 71
forms.py Technologies avancées Formulaires Simples (pas liés à un modèle) class RegisterForm(forms.Form): username = forms.charfield() prenom = forms.charfield() etc. prenom = forms.charfield( label=u'entrez votre prénom', max_length=100) Ce ne sont que des définitions de champs = Fields 34 / 71
views.py Technologies avancées Formulaires Simples (pas liés à un modèle) class RegisterView(FormView): template_name = 'applancement/index.html' form_class = RegisterForm def form_valid(self, form): username = form.cleaned_data['username'] prenom = form.cleaned_data['prenom']... return HttpResponseRedirect(u'{0}{1}'.format( site_web, self.request.meta['path_info'] )) 35 / 71
Formulaires Simples (pas liés à un modèle) templates/index.html <form action="{% url 'register' %}" method="post"> {% csrf_token %} {% for field in form %} {{ field.label_tag }}<br />{{ field }}<br /> {% if field.errors %} {{ field.errors }} {% endif %} {{ field.help_text }} {% endfor %} {{ form.non_field_errors }} <input type="submit" value="register!"> </form> 36 / 71
Editer monblog/forms.py : from django import forms from.models import Post Technologies avancées Formulaires class PostForm(forms.ModelForm): formulaires Django notre modèle Classe dérivée class Meta: classe où définir : model = Post - la table fields = ('title', 'text',) - les champs 37 / 71
Formulaires Ajouter le lien vers le formulaire : <a href="{% url "blog.views.post_new" %}" class="top-menu"> <span class="glyphicon glyphicon-plus"></span> </a> 38 / 71
Authentification Autorisation Installation Droits Vérifie que l utilisateur est bien celui qu il prétend être Détermine ce qu un utilisateur authentifié est autorisé à faire Tout est déjà fait (settings.py) django.contrib.auth Authentification django.contrib.contenttypes Association modèles permissions 39 / 71
Décorateurs rom django.views.decorators.http import require_http_methods require_http_methods(["get", "POST"]) ef my_view(request): # arrivé ici = que GET ou POST! pass require_get(), require_post(), require_safe() rom django.contrib.auth.decorators import login_required login_required(login_url='/login/') ef my_view(request):... 40 / 71
Décorateurs rom django.contrib.auth.decorators import permission_required permission_required('polls.can_vote', login_url='/login/') ef my_view(request):... ans l'application polls : lass Poll(models.Model): def can_vote(self, user): return not self.vote_set.filter(user=user).exists() 41 / 71
Décorateurs from django.contrib.auth.decorators import user_passes_test def email_ok(user): return user.email.endswith('@gmail.com') @user_passes_test(email_ok) def my_view(request):... 42 / 71
ans urls.py Technologies avancées 2 Django Suppléments URLs paramétrées rlpatterns = [... rl(_(r'^vente-groupee/(?p<slug>[a-za-z0-9-_]+)/$'), p_views.detailview.as_view(), name='vente_groupee'),... ans un fichier de template : a href="{% url 'vente_groupee' monparam %}">Ici<a/> 43 / 71
2 Django Suppléments Sessions Dans settings.py vérifier que MIDDLEWARE_CLASSES contient django.contrib.sessions.middleware.sessionmiddlewarer Puis dans les vues : Le premier paramètre d'une vue est un objet HttpRequest Via cet objet, on accède à la propriété session requestion.session.['id']=654654 exemple = requestion.session.get('id') del request.session['id'] Fonctions les plus utilisées de session : keys(), items(), setdefault(), clear(), flush() https://docs.djangoproject.com/fr/1.8/topics/http/sessions/ 44 / 71
2 Django Suppléments Namespace Définition du namespace dans la route principale Ajouter dans urls.py urlpatterns = [ url(_(r'^monapp/'), include('monapp.urls', namespace="produits")), url(r'^admin/', include(admin.site.urls)), ] 45 / 71
2 Django Suppléments Namespace éfinition des routes dans l'application même : blog/urls.py rom django.conf.urls import include, url rom django.contrib import admin rom. import views rlpatterns = [ url(r'^$', views.indexview.as_view(), name='index'), 46 / 71
Technologies avancées 2 Django Suppléments Modèles : choix multiple rom django.db import models rom django.utils.translation import gettext_lazy as _ IFFICULTY = (('easy', _('Easy')), ('medium', _('Medium')), ('hard', _('Hard'))) lass Exercise(models.Model): name = models.charfield(max_length=300, unique=true) level = models.charfield( max_length=15, choices=difficulty) 47 / 71
Technologies avancées 2 Django Suppléments Modèles : base abstraite class BaseModel(models.Model): date_creation = models.datetimefield( auto_now_add=true ) date_last_modif = models.datetimefield( auto_now=true ) class Meta: abstract = True 48 / 71
2 Django Suppléments Modèles : exemple de filtre ttps://docs.djangoproject.com/fr/1.8/topics/db/queries/ Dans le modèle : class Produit(BaseModel): tags = models.manytomanyfield( Tag, related_name='produits' ) Dans la vue : Tag.objects.filter( produits in=produit.objects.all() ).distinct() 49 / 71
Technologies avancées 2 Django Suppléments Administration : ManyToMany class ProduitTagsInline(admin.TabularInline): model = Produit.tags.through extra = 0 class ProduitDescriptionsInline(admin.TabularInline): model = Produit.descriptions.through extra = 0 class ProduitAdmin(admin.ModelAdmin): inlines = [ProduitTagsInline, ProduitDescriptionsInline] exclude = ('descriptions', 'tags') 50 / 71
2 Django Suppléments Administration : ManyToMany 51 / 71
Technologies avancées 2 Django Suppléments Administration : même clé étrangère models.py class LangueTraduction(BaseModel): src = models.foreignkey('langue', related_name='langue_src') dst = models.foreignkey('langue', related_name='langue_dst') nom = models.charfield(max_length=50) admin.py class LangueTraductionsInline(admin.TabularInline): #! fk_name = obligatoire sinon pas visible : fk_name = 'src' model = LangueTraduction extra = 0 class LangueAdmin(admin.ModelAdmin): inlines = [LangueTraductionsInline] 52 / 71
2 Django Suppléments Administration : même clé étrangère 53 / 71
Traduction Au moment où la fonction est appelée from django.utils.translation import ugettext as _ Au moment où la chaîne est réellement utilisée from django.utils.translation import ugettext_lazy as _ Créer un dosser locale dans le projet Avec manage.py shell : makemessages -l fr makemessages -l en compilemessages 2 Django Suppléments 54 / 71
2 Django Suppléments Traduction Pour qu'il charge ces chaines : Dans settings.py, ajouter : LOCALE_PATHS = ( os.path.join(base_dir, 'locale'), ) 55 / 71
Champs de type ImageField Répertoire des fichiers téléversés : Dans settings.py, ajouter : MEDIA_ROOT = os.path.join(base_dir, 'uploads') Puis dans urls.py, ajouter : urlpatterns = [... url(r'^public/(?p<path>.*)$', 'django.views.static.serve', {'document_root': settings.media_root}), ] 2 Django Suppléments 56 / 71
Technologies avancées 2 Django Suppléments Lancer des commandes SQL en direct Exemple ici pour simuler le DESC [table] de mysql : Lancer python en ligne de commande from django.db import connection cursor = connection.cursor() cursor.execute("pragma table_info(ventegroupee)") for row in cursor: print(row) Pour la note : cursor est une classe iterable, donc on peut faire un for dessus 57 / 71
2 Django Suppléments Outils Django déjà en production Allez ici : https://github.com/divio/django-cms Installez le en suivant les instructions. 58 / 71
utre exemple models.py : Technologies avancées 3 Django Jeu de rôle Modèle exemple rom django.db import models rom django.utils import timezone rom django.utils.translation import ugettext_lazy as _ lass CharacterClass(models.Model): name = models.charfield(_(u'nom de la Classe'),max_length=80) playable = models.booleanfield(_(u'le joueur peut le choisir')) def str (self): return self.name 59 / 71
3 Django Jeu de rôle Modèle exemple Types existants : CharField BooleanField EmailField PositiveIntegerField TextField ForeignKey OneToOneField ManyToManyField 60 / 71
3 Django Jeu de rôle Modèle exemple class CharacterManager(models.Manager): def get_query_set(self): return super(charactermanager, self)\.get_query_set()\.filter(playable = True) class CharacterClass(models.Model):... objects = models.manager() # Manager par défaut playable_character = CharacterManager() # Playable Manager playable = CharacterClass.objects.filter(Playable = True) playable = CharacterClass.playable_character.all() 61 / 71
monblog/urls.py Technologies avancées 3 Django Jeu de rôle Vues génériques on peut y ajouter directement : from django.conf.urls import url from django.views.generic import TemplateView urlpatterns = [ url(r'^apropos/$', TemplateView.as_view( template_name='apropos.html' )) ] 62 / 71
3 Django Jeu de rôle Vues génériques : dérivation # monapp/views.py from django.views.generic import TemplateView class AproposView(TemplateView): template_name = "apropos.html" # urls.py from django.conf.urls import url from monapp.views import AproposView urlpatterns = [ url(r'^apropos/', AproposView.as_view()), ] 63 / 71
3 Django Jeu de rôle Vues génériques : listes class IndexView(generic.ListView): template_name = 'produits/index.html' context_object_name = 'liste_produits' def get_queryset(self): """ Dites ce que cela renvoie : """ return Produit.objects.order_by( '-date_v_fin', '-date_v_debut' )[:5] 64 / 71
3 Django Jeu de rôle Vues génériques : détail from django.views.generic.detail import DetailView from django.utils import timezone from.models import Produit class MaDetailView(DetailView): template_name = 'produits/detail.html' model = Produit def get_context_data(self, **kwargs): context = super(madetailview, self).get_context_data(**kwargs) context['now'] = timezone.now() return context 65 / 71
Technologies avancées 3 Django Sujet à faire Choisissez un site à faire parmi les suivants Réservation de places de concert Médiathèque Blog multi-utilisateurs multi rôles Vente de services (développement, formation) Rencontre entre professionnels ( à la LinkedIn ) SAAS de comptabilité basique Création communautaire de livres Présentation et statistiques détaillées d'un MMORPG Crowdfunding ( à la kickstarter / indiegogo ) Site d'apprentissage en ligne Réservation de tables d'un restaurant 66 / 71
4 Structure classique Varnish Configuration de varnish : /etc/varnish/default.vcl : backend apache {.host = "127.0.0.1";.port = "8080"; } backend nodejs {.host = "127.0.0.1";.port = "3000"; }... 67 / 71
4 Structure classique Varnish... sub vcl_recv { if (req.url ~ "(?i)\.(jpeg jpg... html htm)$") { unset req.http.cookie; } if ( (req.http.host ~ "olivierpons\.(.*)") (req.http.host ~ "krystallopolis\.(.*)") ) { set req.backend_hint = apache; set req.http.x-server = req.http.host; }... 68 / 71
... }... Technologies avancées 4 Structure classique Varnish # Surcharge pour NodeJS if ( (req.http.host ~ "node\.wogwog\.(.*)") ) { set req.backend_hint = nodejs; set req.http.x-server = req.http.host; # "pass" = passer direct sans cache possible return(pass); } 69 / 71
4 Structure classique Varnish... sub vcl_deliver { set resp.http.server = "WTF Server"; # Supprimer toutes les signatures : unset resp.http.via; unset resp.http.x-powered-by; unset resp.http.x-varnish; } 70 / 71
Apache httpd.conf Listen 8080 Technologies avancées 4 Structure classique Apache / NodeJS / Python NodeJS bin/www var port = normalizeport(process.env.port '3000'); Python / Django python manage.py runserver 8080 71 / 71