Slideshare.net (beta)

 
Post to TwitterPost to Twitter
Post: 
Myspace Hi5 Friendster Xanga LiveJournal Facebook Blogger Tagged Typepad Freewebs BlackPlanet gigya icons

All comments

Add a comment on Slide 1

If you have a SlideShare account, login to comment; else you can comment as a guest


Showing 1-50 of 0 (more)

Django: utilizzo avanzato e nuove funzionalità

From skam, 4 months ago

Django è uno dei framework web più apprezzati e utilizzati dalla more

850 views  |  0 comments  |  0 favorites  |  5 downloads
 

Categories

Add Category
 
 

Groups / Events

 

 
Embed
options

More Info

This slideshow is Public
Total Views: 850
on Slideshare: 850
from embeds: 0

Slideshow transcript

Slide 1: Django Utilizzo avanzato e nuove funzionalità

Slide 2: Perchè utilizzare django?  Tempi di sviluppo ridotti  Scalabilità e performance  Ottima documentazione, comunità attiva e  partecipe  Motore di templating semplice e adatto ai  designer di pagine HTML  URL puliti e SEO­friendly  Python powered!

Slide 3: Perchè utilizzare la versione trunk?  1 anno dall'ultima release stabile (0.9.6)  Supporto unicode  Cambiamenti dell' API  Nuove funzionalità

Slide 4: Utilizzo della versione trunk  Installazione da sorgenti  $ cd /path/to/packages $ svn co http://code.djangoproject.com/svn/django/trunk  Django $ python setup.py install  Utilizzo senza installazione  $ export PATH=$PATH:/path/to/Django/bin $ export PYTHONPATH=$PYTHONPATH:/path/to/packages/Django

Slide 5: Supporto unicode: gestione delle stringhe  Django assume che tutte le bytestring siano in  UTF­8, in caso contrario l'eccezione  UnicodeDecodeError verrà sollevata  La variabile di configurazione DEFAULT_CHARSET  viene utilizzata solo per il rendering dei template  e dei messaggi e­mail  Le stringhe di traduzione “lazy” possono essere  convertite direttamente in stringhe unicode  http://www.djangoproject.com/documentation/i18n/#lazy­ translation

Slide 6: Supporto unicode: funzioni di conversione  django.utils.encoding.smart_unicode  Converte una qualsiasi stringa in unicode  django.utils.encoding.force_unicode  Identica a smart_unicode, con la differenza che non  preserva le stringhe di traduzione “lazy”, forzandone  la traduzione e la conversione in unicode

Slide 7: Supporto unicode: URI e IRI  Gli URL di tipo URI utilizzano solo caratteri  ASCII.   La funzione iri_to_uri permette di costruire  URL di tipo IRI (RFC 3987)  http://www.ietf.org/rfc/rfc3987.txt >>> from django.utils.encoding import iri_to_uri >>> iri_to_uri(u'/locations/Rennes­le­Château') '/locations/Rennes­le­Ch%C3%A2teau'

Slide 8: Supporto unicode: database  Assicurarsi che il proprio database sia configurato  per utilizzare l'encoding appropriato (UTF­8,  UTF­16)  Il database backend si occuperà di convertire  automaticamente le stringhe unicode  nell'encoding utilizzato dal database, e viceversa  È possibile passare stringhe unicode e bytestring  UTF­8 ai metodi utilizzati per filtrare gli oggetti  QuerySet >>> Place.objects.filter(name__contains=u'Château')

Slide 9: Supporto unicode: modelli  Le stringhe ottenute dai campi testuali dei  modelli sono sempre di tipo unicode  Nel caso il vostro modello contenga un metodo  __str__() può essere necessario implementare  anche il metodo __unicode__()  Se necessario, utilizzare la funzione  iri_to_uri() nel metodo get_absolute_url()

Slide 10: Supporto unicode: template  I template possono essere salvati sul filesystem  utilizzando un charset diverso da UTF­8, in tal  caso è necessario definire la variabile di  configurazione FILE_CHARSET  È consigliato l'utilizzo delle funzione  force_unicode nella creazione di template tag

Slide 11: Escape automatico dei template  Qualsiasi variabile inserita nel contesto di un  template viene convertita automaticamente  utilizzando django.utils.html.escape  È stata introdotta con lo scopo di prevenire  attacchi di tipo XSS (Cross­site scripting)   Non è possibile disabilitare tale funzionalità  globalmente

