Django! Perfectionnistes à la bourre

Python African Tour Dakar - 06-10 juillet 2009

Agence Universitaire de la Francophonie

Auteurs:Ousmane Wilane <ousmane@wilane.org>
Version:$Id: django-course.rst 5 2009-07-06 06:15:01Z wilane $

Python African Tour?

PAT? (2)

Qui sommes nous:

DakarLUG

Ousmane Wilane

Je suis:

Ressources

Pré-requis

Introduction

Qu'est ce qu'un Framework WEB

Design Pattern MVC

Les parpaings de Django (1)

from django.db import models
class Facture(models.Model):
  numero = models.CharField(max_length=50)
  montant = models.FlotField()
  date_facture = models.DateField()

Les parpaings de Django (2)

from django.shortcuts import render_to_response
from models import Facture
def dernieres_factures(request):
  liste_factures = Facture.objects
                       .order_by('-pub_date')[:10]
  return render_to_response('dernieres_factures.html',
               {'listes_factures': liste_factures})

Les parpaings de Django (3)

from django.conf.urls.defaults import *
import views
urlpatterns = patterns('',
  (r'^dernieres/$', views.dernieres_factures),
)

Les parpaings de Django (4)

<html>
  <head><title>Factures</title></head>
  <body>
    <h1>Factures</h1>
    <ul>
    {% for facture in listes_factures %}
      <li>{{ facture.nummero }}</li>
    {% endfor %}
    </ul>
   </body>
 </html>

Installation et configuration

Pré-requis

Python (2.3-2.6)

Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41)
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more
information.
>>>

Téléchargement et installation (Dist)

Téléchargement et installation (à la main)

Téléchargement et installation (à la main) (2)

Téléchargement et installation (svn)

>>> import django
>>> django.VERSION
(1, 1, 0, ‘beta’, 1)

Choix du support de stockage (officiels)

Choix du support de stockage (extérieurs)

Installer le pilote Python d'une base supportée par une communauté externe

Création d'un nouveau projet

Démarrer un projet:

django-admin.py startproject monprojet

Échafaudage:

mysite/
  __init__.py
  manage.py
  settings.py
  urls.py

Exercice

  • Créer un projet
  • Démarrer le serveur WEB de développement python manage.py runserver
  • Démarrer le serveur WEB sur un port différent

Vues et urls Django simples

from django.http import HttpResponse
def salut(request):
  return HttpResponse("Bonjour PAT")

Fonctions de type vue

Odyssée de la requête

Classe HttpResponse (Anatomie)

class HttpRequest(object):
  """A basic HTTP request."""

  _encoding = None
  _upload_handlers = []

  def __init__(self):
      self.GET, self.POST, self.COOKIES, self.META,
                 self.FILES = {}, {}, {}, {}, {}
      self.path = ''
   ...

Objet HttpRequest (Anatomie)

class HttpResponse(object):
  """A basic HTTP response, with content and
             dictionary-accessed headers."""
  status_code = 200
  def __init__(self, content='', mimetype=None, status=None,
          content_type=None):
      from django.conf import settings
      self._charset = settings.DEFAULT_CHARSET
  ...

Object QueryDict (Anatomie)

class QueryDict(MultiValueDict):
  _mutable = True
  _encoding = None

  def __init__(self, query_string, mutable=False, encoding=None):
      MultiValueDict.__init__(self)
      if not encoding:
          from django.conf import settings
          encoding = settings.DEFAULT_CHARSET
  ...

Exercice

Créer une vue qui affiche la date et l'heure: Contenu dynamique.

Exercice (2)

Vue:

from django.http import HttpResponse
import datetime
def date_actuelle(request):
  nunc = datetime.datetime.now()
  html = "<html><body>Hey PAT! Il est  %s.</body></html>" % nunc
  return HttpResponse(html)

url:

urlpatterns = patterns('',
  ('^ladate/$', date_actuelle),
)

Exercice (3)

Exercice (4)

(r'^ladate/plus/\d{1,2}/$', dans_x_heures)

def dans_x_heures(request, offset):
  try:
      offset = int(offset)
  except ValueError:
      raise Http404()
  dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
  assert False
  html = "<html><body>Dans %s heure(s), il sera %s.</body></html>" % (offset, dt)
  return HttpResponse(html)

Routage d'URLs

URLconf

Expressions régulières

