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 SEOfriendly 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 UTF8, 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 email 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/RennesleChâteau') '/locations/RennesleCh%C3%A2teau'
Slide 8: Supporto unicode: database Assicurarsi che il proprio database sia configurato per utilizzare l'encoding appropriato (UTF8, UTF16) Il database backend si occuperà di convertire automaticamente le stringhe unicode nell'encoding utilizzato dal database, e viceversa È possibile passare stringhe unicode e bytestring UTF8 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 UTF8, 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 (Crosssite 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/modelapi/#modelinheritance 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 manytomany
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: newformsadmin 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: newformsadmin: installazione Scaricare il branch e modificare la variabile d'ambiente PYTHONPATH http://code.djangoproject.com/svn/django/branches/newformsadmin/ 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: newformsadmin: 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: newformsadmin: 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, djangotagging, 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/



Add a comment on Slide 1
If you have a SlideShare account, login to comment; else you can comment as a guest- Favorites & Groups
Showing 1-50 of 0 (more)