Slide 12: Escape automatico dei template  È possibile disabilitare l'escape automatico  direttamente nei template {% autoescape off %} ... {{ entry.body }} ... {% endautoescape %} {{ entry.body|safe }}

Slide 13: Escape automatico dei template  È inoltre possibile disabilitare l'escape automatico  nei template filter @register.filter def safefilter(value):   # do something with value...   return value safefilter.is_safe = True  template.Context accetta l'argomento  autoescape  template.Context({'foo': bar},                  autoescape=False)

Slide 14: Modelli  La classe LazyDate() è stata rimossa. È possibile  utilizzare una funzione per l'argomento default from datetime import datetime from django.db import models from django.contrib.auth.models import User class Place(models.Model):   name = models.CharField(max_length=100)   description = models.TextField(blank=True)   created = models.DateField(default=datetime.now)   location = models.ForeignKey(Location)   author = models.ForeignKey(User)  L'utilizzo dell'attributo maxlength è deprecato ed è  stato sostituito da max_length

Slide 15: Modelli  I campi di tipo FloatField utilizzavano float  per rappresentare numeri decimali a precisione  fissa  È stato introdotto il campo DecimalField, che  utilizza il tipo di dato decimal.Decimal per  mantenere la precisione latitude = models.DecimalField(max_digits=8,                                 decimal_places=6)  FloatField non accetta più gli argomenti  precedentemente utilizzati

Slide 16: Modelli  È stato recentemente aggiunto il supporto  all'ereditarietà delle classi Model class Place(models.Model):   name = models.CharField(max_length=100)   description = models.TextField(blank=True)   created = models.DateField(default=datetime.now)   location = models.ForeignKey(Location)   author = models.ForeignKey(User) class Restaurant(Place):   has_bistecca_fiorentina = models.BooleanField()  http://www.djangoproject.com/documentation/model­api/#model­inheritance http://code.djangoproject.com/wiki/QuerysetRefactorBranch

Slide 17: URL patterns  È ora possibile utilizzare la funzione url() per  assegnare un nome a un determinato URL pattern from django.conf.urls.defaults import * urlpatterns = patterns('pycon2.places.views',       url(r'^add/$', 'place_add', name='place_add'),    url(r'^(?P<place_id>\\d+)/edit/$', 'place_edit',        name='place_edit'),    url(r'^(?P<place_id>\\d+)/delete/$','place_delete',        name='place_delete') )

Slide 18: URL patterns: permalink  Utilizzo nel metodo get_absolute_url() di un  modello: from django.db.models import permalink def get_absolute_url(self): return ('place_detail', [str(self.id)]) get_absolute_url = permalink(get_absolute_url)

Slide 19: URL patterns: url template tag <a href=”{% url place_add %}”>Add place</a> <a href=”{% url place_edit place.id %}”>Edit  {{ place }}</a> <a href=”{% url place_delete place.id %}”>Delete  {{ place }}</a>

Slide 20: MEDIA_URL context processor  La variabile di configurazione MEDIA_URL è ora  disponibile per poter essere utilizzata nei  template <img src=”{{ MEDIA_URL }}img/logo.jpg” alt=”logo”  title=”logo” />

Slide 21: URL patterns: urlresolvers >>> from django.core.urlresolvers import reverse >>> reverse('place_add') '/places/add/' >>> reverse('place_edit', args=[1]) '/places/1/edit/' >>> reverse('place_delete', args=[1]) '/places/1/delete/'

Slide 22: newforms  Introdotta nella release 0.9.6 con lo scopo di  sostituire la vecchia libreria di gestione dei form  Permette di semplificare la creazione dei form, la  validazione dei dati ricevuti, l'utilizzo e la  creazione di widget HTML personalizzati  django.forms è stata copiata in django.oldforms  Nella prossima release stabile django.newforms  verrà rinominata in django.forms

Slide 23: newforms  Form  Contiene i campi, riceve i dati, si occupa della  loro validazione e del rendering in HTML   Field  Rappresenta un campo e si occupa della sua  validazione  Widget  Rendering di un campo in HTML