Expressions régulières (2)

Symbole Correspondance
. (point) N'importe quel caractère
d Un chiffre
[A-Z] Un caractère entre A et Z
[a-z] Un caractère entre a et z
[aA-zZ] Un caractère entre a et z (insensible à la casse)

Expressions régulières (3)

Symbole Correspondance
+ Un ou plusieurs occurrences de l'expression précédente
[^/]+ Un ou plusieurs caractères jusqu'au prochain / (exclu)
? 0 ou une occurrence de l'expression précédente
* 0 ou plusieurs occurrence de l'expression précédente
{1,n} Entre et n occurrence de l'expression précédente

Exemple d'URLconf simple

(r'^facture/list/$', 'views.facturelist'),
(r'^facture/new/$', 'views.add_a_facture'),
(r'^facture/new/(?P<extra>\d+)$', 'views.add_x_facture'),
(r'^facture/edit/(?P<facture_id>\d+)$', 'views.editer'),

Utilisation de plusieurs URLconfs

from django.conf.urls.defaults import patterns, include
urlpatterns = patterns('')
urlpatterns += patterns('',
  (r'^$', views.homepage),
  (r'^search/$', views.search),
  (r'^news/$', views.news))
urlpatterns += patterns('',
  (r'^comptess/', include('comptes.urls')))

Passage d'arguments aux URLs

Templates Django

Utilisation de chaîne codée en dur dans les vues précédant, ce qui implique:

Les bases des templates

Exemple de template

Exemple de template(2)

<html>
<head><title>Avis de commande</title></head>
<body>
<h1>Avis de commande</h1>
<p>Bonjour {{ nom_complet }},</p>
<p>Nous vous remercions pour la commande passée par {{ company }}.
Vous serez livré le {{ date_livraison|date:"D d M Y" }}.</p>
<p>Rappel des éléments commandés:</p>
<ul>
{% for element in liste_elements %}
    <li>{{ element }}</li>
{% endfor %}

Exemple de template(3)

</ul>
{% if garantie %}
  <p>Les informations relatives à la garantie seront
                           inclus dans le colis.</p>
{% else %}
  <p>Vous n'avez pas souscris à une garantie.</p>
{% endif %}
<p>Cordialement,<br />{{ company }}</p>
</body>
</html>

Utilisation d'une template

La mise en oeuvre d'une template est très simple:

Exemple

Utilisez le shell du projet (où définir DJANGO_SETTINGS_MODULE à la main sans .py)

>>> from django.template import Template, Context
>>> t=Template('Je suis le {{ nom_complet }}.')
>>> c = Context({'nom_complet': 'Python African Tour'})
>>> t.render(c)
u'Je suis le Python African Tour.'

Objets Template

>>> print t
<django.template.Template object at 0xb7ea7e2c>
>>>
>>> id(t)
3085598252L
>>> int(0xb7ea7e2c)
3085598252L

Exception TemplateSyntaxError

Objets Context

Exercice

En console chargez la template définit ci-dessous (faire référence à la template Avis de commane) et passez lui le contexte adéquat et ensuite effectuer le rendu.

Résolution de variables dans une template

Ordre de résolution

Lorsque le système de template rencontre un . il procède à sa résolution dans l'ordre suivant:

Gestion de variable indéfini

Les Tags

Les tags permettent de donner des ordres au système de template

if/else

