Django: utilizzo avanzato e nuove funzionalità
-
Upload
skam -
Category
Technology
-
view
4.914 -
download
7
description
Transcript of Django: utilizzo avanzato e nuove funzionalità
- 1. Django Utilizzo avanzato e nuove funzionalit
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!
3. Perch utilizzare la versione trunk?
- 1 anno dall'ultima release stabile (0.9.6)
- Supporto unicode
- Cambiamenti dell' API
- Nuove funzionalit
4. Utilizzo della versione trunk
- Installazione da sorgenti
- $ cd /path/to/packages
- $ svn cohttp://code.djangoproject.com/svn/django/trunkDjango
- $ python setup.py install
- Utilizzo senza installazione
- $ export PATH=$PATH:/path/to/Django/bin
- $ export PYTHONPATH=$PYTHONPATH:/path/to/packages/Django
5. Supporto unicode: gestione delle stringhe
- Django assume che tutte le bytestring siano inUTF-8 , in caso contrario l'eccezioneUnicodeDecodeErrorverr sollevata
- La variabile di configurazioneDEFAULT_CHARSETviene 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
-
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
7. Supporto unicode: URI e IRI
- Gli URL di tipo URI utilizzano solo caratteri ASCII.
- La funzioneiri_to_uripermette 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-Chteau')
- '/locations/Rennes-le-Ch%C3%A2teau'
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 bytestringUTF-8ai metodi utilizzati per filtrare gli oggettiQuerySet
- >>> Place.objects.filter(name__contains=u'Chteau')
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 funzioneiri_to_uri()nel metodoget_absolute_url()
10. Supporto unicode: template
- I template possono essere salvati sul filesystem utilizzando un charset diverso daUTF-8 , in tal caso necessario definire la variabile di configurazioneFILE_CHARSET
- consigliato l'utilizzo delle funzioneforce_unicodenella creazione di template tag
11. Escape automatico dei template
- Qualsiasi variabile inserita nel contesto di un template viene convertita automaticamente utilizzandodjango.utils.html.escape
- stata introdotta con lo scopo di prevenire attacchi di tipoXSS(Cross-site scripting)
- Non possibile disabilitare tale funzionalit globalmente
12. Escape automatico dei template
- possibile disabilitare l'escape automatico direttamente nei template {% autoescape off %} ... {{ entry.body }} ... {% endautoescape %} {{ entry.body|safe }}
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.Contextaccetta l'argomentoautoescapetemplate.Context({'foo': bar}, autoescape=False)
14. Modelli
- La classeLazyDate() stata rimossa. possibile utilizzare una funzione per l'argomentodefault 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'attributomaxlength deprecato ed stato sostituito damax_length
15. Modelli
- I campi di tipoFloatFieldutilizzavanofloatper rappresentare numeri decimali a precisione fissa
- stato introdotto il campoDecimalField , che utilizza il tipo di datodecimal.Decimalper mantenere la precisione latitude = models.DecimalField(max_digits=8,decimal_places=6)
- FloatFieldnon accetta pi gli argomenti precedentemente utilizzati
16. Modelli
- stato recentemente aggiunto il supporto all'ereditariet delle classiModel 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
17. URL patterns
- ora possibile utilizzare la funzioneurl()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'^(?Pd+)/edit/$', 'place_edit', name='place_edit'), url(r'^(?Pd+)/delete/$','place_delete', name='place_delete') )
18. URL patterns: permalink
- Utilizzo nel metodoget_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)
19. URL patterns: url template tag
- Add place Edit {{ place }} Delete {{ place }}
20. MEDIA_URL context processor
- La variabile di configurazioneMEDIA_URL ora disponibile per poter essere utilizzata nei template
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/'
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 indjango.oldforms
- Nella prossima release stabiledjango.newformsverr rinominata indjango.forms
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
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 argomentimax_lengtherequiredvengono utilizzati per la validazione del campo name
-
- L'argomentowidgetviene utilizzato per definire il tipo di widget utilizzato per l'output HTML
25. newforms
- I dati ricevuti vengono passati come primo argomento del costruttore della classe
- possibile verificare la validit dei dati ricevutiutilizzando il metodois_valid()
- L'attributoerrors un dizionario contenente gli errori di validazione
- Nella release 0.9.6 i datidel form, normalizzati a seconda dei tipi di campo, erano disponibili nell'attributoclean_data , che stato rinominato incleaned_data
26. newforms
- >>> form = ContactForm() >>> form.is_bound False >>> form = ContactForm({'email': '[email protected] '}) >>> form.is_bound True >>> form.is_valid() False >>> form.errors {'comment': [u'This field is required.']}
27. newforms
- Per gestire gli upload possibile passarerequest.FILEScome secondo argomento nel costruttore della classe
- Il metodois_multipart()ritornaTruese la classe contiene campi di tipoFileField
- possibile accedere al nome e al contenuto del file attraversocleaned_data form.cleaned_data['fieldname'].content form.cleaned_data['fieldname'].filename
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))
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()
30. CaptchaWidget
-
- # pycon2.utils.captcha.fields
-
- from django.newforms.util import flatatt
-
- from django.utils.safestring import mark_safe
-
- CAPTCHA_IMAGE = ''''''
-
- 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' % (image, attrs))
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 funzionedjango.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
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
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
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')
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
36. Form preview # pycon2.contacts.preview import pprint from django.http import HttpResponse from django.contrib.formtools.preview importFormPreview 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) 37. Form preview
- Per visualizzare l'anteprima sufficiente aggiungere un pattern nella propria configurazione degli URL ('^preview/contact/$', 'pycon2.contacts.preview.contact'),
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 metododone()
- possibile specificare i template da utilizzare definendo il metodoget_template()
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) 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') ) 41. Form wizard {# templates/contacts/poll.html #} {% extends 'base_site.html' %} {% block title %} Poll - step {{ step }} of {{ step_count }} {% endblock %} {% block content %} {{ form.as_p }} {{ previous_fields|safe }}
{% endblock %} 42. ModelForm
- Permette di creare una classeFormda un modello
- Sostituisce le funzioni deprecateform_for_model()eform_for_instance()
- Permette di modificare con facilit i campi generati automaticamente
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 internaMetaviene utilizzata per definire
-
- il modello da utilizzare
-
- i campi da escludere
44. ModelForm
- Il costruttore della classe accetta l'argomentoinstanceper definire l'istanza del modello da utilizzare
- Il metodosave()ritorna l'istanza del modello
-
- accetta il parametrocommit : se il suo valore Falseil metodo restituisce un istanza non salvata
-
- in tal caso il metodosave_m2m()deve essere utilizzato per poter salvare i dati delle relazioni many-to-many
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)) 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)) 47. newforms-admin
- Utilizza la librerianewforms , eliminando le dipendenze conoldforms
- 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
48. newforms-admin: installazione
- Scaricare il branch e modificare la variabile d'ambientePYTHONPATH 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), )
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) 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) 51. Contenttypes
- Applicazione basata sul modelloContentType , che contiene i seguenti campi
-
- app_label(nome dell'applicazione)
-
- model(nome del modello)
-
- name( verbose_namedi un modello)
- Permette di creare delle relazioni generiche fral' istanza di un modello e diverse altre istanze
- Viene utilizzata in diverse applicazioni fra cui django.contrib.comments, django-tagging, django-voting etc.
52. Contenttypes
-
- >>> from django.contrib.contenttypes.models import ContentType
-
- >>> place_type = ContentType.objects.get(app_label='places',
-
- model='place')
-
- >>> place_type
-
- >>> place_type.model_class()
-
- >>> from pycon2.places.models import Location
-
- >>> ContentType.objects.get_for_model(Location)
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)
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]
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)
-
- [, , ]
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 partiredalla versione 0.9.5
57. Signals
- I seguenti segnali sono utilizzati internamente nei modelli e sono definiti indjango.db.models.signals
-
- pre_init
-
- post_init
-
- pre_save
-
- pre_delete
-
- post_delete
-
- post_syncdb
58. Signals: utilizzo di esempio
- possibile estendere il modellodjango.contrib.auth.models.Userutilizzando un proprio modello attraverso la variabile di configurazioneAUTH_PROFILE_MODULE AUTH_PROFILE_MODULE = 'members.Profile'
- L'istanza del modello definito inAUTH_PROFILE_MODULEnon viene creata automaticamente nel momento in cui un istanza diUserviene salvata
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?
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) 61.
-
- Massimo Scamarcia [email_address]
-
- http://skam.webfactional.com/
Grazie! http://creativecommons.org/licenses/by-nd/2.5/it/