Slide 24: newforms # pycon2.contacts.forms from django import newforms as forms class ContactForm(forms.Form):   name = forms.CharField(required=False,                         max_length=50)  email = forms.EmailField()  comment = forms.CharField(widget=forms.Textarea)  Gli argomenti max_length e required vengono  utilizzati per la validazione del campo “name”  L'argomento widget viene utilizzato per definire il  tipo di widget utilizzato per l'output HTML

Slide 25: newforms  I dati ricevuti vengono passati come primo  argomento del costruttore della classe  È possibile verificare la validità dei dati ricevuti   utilizzando il metodo is_valid()  L'attributo errors è un dizionario contenente gli  errori di validazione  Nella release 0.9.6 i dati  del form, normalizzati a  seconda dei tipi di campo, erano disponibili  nell'attributo clean_data, che è stato rinominato  in cleaned_data

Slide 26: newforms >>> form = ContactForm() >>> form.is_bound False >>> form = ContactForm({'email':                         'foo@bar.com'}) >>> form.is_bound True >>> form.is_valid() False >>> form.errors {'comment': [u'This field is required.']}

Slide 27: newforms  Per gestire gli upload è possibile passare  request.FILES come secondo argomento nel  costruttore della classe  Il metodo is_multipart() ritorna True se la  classe contiene campi di tipo FileField  È possibile accedere al nome e al contenuto del  file attraverso cleaned_data form.cleaned_data['fieldname'].content form.cleaned_data['fieldname'].filename

Slide 28: newforms: esempio di utilizzo from django.template import RequestContext from django.template.loader import render_to_string from django.shortcuts import render_to_response from django.core.mail import mail_admins from pycon2.contacts.forms import ContactForm def contact(request, template='contacts/contact.html',            email_template='contacts/contact_email.txt'): if request.method == 'POST': form = ContactForm(request.POST) if form.is_valid(): message = render_to_string(email_template,                                   form.cleaned_data) mail_admins('contact request', message) else: form = ContactForm() return render_to_response(template, {'form': form},                              context_instance=RequestContext(request))

Slide 29: Field e Widget personalizzati: CaptchaField # pycon2.contacts.forms from django import newforms as forms from pycon2.utils.captcha.fields import CaptchaField class ContactForm(forms.Form):  name = forms.CharField(required=False, max_length=50)  email = forms.EmailField()  comment = forms.CharField(widget=forms.Textarea)  captcha = CaptchaField()

Slide 30: CaptchaWidget # pycon2.utils.captcha.fields from django.newforms.util import flatatt from django.utils.safestring import mark_safe CAPTCHA_IMAGE = '''<div class=”captcha”><img src=”%s”  alt=”captcha” /></div>''' class CaptchaWidget(forms.TextInput): def render(self, name, value, attrs=None):    captcha_url = reverse('captcha')    image = CAPTCHA_IMAGE % captcha_url    if value is None: value = ''    final_attrs = self.build_attrs(attrs,                                     type=self.input_type,                                         name=name)    attrs = flatatt(final_attrs)    if value != '':      final_attrs['value'] = force_unicode(value)    return mark_safe(u'%s<input%s />' % (image, attrs))

Slide 31: CaptchaWidget: funzioni utilizzate  django.newforms.util.flatatt  Converte un dizionario in una stringa contenente gli  attributi utilizzabili in un elemento HTML. I valori del  dizionario vengono convertiti utilizzando la funzione  django.utils.html.escape  django.utils.safestring.mark_safe  Funzione utilizzata per definire che la stringa passata  come argomento è sicura per poter essere utilizzata  nell'output HTML

Slide 32: CaptchaField # pycon2.utils.captcha.fields from django.utils.translation import ugettext as _ from pycon2.utils.captcha.middleware import \\                                        get_current_captcha class CaptchaField(forms.CharField):   widget = CaptchaWidget   def clean(self, value):     captcha = get_current_captcha()     if not captcha:       raise forms.ValidationError(_('You must enable  cookies.'))     if value != captcha:       raise forms.ValidationError(_('Incorrect! Try  again.'))     return value

Slide 33: Captcha middleware # pycon2.utils.captcha.middleware try:   from threading import local except ImportError:   from django.utils._threading_local import local _thread_locals = local() def get_current_captcha():   return getattr(_thread_locals, 'captcha', None) class CaptchaMiddleware(object):   def process_request(self, request):     captcha = None    session = getattr(request, 'session', None)    if session: captcha = session.get('captcha')     _thread_locals.captcha = captcha