Les opérateurs not, or et and peuvent être utilisés (sans parenthèses pour le contrôle de l'ordre)

{% if on_est_le_weekend %}
  <p>Génial c'est le Weekend!</p>
{% endif %}
{% if on_est_le_weekend %}
  <p>Génial c'est le Weekend!</p>
{% else %}
  <p>Bon, il va falloir travailler.</p>
{% endif %}

if/else (2)

Sont considérés comme fausse par Python comme par le système de template les objets suivants dans un contexte booléen:

for

Il permet d'itérer parmi les éléments d'une séquence

<ul>
{% for facture in listes_factures %}
  <li>{{ facture.numero }}</li>
{% endfor %}
</ul>

for (2)

Un motif couramment utilisé est la vérification de la taille de la séquence avant de démarrer la boucle:

{% if listes_factures %}
  {% for facture in listes_factures %}
      <p>{{ facture.numero }}</p>
  {% endfor %}
{% else %}
  <p>Aucune facture.</p>
{% endif %}

Exercice

Définissez une liste de facture (liste de dictionnaire) et utiliser la en console pour le rendu de la template ci-dessus.

for (3)

Parce que le motif précédant est courant, un tag empty a été introduit:

{% for facture in listes_factures %}
  <p>{{ facture.numero }}</p>
{% empty %}
  <p>Aucune facture.</p>
{% endfor %}

for (3)

Exercice

Utilisez {{ forloop.last }} pour séparer une liste de mots par des virgules et finir la liste par un point.

ifequal/ifnotequal

{% ifequal type_facture 'pro-forma' %}
  <h1>Factures Pro-forma</h1>
{% else %}
  <h1>Facture</h1>
{% endifequal %}

ifequal/ifnotequal (syntaxe invalide)

{% ifequal variable True %}
{% ifequal variable [1, 2, 3] %}
{% ifequal variable {'cle': 'valeur'} %}

commentaires

Le système de template accepte permet les commentaire exprimés au choix sous les formes suivantes:

{# Je suis un commentaire et je peut pas passer à la ligne #}
{% comment %}
Je suis un commentaire
multi-ligne .
{% endcomment %}

Les filtres

Les filtres permettent d'altérer les valeurs des objets avant de les afficher. Il utilisent le caractère |

Utilisation des templates dans une vue

from django.http import HttpResponse
import datetime
def date_actuelle(request):
  nunc = datetime.datetime.now()
  html = "<html><body>Hey PAT! Il est  %s.</body></html>" % nunc
  return HttpResponse(html)

Utilisation des templates dans une vue (2)

from django.http import HttpResponse
from django.template import Template, Context
import datetime
def date_actuelle(request):
  t = Template("<html><body>Hey PAT! Il est
                            {{ nunc }}.</body></html>")
  html = t.render(Context({'nunc': datetime.datetime.now()}))
  return HttpResponse(html)

Utilisation des templates dans une vue (3)

L'approche de la page précédente ne règle pas les problème évoqués liés à cette approche.

Exercice

Sauvegarder le code html dans le répertoire de votre projet sous un répertoire appelés templates et utilisez open pour accéder au contenu.

Exercice (2)

def date_actuelle(request):
  now = datetime.datetime.now()
  fp = open('/home/wilane/pat-sn/monprojet/templates/heure.html')
  t = Template(fp.read())
  fp.close()
  html = t.render(Context({'nunc': now}))
  return HttpResponse(html)

Utilisation des templates dans une vue (4)

Chargement de templates

Le fichier settings.py contient une directive indiquant une série de répertoires contenant des templates:

TEMPLATE_DIRS = (
  # Put strings here, like "/home/html/django_templates" or
  # "C:/www/django/templates".
  # Always use forward slashes, even on Windows.
  # Don't forget to use absolute paths, not relative paths.
)
TEMPLATE_DIRS = (
  '/home/wilane/pat-sn/monprojet/templates',
)

Chargement de templates (2)

os.path.join(os.path.dirname(__file__), 'templates')
                                  .replace('\\','/'),

Chargement de templates (3)

from django.http import HttpResponse
import datetime
from django.template import Context
from django.template.loader import get_template
def date_actuelle_templatedir(request):
  now = datetime.datetime.now()
  t = get_template('heure.html')
  html = t.render(Context({'nunc': now}))
  return HttpResponse(html)

Exercice

Faite référence à une template qui n'existe pas avec get_template et commentez l'erreur.

Chargement de templates (3)

Pour éviter de répéter le cycle chargement de template, rendu contextuel et renvoie de HttpResponse on utilise render_to_response sous django.shortcuts:

from django.shortcuts import render_to_response
import datetime
def date_actuelle_rtr(request):
  now = datetime.datetime.now()
  return render_to_response('heure.html', {'nunc': now})

Exercice

1- Utilisez un sous répertoire sous templates et vérifiez que render_to_response() peut accéder aux fichiers dans le sous-répertoire. 2- Utilisez la fonction locals() pour encore condenser le code ci-dessus.

Tag include

La tag {% tag %} peut être utilisée pour inclure une template dans une autre en utilisant la même méthode de résolution:

{% include 'nav.html' %}
{% include "nav.html" %}
{% include nom_template %}

Héritage de templates

Exemple (base.html)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="fr">
<head>
  <title>{% block titre %}{% endblock %}</title>
</head>
<body>
  <h1>Mon site horloge</h1>
  {% block contenu %}{% endblock %}
  {% block pied_de_page %}
  <hr>
  <p>Merci de votre visite.</p>
  {% endblock %}
</body>
</html>

Exemple (fille heure_extends.html)

{% extends "base.html" %}
{% block titre %}L'heure actuelle{% endblock %}
{% block contenu %}
<p>Il est  {{ nunc }}.</p>
{% endblock %}

Exercice

Modèle Base de Données

Accès DB à l'ancienne

from django.shortcuts import render_to_response
import sqlite3
def liste_factures(request):
  cx=sqlite3.connect('/tmp/MaBase.pasdesoucis')
  cursor = cx.cursor()
  cursor.execute('SELECT numero FROM facture
                                ORDER BY date_facture')
  liste_facture = [ligne[0] for ligne in cursor.fetchall()]
  cx.close()
  return render_to_response('list_factures.html',
                         {'liste_factures': liste_factures})

Accès DB à l'ancienne (2)

Cette approche pose un nombre de problèmes:

Accès DB à l'ancienne (3)

La couche base de données Django est une tentative de résolution de ces problèmes entre autre:

Exemple:

def liste_factures(request):
  liste_factures = Facture.objects..order_by('date_facture')
  return render_to_response('list_factures.html',
                         {'liste_factures': liste_factures})

Configuration d'accès à une base de données

Les paramètres de configuration de la couche d'accès à la base de données sont généralement stockés dans le fichier settings.py où on a définit TEMPLATE_DIR:

DATABASE_ENGINE = ''
DATABASE_NAME = ''
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''

DATABASE_ENGINE

Configuration Base de données Pilote
postgresql PostgreSQL psycopg2
mysql MySQL MySQLdb
sqlite3 SQLite aucun pour Python >= 2.5
oracle Oracle cx_oracle

Exercice

Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41)
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for
more information.
>>> from django.db import connection
>>> cursor=connection.cursor()
>>>

Concept d'application Django

Création d'une application

Création d'une application (2)

Dans le répertoire monprojet exécutons la commande suivante:

python manage.py startapp factures
factures/
  __init__.py
  models.py
  tests.py
  views.py

Ces fichiers contiendrons les modèles et les vue de l'application facture

Définition de notre modèle Django (hypothèses simplistes)

Définition de notre modèle Django

from django.db import models
class Client(models.Model):
  nom = models.CharField(max_length=30)
  adresse = models.CharField(max_length=50)
  ville = models.CharField(max_length=60)
  pays = models.CharField(max_length=50)
  website = models.URLField()

Définition de notre modèle Django (2)

class Facture(models.Model):
  numero = models.CharField(max_length=64)
  client = models.ForeignKey(Client)
  montant = models.FloatField()
class Detail_Facture(models.Model):
  facture = models.ForeignKey(Facture)
  nom = models.CharField(max_length=30)
  prix_unitaire  = models.FloatField()
  description = models.CharField(max_length=50)

Définition de notre modèle Django (commentaires)

Activation d'application

Dans settings.py nous allons ajouter notre application dans INSTALLED_APPS:

INSTALLED_APPS = (
  # 'django.contrib.auth',
  # 'django.contrib.contenttypes',
  # 'django.contrib.sessions',
  # 'django.contrib.sites',
  'factures',
)

Validation d'application

Pour vérifier que tout se passe bien nous disposons de la commande de gestion validate

python manage.py validate
wilane@ndoucoumane:~/pat-sn/monprojet$ python manage.py validate
0 errors found
Cette commande vérifie la syntaxe et la logique de vos modèle et rapporte les
erreurs éventuelles trouvées.

Génération SQL

Si le modèle est valide alors on peut regarder le code SQL correspondant à la définition de nos modèles:

python manage.py sqlall factures

Génération SQL (2)

wilane@ndoucoumane:~/pat-sn/monprojet$ python manage.py
                                     sqlall factures
BEGIN;
CREATE TABLE "factures_client" (
  "id" integer NOT NULL PRIMARY KEY,
  "nom" varchar(30) NOT NULL,
  "adresse" varchar(50) NOT NULL,
  "ville" varchar(60) NOT NULL,
  "pays" varchar(50) NOT NULL,
  "website" varchar(200) NOT NULL
  )
  ;

Génération SQL (3)

CREATE TABLE "factures_facture" (
  "id" integer NOT NULL PRIMARY KEY,
  "numero" varchar(64) NOT NULL,
  "client_id" integer NOT NULL REFERENCES "factures_client" ("id"),
  "montant" real NOT NULL
  )
  ;

Génération SQL (4)

CREATE TABLE "factures_detail_facture" (
  "id" integer NOT NULL PRIMARY KEY,
  "facture_id" integer NOT NULL REFERENCES
                 "factures_facture" ("id"),
  "nom" varchar(30) NOT NULL,
  "prix_unitaire" real NOT NULL,
  "description" varchar(50) NOT NULL
  )
  ;
CREATE INDEX "factures_facture_client_id" ON
              "factures_facture" ("client_id");
CREATE INDEX "factures_detail_facture_facture_id" ON
               "factures_detail_facture" ("facture_id");
COMMIT;

Convention de nommage

Synchronisation de données

Une fois que le modèle est définit, vous devez demander à Django de créer le schéma

python manage.py syncdb
wilane@ndoucoumane:~/pat-sn/monprojet$ python manage.py syncdb
Creating table factures_client
Creating table factures_facture
Creating table factures_detail_facture
Installing index for factures.Facture model
Installing index for factures.Detail_Facture model
wilane@ndoucoumane:~/pat-sn/monprojet$

Accès aux données

Gesion simple de données

python manage.py shell
from factures.models import Client, Facture, Detail_Facture
c0 = Client(nom='PAT',
  adresse='22 Rue de la vie',
  ville='Dakar',
  pays='Sénégal',
  website='http://www.coactivate.org/projects/python-african-tour/project-home')
c0.save()

Gesion simple de données (2)

c1 = Client(nom='AUF BAO',
            adresse='Corniche Ouest',
            ville='Dakar',
            pays='Sénégal',
            website='http://www.refer.sn')
 c1.save()
 liste_clients = Client.objects.all()
 liste_clients
 [<Client: Client object>, <Client: Client object>]

Exercice

Représentation chaîne d'un modèle

Exercice

Ajouter une méthode __unicode__ à chaque modèle dans models.py

class Client(models.Model):
  nom = models.CharField(max_length=30)
  adresse = models.CharField(max_length=50)
  ville = models.CharField(max_length=60)
  pays = models.CharField(max_length=50)
  website = models.URLField()
def __unicode__(self):
   return u'%s %s' % (self.nom, self.ville)

Les QuerySets

Application de Filtres

Client.objects.filter(nom='AUF BAO')
[<Client: AUF BAO Dakar>]
>>>

Exercice

Utilisez .query_as_sql() pour voir la requête générée par le filtre précédant:

>>> q=Client.objects.filter(nom='AUF BAO')
>>> q.query
<django.db.models.sql.query.BaseQuery object at 0x8fae14c>
>>> q.query.as_sql()

Critères de recherche

nom__contains permet de recherche des occurrences dont le nom contient

q = Client.objects.filter(nom__contains='BAO')
q.query.as_sql()

Chaîner QuerySets

Les QuerySet peuvent être chaînés

q = Client.objects.filter(ville='Dakar').order_by('nom')
q
[<Client: AUF BAO Dakar>, <Client: PAT Dakar>]

Exercice

Utilisez la QuerySet Client.objects.filter(ville='Dakar').order_by('nom') et commentez le résultat.

Slicing de QuerySets

Exercice

Vérifiez que l'API n'effectue pas le découpage en tranche sur l'ensemble des données mais construit plutôt une requête adaptées pour les bases de données le supportant (Indice: LIMIT SQL)

Ordres dans les QuerySets

class Meta:
  ordering = ['nom']

Exercice

Ajouter un ordre au modèle par la méthode précédente et testez le résultat.

Exercice

Pour aller plus loin

Application contrib.admin

Activation de l'admin

Activation de l'admin (suite)

Exercice

Enregistrement de nos modèles dans l'admin

from django.contrib import admin
from factures.models import Client, Facture, Detail_Facture

admin.site.register(Client)
admin.site.register(Facture)
admin.site.register(Detail_Facture)

Personnalisation dans le modèle

Classe ModelAdmin personnalisée

admin.site.register(Client, ClientAdmin)

Exercice

Gestion des utilisateurs

Les drapeaux:

Formulaires HTML

Gestion d'un formulaire simple (vue formulaire)

On va construire un formulaire permettant de rechercher un client

from django.shortcuts import render_to_response

def form_recherche(request):
  return render_to_response('form_recherche.html')

Gestion d'un formulaire simple (vue de recherche)

from django.http import HttpResponse
def search(request):
  if 'q' in request.GET and request.GET['q']:
      message = 'Vous avez recherché: %r' % request.GET['q']
  else:
      message = 'Votre formulaire envoyé est vide.'
  return HttpResponse(message)

Gestion d'un formulaire simple (template)

<html>
<head>
  <title>Recherche</title>
</head>
<body>
  <form action="/chercher/" method="get">
      <input type="text" name="q">
      <input type="submit" value="Chercher">
  </form>
</body>
</html>

Gestion d'un formulaire simple (URLconf)

from factures import views

urlpatterns = patterns('',
  # ...
  (r'^rech-form/$', views.form_recherche),
  (r'^chercher/$', views.rechercher),
  # ...
)

Exercice

def affichez_meta(request):
  values = request.META.items()
  values.sort()
  html = []
  for k, v in values:
      html.append('<tr><td>%s</td><td>%s</td></tr>' % (k, v))
  return HttpResponse('<table>%s</table>' % '\n'.join(html))

Exercice

Exercice

def search(request):
  error = False
  if 'q' in request.GET:
      q = request.GET['q']
      if not q:
          error = True
      else:
          clients = Client.objects.filter(nom__icontains=q)
          return render_to_response('resultats.html',
              {'clients': clients, 'query': q})
  return render_to_response('form_recherche.html',
      {'error': error})

Exercice

API Form

from django import forms

class FormRecherche(forms.Form):
  q = forms.CharField()

API Form (2)

>>> from django import forms
>>> class RechForm(forms.Form):
...   q = forms.CharField()
...
>>> RechForm()
<__main__.RechForm object at 0xb783f1cc>
>>> f= RechForm()
>>> print f
<tr><th><label for="id_q">Q:</label></th><td>
      <input type="text" name="q" id="id_q" />
               </td></tr>

Rendu du formulaire

>>> print f.as_ul()
<li>
   <label for="id_q">Q:</label>
    <input type="text" name="q" id="id_q" />
</li>

>>> print f.as_p()
<p>
   <label for="id_q">Q:</label>
   <input type="text" name="q" id="id_q" />
</p>

Association d'un formulaire

>>> f= RechForm({'q':'AUF'})
>>> print f
<tr>
  <th>
     <label for="id_q">Q:</label>
  </th>
  <td>
    <input type="text" name="q" value="AUF" id="id_q" />
  </td>
</tr>

Validation du formulaire

>>> f = RechForm()
>>> f.is_bound
False
>>> f = RechForm({'q':'PAT'})
>>> f.is_bound
True
>>> f.is_valid()
True

Validation du formulaire (2)

>>> f = RechForm({'q':''})
>>> f.is_bound
True
>>> f.is_valid()
False
>>> f.errors
{'q': [u'Ce champ est obligatoire.']}
>>> f['q'].errors
[u'Ce champ est obligatoire.']

Données valide d'un formulaire attaché

>>> f = RechForm({'q':'PAT'})
>>> f.is_bound
True
>>> f.is_valid()
True
>>> f.cleaned_data
{'q': u'PAT'}
>>>

Formulaire et Vue (Exercice)

Formulaire et Vue (Exercice) (2)

from django.shortcuts import render_to_response
from factures.forms import RechForm

def recherche(request):
  if request.method == 'POST':
      form = RechForm(request.POST)
      if form.is_valid():
          q = form.cleaned_data['q']
          clients = Client.objects.filter(nom__icontains=q)
          return render_to_response('resultats.html',
              {'clients': clients, 'query': q})
  else:
      form = RechForm()
  return render_to_response('form_recherche.html', {'form': form})

Personnalisation des paramètres de champs

Validation personalisée des champs

from django import forms
class RechForm(forms.Form)
  q = forms.CharField()

  def clean_q(self):
    q = self.cleaned_data['q']
    if not 3 < len(q) <  20:
      raise forms.ValidationError("La chaîne de
              recherche doit avoir entre 3 et 20 caractères!")
    return q

Thèmes avancés à exploré