Slide 34: Captcha view # pycon2.utils.captcha.view import Captcha import cStringIO from Captcha.Visual.Tests import PseudoGimpy from django.http import HttpResponse def captcha(request):  '''  Visualizza un immagine captcha utilizzando PyCaptcha   '''   g = PseudoGimpy()   s = cStringIO.StringIO()   i = g.render()   i.save(s, 'JPEG')   request.session['captcha'] = g.solutions[0]   return HttpResponse(s.getvalue(), 'image/jpeg')

Slide 35: Form preview  Viene fornita un'applicazione per poter  visualizzare l'anteprima di una classe form  Il form viene visualizzato in formato HTML su una  pagina web  I dati del form vengono validati  Se il form non è valido, viene visualizzato con gli errori di  validazione  Se il form è valido, viene visualizzato per conferma  I dati del form di conferma vengono ricevuti da un  hook definito dall'utente

Slide 36: Form preview # pycon2.contacts.preview import pprint from django.http import HttpResponse from django.contrib.formtools.preview import \\                                       FormPreview from pycon2.contacts.forms import ContactForm class ContactFormPreview(FormPreview):   def done(self, request, cleaned_data):     # do something with cleaned_data...     output = pprint.pformat(cleaned_data)     return HttpResponse(output, mimetype='text/plain') contact = ContactFormPreview(ContactForm)

Slide 37: Form preview  Per visualizzare l'anteprima è sufficiente  aggiungere un pattern nella propria  configurazione degli URL ('^preview/contact/$',   'pycon2.contacts.preview.contact'),

Slide 38: Form wizard  È stata introdotta un applicazione che permette  di creare form suddivisi su più pagine  L'applicazione si occupa di mantenere lo stato dei  dati inseriti nei form fra una pagina e l'altra  Le istanze delle classi form vengono passate al  metodo done()  È possibile specificare i template da utilizzare  definendo il metodo get_template()

Slide 39: Form wizard # pycon2.contacts.forms from django import newforms as forms from django.http import HttpResponseRedirect from django.contrib.formtools.wizard import FormWizard from django.core.urlresolvers import reverse class PollOne(forms.Form):   name = forms.CharField()   email = forms.EmailField()   job_position = forms.CharField() LANGUAGE_CHOICES = (   ('python', 'Python'),   ('ruby', 'Ruby'),    ('perl', 'Perl'),   ('php', 'PHP'), ) class PollTwo(forms.Form):   languages = forms.MultipleChoiceField(choices=LANGUAGE_CHOICES)   preferred_language = forms.ChoiceField(choices=LANGUAGE_CHOICES)

Slide 40: Form wizard # pycon2.contacts.forms class PollWizard(FormWizard):   def done(self, request, form_list):     form_data = [form.cleaned_data for form in form_list]     # do something with form_data     return HttpResponseRedirect(reverse('poll_done'))   def get_template(self, step):     '''Override default template “forms/wizard.html”'''     return ['contacts/poll_%s.html' % step, 'contacts/poll.html'] # pycon2.contacts.urls from django.conf.urls.defaults import * from pycon2.contacts.forms import PollOne, PollTwo, PollWizard urlpatterns = patterns('', url(r'^poll/$', PollWizard([PollOne, PollTwo]), name='poll'), url(r'^poll/done/$',        'django.views.generic.simple.direct_to_template',        {'template': 'contacts/poll_done.html'}, name='poll_done') )

Slide 41: Form wizard {# templates/contacts/poll.html #} {% extends 'base_site.html' %} {% block title %} Poll ­ step {{ step }} of {{ step_count }} {% endblock %} {% block content %} <form action='.' method='POST'> <input type=\"hidden\" name=\"{{ step_field }}\" value=\"{{ step0 }}\" /> {{ form.as_p }} {{ previous_fields|safe }} <p>   <input type=\"submit\" value=\"Submit\" /> </p> </form> {% endblock %}

Slide 42: ModelForm  Permette di creare una classe Form da un  modello  Sostituisce le funzioni deprecate  form_for_model() e form_for_instance()   Permette di modificare con facilità i campi  generati automaticamente

Slide 43: ModelForm # pycon2.places.forms from django import newforms as forms from pycon2.places.models import Place class PlaceForm(forms.ModelForm):   class Meta:    model = Place    exclude = ('created', 'author') La classe interna Meta viene utilizzata per definire   il modello da utilizzare  i campi da escludere

Slide 44: ModelForm  Il costruttore della classe accetta l'argomento  instance per definire l'istanza del modello da  utilizzare  Il metodo save() ritorna l'istanza del modello   accetta il parametro commit: se il suo valore è False  il metodo restituisce un istanza non salvata  in tal caso il metodo save_m2m() deve essere  utilizzato per poter salvare i dati delle relazioni  many­to­many

Slide 45: ModelForm: esempio di utilizzo # pycon2.places.views @login_required def place_add(request, template='places/place_add.html'):   if request.method == 'POST':     form = PlaceForm(request.POST)     if form.is_valid():       place = form.save(commit=False)       place.author = request.user       place.save()       return HttpResponseRedirect(place.get_absolute_url())   else:     form = PlaceForm()   return render_to_response(template, {'form': form},                 context_instance=RequestContext(request))

Slide 46: ModelForm: esempio di utilizzo # pycon2.places.views @login_required def place_edit(request, place_id,                 template='places/place_edit.html'):   place = get_object_or_404(Place, pk=place_id)   if request.method == 'POST':     form = PlaceForm(request.POST, instance=place)     if form.is_valid():       form.save()       return HttpResponseRedirect(place.get_absolute_url())   else:     form = PlaceForm(instance=place)   return render_to_response(template, {'form': form,                                        'place': place},                 context_instance=RequestContext(request))

Slide 47: newforms­admin  Utilizza la libreria newforms, eliminando le  dipendenze con oldforms  Disaccoppia le funzionalità dell'interfaccia di  amministrazione dai modelli  Permette di personalizzare alcuni aspetti  dell'interfaccia di amministrazione, fra cui:  Definire widget personalizzati per i campi e  modificarne gli attributi  Modificare i permessi sui singoli oggetti

Slide 48: newforms­admin: installazione  Scaricare il branch e modificare la variabile  d'ambiente PYTHONPATH  http://code.djangoproject.com/svn/django/branches/newforms­admin/  Modificare la propria configurazione degli URL from django.conf.urls.defaults import * from django.contrib import admin urlpatterns = patterns('',   (r'^admin/(.*)', admin.site.root), )

Slide 49: newforms­admin: esempio di utilizzo # pycon2.places.models from django.contrib import admin class Place(models.Model):   name = models.CharField(max_length=100)   description = models.TextField(blank=True)   created = models.DateField(default=datetime.now)   location = models.ForeignKey(Location)   author = models.ForeignKey(User) class Location(models.Model):   name = models.CharField(max_length=100)   latitude = models.DecimalField(max_digits=8,                                  decimal_places=6)   longitude = models.DecimalField(max_digits=8,                                   decimal_places=6) # register models into admin site admin.site.register(Place) admin.site.register(Location)

Slide 50: newforms­admin: esempio di utilizzo class PlaceOptions(admin.ModelAdmin):   list_display = ('name', 'location', 'author')   search_fields = ('name', 'description')   def has_change_permission(self, request, obj=None):     if obj:       if request.user != obj.author: return False     return super(PlaceOptions,                  self).has_change_permission(request, obj) class PlaceInline(admin.TabularInline)   model = Place   extra = 5 class LocationOptions(admin.ModelAdmin):   inlines = [PlaceInline] # register models into admin site admin.site.register(Place, PlaceOptions) admin.site.register(Location, LocationOptions)

Slide 51: Contenttypes  Applicazione basata sul modello ContentType,  che contiene i seguenti campi  app_label (nome dell'applicazione)  model (nome del modello)  name (verbose_name di un modello)  Permette di creare delle relazioni generiche fra  l' istanza di un modello e diverse altre istanze  Viene utilizzata in diverse applicazioni fra cui  django.contrib.comments, django­tagging, django­ voting etc.

Slide 52: Contenttypes >>> from django.contrib.contenttypes.models import ContentType >>> place_type = ContentType.objects.get(app_label='places',                                          model='place') >>> place_type <ContentType: place> >>> place_type.model_class() <class 'pycon2.places.models.Place'> >>> from pycon2.places.models import Location >>> ContentType.objects.get_for_model(Location) <ContentType: location>

Slide 53: Contenttypes: ObjectCounter # pycon2.counter.models from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.contrib.auth.models import User from pycon2.counter.managers import ObjectCounterManager class ObjectCounter(models.Model):   content_type = models.ForeignKey(ContentType)   object_id = models.PositiveIntegerField()   content_object = generic.GenericForeignKey('content_type',                                             'object_id')   visited = models.DateTimeField(auto_now_add=True)   user = models.ForeignKey(User, blank=True, null=True)      objects = ObjectCounterManager()   def __unicode__(self):     return u'%s: %d' % (self.content_type.name, self.object_id)

Slide 54: Contenttypes: ObjectCounterManager # pycon2.counter.managers from django.db import models, connection from django.contrib.contenttypes.models import ContentType class ObjectCounterManager(models.Manager):   def count_object(self, obj, user=None):      counter = self.model()      counter.content_object = obj      if user: counter.user = user      counter.save()   def most_visited_for_model(self, model, num=10):     '''Inspired by http://www.djangosnippets.org/snippets/108/'''      content_type = ContentType.objects.get_for_model(model)     primary_table = model._meta.db_table     secondary_table = self.model()._meta.db_table     query = \"\"\"SELECT p.id AS obj_id, COUNT(*) AS score     FROM %s p INNER JOIN %s s ON (p.id = s.object_id)     WHERE s.content_type_id = %%s GROUP BY obj_id     ORDER BY score DESC\"\"\" % (primary_table, secondary_table)     cursor = connection.cursor()     cursor.execute(query, [content_type.id])     object_ids = [row[0] for row in cursor.fetchall()[:num]]     object_dict = model._default_manager.in_bulk(object_ids)     return [object_dict[object_id] for object_id in object_ids]

Slide 55: Contenttypes: utilizzo di ObjectCounter >>> from django.db import models >>> from pycon2.counter.models import ObjectCounter >>> from pycon2.members.models import Profile >>> profile1 = Profile.objects.get(pk=1) >>> profile2 = Profile.objects.get(pk=2) >>> profile3 = Profile.objects.get(pk=3) >>> ObjectCounter.objects.count_object(profile1) >>> ObjectCounter.objects.count_object(profile1) >>> ObjectCounter.objects.count_object(profile1) >>> ObjectCounter.objects.count_object(profile2) >>> ObjectCounter.objects.count_object(profile2) >>> ObjectCounter.objects.count_object(profile3) >>> ObjectCounter.objects.most_visited_for_model(Profile) [<Profile: foo>, <Profile: bar>, <Profile: baz>]

Slide 56: Signals  Django utilizza il dispatching di eventi per la  gestione di determinate funzionalità  È possibile collegare le proprie funzioni ai segnali  utilizzati internamente dal framework  Tali funzionalità sono offerte da PyDispatcher,  che è stato incluso a partire dalla versione 0.9.5

Slide 57: Signals  I seguenti segnali sono utilizzati internamente  nei modelli e sono definiti in  django.db.models.signals  pre_init  post_init  pre_save  pre_delete  post_delete  post_syncdb

Slide 58: Signals: utilizzo di esempio  È possibile estendere il modello  django.contrib.auth.models.User utilizzando  un proprio modello attraverso la variabile di  configurazione AUTH_PROFILE_MODULE AUTH_PROFILE_MODULE = 'members.Profile'  L'istanza del modello definito in  AUTH_PROFILE_MODULE non viene creata  automaticamente nel momento in cui un istanza di  User viene salvata 

Slide 59: Signals: utilizzo di esempio  Possiamo creare l'istanza del modello nella nostra  view che si occupa della registrazione dei modelli  E se l'utente viene creato da un amministratore  attraverso l'interfaccia di amministrazione?

Slide 60: Signals: utilizzo d'esempio # pycon2.members.models from django.db import models from django.contrib.auth.models import User from django.dispatch import dispatcher from django.db.models import signals class Profile(models.Model):   user = models.ForeignKey(User)   photo = models.ImageField(upload_to='pics', blank=True)   bio = models.TextField(blank=True) def create_profile(sender, instance, signal, *args, **kwargs):   if kwargs.get('created'):     try:       instance.get_profile()     except Profile.DoesNotExist:       profile = Profile(user=instance)       profile.save()        dispatcher.connect(create_profile, signal=signals.post_save,                    sender=User)

Slide 61: Grazie! Massimo Scamarcia massimo.scamarcia@gmail.com http://skam.webfactional.com/ http://creativecommons.org/licenses/by-nd/2.5/it/