SlideShare une entreprise Scribd logo
1  sur  81
Télécharger pour lire hors ligne
Advanced Django

Simon Willison
http://simonwillison.net/
PyCon UK, 8th September 2007
Today’s topics

  Unit testing
  newforms
  Ajax
  And if we have time... OpenID
Unit testing
Hard core TDD
 Test Driven Development as taught by Kent
 Beck
 “Write new code only if an automated test has
 failed”
 Revolutionises the way you write code
 Pretty hard to adopt
“     I don’t do test driven
  development. I do stupidity
driven testing... I wait until I do
 something stupid, and then write




                               ”
   tests to avoid doing it again.


                      Titus Brown
What NOT to do

 No matter how tough the deadline, don't let
 your test suite start breaking and say “I’ll fix it
 later”
 This will hurt. A lot.
Testing in Django
  Testing web apps is HARD, but Django helps out
  with a bunch of features:
   Fixtures
   Doctests
   Test Client
   E-mail capture
Doctests
  Used by Django for both ORM testing and
  generated documentation
  You are encouraged to add them to your own
  models.py
  manage.py test to execute them
  Great for regression tests - just copy and paste
  from an interpreter session
>>>   p = Person(name=quot;Bobquot;, dob=date(1980, 1, 1))
>>>   p.age(date(1980, 1, 1))
0
>>>   p.age(date(1979, 1, 1))
-1
>>>   p.age(date(1981, 6, 1))
1
>>>   p.age(date(2000, 1, 1))
20
>>>   p.age(date(1999, 12, 31))
19
>>>   p2 = Person(name=quot;Barryquot;, dob=date(1981, 5, 5))
>>>   p2.age(date(1981, 5, 5))
0
>>>   p2.age(date(1982, 5, 4))
0
>>>   p2.age(date(1982, 5, 5))
1
>>>   p2.age(date(1982, 5, 6))
1
class Person(models.Model):
    quot;quot;quot;
    ... tests here ...
    quot;quot;quot;
    name = models.CharField(maxlength=100)
    dob = models.DateField()
    def age(self, age=False):
        return 1
$ python manage.py test

...F
======================================================================
FAIL: Doctest: peopleage.models.Person
----------------------------------------------------------------------
Traceback (most recent call last):
  File quot;/usr/lib/python2.5/site-packages/django/test/_doctest.pyquot;, line 2161, in runTest
     raise self.failureException(self.format_failure(new.getvalue()))
AssertionError: Failed doctest test for peopleage.models.Person
  File quot;/home/simon/Projects/Django/oscon07/peopleage/models.pyquot;, line 4, in Person

----------------------------------------------------------------------
File quot;/home/simon/Projects/Django/oscon07/peopleage/models.pyquot;, line 7, in
peopleage.models.Person
Failed example:
     p.age(date(1980, 1, 1))
Expected:
     0
Got:
     1
def age(self, age=False):
    if not age:
        age = date.today()
    delta = age - self.dob
    return int(math.floor(delta.days / float(365)))
File quot;/home/simon/Projects/Django/oscon07/peopleage/models.pyquot;, line 16,
in peopleage.models.Person
Failed example:
     p.age(date(1999, 12, 31))
Expected:
     19
Got:
     20
def age(self, age=False):
    if not age:
        age = date.today()
    years = age.year - self.dob.year
    this_year_birthday = self.dob.replace(year=age.year)
    birthday_has_passed = age >= this_year_birthday
    if not birthday_has_passed:
        years = years - 1
    return years
Fixtures

  It’s useful to be able to clear and populate your
  test database in between tests
  Fixtures let you do that (they also let you
  populate your database with real data when you
  deploy your application)
Denormalisation
“
Normalised data is
   for sissies


                   ”
          Cal Henderson
A forum, where each thread can have one
or more replies

Maintain a separate counter in the Forum
table of number of replies, to speed up
queries
class Thread(models.Model):
    subject = models.CharField(maxlength=100)
    num_replies = models.IntegerField(default=0)

class Reply(models.Model):
    thread = models.ForeignKey(Thread)
    message = models.TextField()
[{ quot;modelquot;: quot;forum.threadquot;,
    quot;pkquot;: quot;1quot;,
    quot;fieldsquot;: {
      quot;num_repliesquot;: 0,
      quot;subjectquot;: quot;First threadquot; }
 },{ quot;modelquot;: quot;forum.threadquot;,
    quot;pkquot;: quot;2quot;,
    quot;fieldsquot;: {
      quot;num_repliesquot;: 1,
      quot;subjectquot;: quot;Second threadquot; }
 },{ quot;modelquot;: quot;forum.replyquot;,
    quot;pkquot;: quot;1quot;,
    quot;fieldsquot;: {
      quot;threadquot;: 2,
      quot;messagequot;: quot;First post!1quot; }
 }]
from django.test import TestCase
from models import Thread, Reply

class ThreadCountTestCase(TestCase):
    fixtures = ['threadcount.json']

    def test_add_reply(self):
        thread = Thread.objects.get(pk=2)
        self.assertEqual(thread.num_replies, 1)
        thread.reply_set.create(message=quot;Another postquot;)
        thread = Thread.objects.get(pk=2)
        self.assertEqual(thread.reply_set.count(), 2)
        self.assertEqual(thread.num_replies, 2)

    def test_delete_reply(self):
        thread = Thread.objects.get(pk=2)
        self.assertEqual(thread.num_replies, 1)
        Reply.objects.get(pk=1).delete()
        thread = Thread.objects.get(pk=2)
        self.assertEqual(thread.reply_set.count(), 0)
        self.assertEqual(thread.num_replies, 0)
======================================================
FAIL: test_add_reply (forum.tests.ThreadCountTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File quot;/home/simon/Projects/Django/oscon07/forum/tests.pyquot;, line 16, in
test_add_reply
    self.assertEqual(thread.num_replies, 2)
AssertionError: 1 != 2

======================================================
FAIL: test_delete_reply (forum.tests.ThreadCountTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File quot;/home/simon/Projects/Django/oscon07/forum/tests.pyquot;, line 23, in
test_delete_reply
    self.assertEqual(thread.num_replies, 0)
AssertionError: 1 != 0

----------------------------------------------------------------------
class Reply(models.Model):
    ...
    def save(self):
        super(Reply, self).save()
        self.thread.num_replies = self.thread.reply_set.count()
        self.thread.save()
    def delete(self):
        super(Reply, self).delete()
        self.thread.num_replies = self.thread.reply_set.count()
        self.thread.save()
.......
-----------------------------------------------------------
Ran 7 tests in 0.372s

OK
Testing views

  Django’s TestClient lets you simulate a browser
  interacting with your site
  It also provides hooks in to the underlying
  application, so you can test against the template
  and context that was used to generate a page
from django.test import TestCase

class RegistrationTest(TestCase):
    def test_slash(self):
        response = self.client.get('/register')
        self.assertEqual(response.status_code, 301)
        response = self.client.get('/register/')
        self.assertEqual(response.status_code, 200)

   def test_register(self):
       response = self.client.get('/register/')
       self.assertEqual(response.template[0].name, 'register.html')
       self.assertTemplateUsed(response, 'register.html')
def test_signup_done_page(self):
    self.assertEqual(len(mail.outbox), 0)
    data = {
        'email': 'simon@example.com', 'username': 'example',
        'firstname': 'Example', 'lastname': 'User',
        'password': 'password1', 'password2': 'password1',
    }
    response = self.client.post('/signup/', data)
    self.assertEquals(response.status_code, 302)
    self.assertEquals(response['Location'], '/welcome/')
    # Check that the confirmation e-mail was sent
    self.assertEqual(len(mail.outbox), 1)
    sent = mail.outbox[0]
    self.assertEqual(sent.subject, 'Welcome to example.com')
More on testing with Django:
http://www.djangoproject.com/documentation/testing/
newforms
The perfect form
  Display a form
  User fills it in and submits it
  Validate their entered data
    If errors, redisplay form with previously
    entered data and contextual error messages
  Continue until their submission is valid
  Convert submission to appropriate Python types
Manipulators
Manipulators
 newforms
Forms are declarative
from django import newforms as forms

class UserProfileForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    bio = forms.CharField(widget=forms.Textarea)
    dob = forms.DateField(required=False)
    receive_newsletter = forms.BooleanField(required=False)
A form instance can...

  validate a set of data against itself

  render itself (and its widgets)

  re-render itself with errors

  convert to Python types
Simple form handling view
 def create_profile(request):
     if request.method == 'POST':
         form = UserProfileForm(request.POST)
         if form.is_valid():
             # ... save the user’s profile
             return HttpResponseRedirect('/profile/saved/')
     else:
         form = UserProfileForm()
     return render_to_response('profile.html', {'form': form})
Initial data

    form = UserProfileForm(
        initial = {
            'name': 'Simon Willison',
            'email': 'simon@simonwillison.net',
        }
    )
Simple template
<style type=quot;text/cssquot;>
ul.errorlist { color: red; }
</style>

...
<form action=quot;/profile/create/quot; method=quot;POSTquot;>
{{ form.as_p }}
<input type=quot;submitquot; value=quot;Submitquot; />
</form>
...
Output
<form action=quot;/profile/create/quot; method=quot;POSTquot;>
<p><label for=quot;id_namequot;>Name:</label> <input id=quot;id_namequot;
type=quot;textquot; name=quot;namequot; maxlength=quot;100quot; /></p>
<p><label for=quot;id_emailquot;>Email:</label> <input type=quot;textquot;
name=quot;emailquot; id=quot;id_emailquot; /></p>
<p><label for=quot;id_bioquot;>Bio:</label> <textarea id=quot;id_bioquot; rows=quot;10quot;
cols=quot;40quot; name=quot;bioquot;></textarea></p>
<p><label for=quot;id_dobquot;>Dob:</label> <input type=quot;textquot; name=quot;dobquot;
id=quot;id_dobquot; /></p>
<p><label for=quot;id_receive_newsletterquot;>Receive newsletter:</label>
<input type=quot;checkboxquot; name=quot;receive_newsletterquot;
id=quot;id_receive_newsletterquot; /></p>
<input type=quot;submitquot; value=quot;Submitquot; />
</form>
Output
<form action=quot;/profile/create/quot; method=quot;POSTquot;>
<p><label for=quot;id_namequot;>Name:</label> <input id=quot;id_namequot;
type=quot;textquot; name=quot;namequot; maxlength=quot;100quot; /></p>
<p><label for=quot;id_emailquot;>Email:</label> <input type=quot;textquot;
name=quot;emailquot; id=quot;id_emailquot; /></p>
<p><label for=quot;id_bioquot;>Bio:</label> <textarea id=quot;id_bioquot; rows=quot;10quot;
cols=quot;40quot; name=quot;bioquot;></textarea></p>
<p><label for=quot;id_dobquot;>Dob:</label> <input type=quot;textquot; name=quot;dobquot;
id=quot;id_dobquot; /></p>
<p><label for=quot;id_receive_newsletterquot;>Receive newsletter:</label>
<input type=quot;checkboxquot; name=quot;receive_newsletterquot;
id=quot;id_receive_newsletterquot; /></p>
<input type=quot;submitquot; value=quot;Submitquot; />
</form>
Output
<form action=quot;/profile/create/quot; method=quot;POSTquot;>
<p><label for=quot;id_namequot;>Name:</label> <input id=quot;id_namequot;
type=quot;textquot; name=quot;namequot; maxlength=quot;100quot; /></p>
<p><label for=quot;id_emailquot;>Email:</label> <input type=quot;textquot;
name=quot;emailquot; id=quot;id_emailquot; /></p>
<p><label for=quot;id_bioquot;>Bio:</label> <textarea id=quot;id_bioquot; rows=quot;10quot;
cols=quot;40quot; name=quot;bioquot;></textarea></p>
<p><label for=quot;id_dobquot;>Dob:</label> <input type=quot;textquot; name=quot;dobquot;
id=quot;id_dobquot; /></p>
<p><label for=quot;id_receive_newsletterquot;>Receive newsletter:</label>
<input type=quot;checkboxquot; name=quot;receive_newsletterquot;
id=quot;id_receive_newsletterquot; /></p>
<input type=quot;submitquot; value=quot;Submitquot; />
</form>
Custom validation
from django import newforms as forms
from django.newforms.util import ValidationError

class UserProfileForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    bio = forms.CharField(widget=forms.Textarea)
    dob = forms.DateField(required=False)
    receive_newsletter = forms.BooleanField(required=False)

    def clean_email(self):
        if self.cleaned_data['email'].split('@')[1] == 'hotmail.com':
            raise ValidationError, quot;No hotmail.com emails, please.quot;
Custom validation
from django import newforms as forms
from django.newforms.util import ValidationError

class UserProfileForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    bio = forms.CharField(widget=forms.Textarea)
    dob = forms.DateField(required=False)
    receive_newsletter = forms.BooleanField(required=False)

    def clean_email(self):
        if self.cleaned_data['email'].split('@')[1] == 'hotmail.com':
            raise ValidationError, quot;No hotmail.com emails, please.quot;
Custom rendering
<ol class=quot;formItems longFormquot;>
     <li{% if form.email.errors %} class=quot;errorsquot;{% endif %}>
         <label for=quot;id_emailquot;>Email: </label>
         {{ form.email }}
         {{ form.email.errors }}
         <p class=quot;infoquot;>Your e-mail address.</p>
     </li>
     ...
</ol>
Model shortcuts
  DRY: You’ve already declared your models; you
  shouldn’t have to repeat yourself in your forms
  UserForm = form_for_model(User)

  ###############################

  page = Page.objects.get(pk=1)
  PageForm = form_for_instance(page)

  form = PageForm(request.POST)
  ...
  if form.is_valid():
      form.save()
Full documentation:
http://www.djangoproject.com/documentation/newforms/

    django/trunk/tests/regressiontests/forms/tests.py
Ajax
First things first
  If you're going to do Ajax, you need a JavaScript
  library
  You could use Yet Another XMLHttpRequest
  abstraction... but the popular libraries offer fantastic
  convenience
  Good libraries include YUI, Dojo, MochiKit and
  (controversial) Prototype...
.. and jQuery

  I'm going to be using jQuery
    Almost everything is done in terms of CSS
    selectors and chained methods
    It looks like a gimmick, but it isn't
    http://simonwillison.net/2007/Aug/15/jquery/
Ajax formats...

  Django has great support for any and every Ajax
  format
    HTML fragments
    XML
    JSON
Username available?
from django.contrib.auth.models import User

def check_username(request):
    reply = quot;quot;
    username = request.GET.get('username', '')
    if username:
        if User.objects.filter(username=username).count():
            reply = 'Unavailable'
        else:
            reply = 'Available'
    return HttpResponse(reply)
jQuery('span#msg').load(
    '/check_username/?username=' + input.val()
);
$('span#msg').load(
    '/check_username/?username=' + input.val()
);
var input = $('input#id_username')
input.keyup(function() {
    $('span#msg').load(
        '/check_username/?username=' + input.val()
    );
});
$(document).ready(function() {
    var input = $('input#id_username')
    input.keyup(function() {
        $('span#msg').load(
            '/check_username/?username=' + input.val()
        );
    });
});
$(function() {
    var input = $('input#id_username')
    input.keyup(function() {
        $('span#msg').load(
            '/check_username/?username=' + input.val()
        );
    });
});
Recycling server-side
  form validation
from django import newforms as forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea())
    sender = forms.EmailField()
def validate_contact(request):
    quot;Validate post data, return errors as jsonquot;
    form = ContactForm(request.POST)
    if (request.GET.has_key('field')):
        # Validate a single field
        errors = form.errors[request.GET['field']]
    else:
        errors = form.errors
    return JsonResponse({
        'valid': not errors,
        'errors': errors
    })
from django.utils import simplejson

class JsonResponse(HttpResponse):
    def __init__(self, data):
        HttpResponse.__init__(
            self, simplejson.dumps(data),
            mimetype='application/json'
        )
function validateInput(input) {
      $.post('/contact/validate/?field=' + input.attr('id').replace('id_', ''),
          $('form').formToArray(), function(data) {
              var json = eval('(' + data + ')');
              showErrors(input, json.errors);
          }
      );
}

$(function() {
      $(':input').blur(function() {
          validateInput($(this));
      });
});
function relatedErrorList(input) {
     var prevUL = $(input).parent().prev();
     if (prevUL && prevUL.attr('class') == 'errorlist') {
        return prevUL;
     }
     var errorlist = $('<ul class=quot;errorlistquot;></ul>');
     input.parent().before(errorlist);
     return errorlist;
}

function showErrors(input, errors) {
     var errorlist = relatedErrorList(input);
     errorlist.empty();
     $.each(errors, function(i, error) {
         errorlist.append('<li>' + error + '</li>');
     });
}
Django philosophy

  Django often gets marked down in “framework
  comparisons” due to the lack of built in Ajax
  support
  Personally I think that shipping without a
  recommended library is a feature, not a bug
(bonus section)
What is OpenID?
OpenID is a decentralised
mechanism for Single Sign On
An OpenID is a URL

  http://simonwillison.myopenid.com/
  http://simonwillison.net/
  http://swillison.livejournal.com/
  http://openid.aol.com/simonwillison
How it works

  You enter your OpenID on a site (instead of the
  usual username and password)
  It redirects you back to your OpenID provider
  They authenticate you in some way
  They redirect you back to the original site
Simple registration
(an optional but useful extension)
Consumers can also ask...

   Your preferred username
   Your e-mail address
   Your first and last name
   Your date of birth
   Your language, country and timezone
How do you use
OpenID in a Django
   application?
The hard way
  Use the JanRain OpenID library
  Pretty much a reference implementation for the
  OpenID spec
  Well written, well tested but takes a while to get
  the hang of
  www.openidenabled.com/openid/libraries/python/
The easy way

  Use django-openid
  A simple wrapper around JanRain
  A middleware component, some models and a
  few pre-written views
  http://code.google.com/p/django-openid/
Installation
  Add 'django_openidconsumer' to your
  INSTALLED_APPS setting
  manage.py syncdb
  Add the OpenIDMiddleware to your
  MIDDLEWARE_CLASSES setting
  Add the views to your URLconf
In urls.py
  ...
  (r'^openid/$',
  'django_openidconsumer.views.begin'),
  (r'^openid/complete/$',
  'django_openidconsumer.views.complete'),
  (r'^openid/signout/$',
  'django_openidconsumer.views.signout'),
  ...
request.openid

  The middleware adds an openid property to the
  Django request object
  If the user is not signed in, this will be None
  Otherwise, it will be an OpenID object; the str()
  representation will be the OpenID (or use
  request.openid.openid)
def example_view(request):
    if request.openid:
        return HttpResponse(quot;OpenID is %squot; % escape(request.openid))
    else:
        return HttpResponse(quot;No OpenIDquot;)
request.openids

  The module supports users signing in with more
  than one OpenID at a time
   request.openids provides a list of all
   authenticated OpenIDs
   request.openid merely returns the most
   recent from this list
For simple registration

(r'^openid/$', 'django_openidconsumer.views.begin', {
    'sreg': 'email,nickname'
}),

def example_sreg(request):
    if request.openid and request.openid.sreg.has_key('email'):
        return HttpResponse(quot;Your e-mail address is: %squot; % escape(
            request.openid.sreg['email']
        ))
    else:
        return HttpResponse(quot;No e-mail addressquot;)
Coming soon

 django_openidauth, providing tools to associate
 OpenIDs with existing django.contrib.auth
 accounts
 django_openidserver, to make it easy to provide
 OpenIDs for users of your Django application
More information

   http://openid.net/
     Also home to the OpenID mailing lists
   http://www.openidenabled.com/
   http://simonwillison.net/tags/openid/
   http://code.google.com/p/django-openid/
Thank you!

Contenu connexe

Tendances

Web2py Code Lab
Web2py Code LabWeb2py Code Lab
Web2py Code LabColin Su
 
JavaScript
JavaScriptJavaScript
JavaScriptSunil OS
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologyDaniel Knell
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your projectMichelangelo van Dam
 
Backbone js
Backbone jsBackbone js
Backbone jsrstankov
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 MinutesAzim Kurt
 
Building a Dynamic Website Using Django
Building a Dynamic Website Using DjangoBuilding a Dynamic Website Using Django
Building a Dynamic Website Using DjangoNathan Eror
 
Building a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesBuilding a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesCiaranMcNulty
 
Owl: The New Odoo UI Framework
Owl: The New Odoo UI FrameworkOwl: The New Odoo UI Framework
Owl: The New Odoo UI FrameworkOdoo
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to Djangocolinkingswood
 
Overlays, Accordions & Tabs, Oh My
Overlays, Accordions & Tabs, Oh MyOverlays, Accordions & Tabs, Oh My
Overlays, Accordions & Tabs, Oh MySteve McMahon
 
Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Eric Palakovich Carr
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateKiev ALT.NET
 
Doctrine 2
Doctrine 2Doctrine 2
Doctrine 2zfconfua
 
Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4DEVCON
 
Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4DEVCON
 

Tendances (20)

Ruby - Design patterns tdc2011
Ruby - Design patterns tdc2011Ruby - Design patterns tdc2011
Ruby - Design patterns tdc2011
 
Web2py Code Lab
Web2py Code LabWeb2py Code Lab
Web2py Code Lab
 
JavaScript
JavaScriptJavaScript
JavaScript
 
QA for PHP projects
QA for PHP projectsQA for PHP projects
QA for PHP projects
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Zf2 how arrays will save your project
Zf2   how arrays will save your projectZf2   how arrays will save your project
Zf2 how arrays will save your project
 
Backbone js
Backbone jsBackbone js
Backbone js
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
Unit testing zend framework apps
Unit testing zend framework appsUnit testing zend framework apps
Unit testing zend framework apps
 
Building a Dynamic Website Using Django
Building a Dynamic Website Using DjangoBuilding a Dynamic Website Using Django
Building a Dynamic Website Using Django
 
Building a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing StrategiesBuilding a Pyramid: Symfony Testing Strategies
Building a Pyramid: Symfony Testing Strategies
 
JQuery introduction
JQuery introductionJQuery introduction
JQuery introduction
 
Owl: The New Odoo UI Framework
Owl: The New Odoo UI FrameworkOwl: The New Odoo UI Framework
Owl: The New Odoo UI Framework
 
Introduction to Django
Introduction to DjangoIntroduction to Django
Introduction to Django
 
Overlays, Accordions & Tabs, Oh My
Overlays, Accordions & Tabs, Oh MyOverlays, Accordions & Tabs, Oh My
Overlays, Accordions & Tabs, Oh My
 
Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!Django Rest Framework and React and Redux, Oh My!
Django Rest Framework and React and Redux, Oh My!
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
 
Doctrine 2
Doctrine 2Doctrine 2
Doctrine 2
 
Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4
 
Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4
 

En vedette

Advanced Django ORM techniques
Advanced Django ORM techniquesAdvanced Django ORM techniques
Advanced Django ORM techniquesDaniel Roseman
 
Getting Started With Django
Getting Started With DjangoGetting Started With Django
Getting Started With Djangojeff_croft
 
Advanced Django Forms Usage
Advanced Django Forms UsageAdvanced Django Forms Usage
Advanced Django Forms UsageDaniel Greenfeld
 
Django - Python MVC Framework
Django - Python MVC FrameworkDjango - Python MVC Framework
Django - Python MVC FrameworkBala Kumar
 
12 tips on Django Best Practices
12 tips on Django Best Practices12 tips on Django Best Practices
12 tips on Django Best PracticesDavid Arcos
 
Introduction To Django
Introduction To DjangoIntroduction To Django
Introduction To DjangoJay Graves
 
Web Development with Python and Django
Web Development with Python and DjangoWeb Development with Python and Django
Web Development with Python and DjangoMichael Pirnat
 
Django Admin (Python meeutp)
Django Admin (Python meeutp)Django Admin (Python meeutp)
Django Admin (Python meeutp)Ines Jelovac
 
Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial之宇 趙
 
(Self) Publishing
(Self) Publishing(Self) Publishing
(Self) PublishingVlad Micu
 
Profile.e (yuki sato)0529
Profile.e (yuki sato)0529Profile.e (yuki sato)0529
Profile.e (yuki sato)0529Sato Yuki
 
Status Www Undersøgelse Udenpix
Status Www Undersøgelse UdenpixStatus Www Undersøgelse Udenpix
Status Www Undersøgelse UdenpixElisabeth Tejlmand
 
2012 Email Evolution Sneak Peak Webinar: Part 1
2012 Email Evolution Sneak Peak Webinar: Part 12012 Email Evolution Sneak Peak Webinar: Part 1
2012 Email Evolution Sneak Peak Webinar: Part 1Vivastream
 

En vedette (20)

Advanced Django ORM techniques
Advanced Django ORM techniquesAdvanced Django ORM techniques
Advanced Django ORM techniques
 
Django in the Real World
Django in the Real WorldDjango in the Real World
Django in the Real World
 
Getting Started With Django
Getting Started With DjangoGetting Started With Django
Getting Started With Django
 
Django Best Practices
Django Best PracticesDjango Best Practices
Django Best Practices
 
Advanced Django Forms Usage
Advanced Django Forms UsageAdvanced Django Forms Usage
Advanced Django Forms Usage
 
Scaling Django
Scaling DjangoScaling Django
Scaling Django
 
Django - Python MVC Framework
Django - Python MVC FrameworkDjango - Python MVC Framework
Django - Python MVC Framework
 
12 tips on Django Best Practices
12 tips on Django Best Practices12 tips on Django Best Practices
12 tips on Django Best Practices
 
Python/Django Training
Python/Django TrainingPython/Django Training
Python/Django Training
 
Introduction To Django
Introduction To DjangoIntroduction To Django
Introduction To Django
 
Web Development with Python and Django
Web Development with Python and DjangoWeb Development with Python and Django
Web Development with Python and Django
 
Django In Depth
Django In DepthDjango In Depth
Django In Depth
 
Django Admin (Python meeutp)
Django Admin (Python meeutp)Django Admin (Python meeutp)
Django Admin (Python meeutp)
 
Free django
Free djangoFree django
Free django
 
Django Introduction & Tutorial
Django Introduction & TutorialDjango Introduction & Tutorial
Django Introduction & Tutorial
 
(Self) Publishing
(Self) Publishing(Self) Publishing
(Self) Publishing
 
Profile.e (yuki sato)0529
Profile.e (yuki sato)0529Profile.e (yuki sato)0529
Profile.e (yuki sato)0529
 
Status Www Undersøgelse Udenpix
Status Www Undersøgelse UdenpixStatus Www Undersøgelse Udenpix
Status Www Undersøgelse Udenpix
 
2012 Email Evolution Sneak Peak Webinar: Part 1
2012 Email Evolution Sneak Peak Webinar: Part 12012 Email Evolution Sneak Peak Webinar: Part 1
2012 Email Evolution Sneak Peak Webinar: Part 1
 
Tarea 1 e busines
Tarea 1 e businesTarea 1 e busines
Tarea 1 e busines
 

Similaire à Advanced Django

Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и DjangoMoscowDjango
 
Pruebas unitarias con django
Pruebas unitarias con djangoPruebas unitarias con django
Pruebas unitarias con djangoTomás Henríquez
 
Testing My Patience
Testing My PatienceTesting My Patience
Testing My PatienceAdam Lowry
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everythingnoelrap
 
Django in the Office: Get Your Admin for Nothing and Your SQL for Free
Django in the Office: Get Your Admin for Nothing and Your SQL for FreeDjango in the Office: Get Your Admin for Nothing and Your SQL for Free
Django in the Office: Get Your Admin for Nothing and Your SQL for FreeHarvard Web Working Group
 
Python testing using mock and pytest
Python testing using mock and pytestPython testing using mock and pytest
Python testing using mock and pytestSuraj Deshmukh
 
Creating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonCreating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonSiddhi
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Tsuyoshi Yamamoto
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Librariesjeresig
 
Mock it right! A beginner’s guide to world of tests and mocks, Maciej Polańczyk
Mock it right! A beginner’s guide to world of tests and mocks, Maciej PolańczykMock it right! A beginner’s guide to world of tests and mocks, Maciej Polańczyk
Mock it right! A beginner’s guide to world of tests and mocks, Maciej PolańczykPôle Systematic Paris-Region
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxMichelangelo van Dam
 

Similaire à Advanced Django (20)

Тестирование и Django
Тестирование и DjangoТестирование и Django
Тестирование и Django
 
Pruebas unitarias con django
Pruebas unitarias con djangoPruebas unitarias con django
Pruebas unitarias con django
 
Testing My Patience
Testing My PatienceTesting My Patience
Testing My Patience
 
Django (Web Konferencia 2009)
Django (Web Konferencia 2009)Django (Web Konferencia 2009)
Django (Web Konferencia 2009)
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Django in the Office: Get Your Admin for Nothing and Your SQL for Free
Django in the Office: Get Your Admin for Nothing and Your SQL for FreeDjango in the Office: Get Your Admin for Nothing and Your SQL for Free
Django in the Office: Get Your Admin for Nothing and Your SQL for Free
 
Auto testing!
Auto testing!Auto testing!
Auto testing!
 
Python testing using mock and pytest
Python testing using mock and pytestPython testing using mock and pytest
Python testing using mock and pytest
 
Creating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonCreating Domain Specific Languages in Python
Creating Domain Specific Languages in Python
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Libraries
 
Mock it right! A beginner’s guide to world of tests and mocks, Maciej Polańczyk
Mock it right! A beginner’s guide to world of tests and mocks, Maciej PolańczykMock it right! A beginner’s guide to world of tests and mocks, Maciej Polańczyk
Mock it right! A beginner’s guide to world of tests and mocks, Maciej Polańczyk
 
What's new in Django 1.2?
What's new in Django 1.2?What's new in Django 1.2?
What's new in Django 1.2?
 
How to fake_properly
How to fake_properlyHow to fake_properly
How to fake_properly
 
Why Our Code Smells
Why Our Code SmellsWhy Our Code Smells
Why Our Code Smells
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Django quickstart
Django quickstartDjango quickstart
Django quickstart
 
Unit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBeneluxUnit testing with zend framework PHPBenelux
Unit testing with zend framework PHPBenelux
 
Spsl vi unit final
Spsl vi unit finalSpsl vi unit final
Spsl vi unit final
 
Spsl v unit - final
Spsl v unit - finalSpsl v unit - final
Spsl v unit - final
 

Plus de Simon Willison

Cheap tricks for startups
Cheap tricks for startupsCheap tricks for startups
Cheap tricks for startupsSimon Willison
 
The Django Web Framework (EuroPython 2006)
The Django Web Framework (EuroPython 2006)The Django Web Framework (EuroPython 2006)
The Django Web Framework (EuroPython 2006)Simon Willison
 
How we bootstrapped Lanyrd using Twitter's social graph
How we bootstrapped Lanyrd using Twitter's social graphHow we bootstrapped Lanyrd using Twitter's social graph
How we bootstrapped Lanyrd using Twitter's social graphSimon Willison
 
Web Services for Fun and Profit
Web Services for Fun and ProfitWeb Services for Fun and Profit
Web Services for Fun and ProfitSimon Willison
 
Tricks & challenges developing a large Django application
Tricks & challenges developing a large Django applicationTricks & challenges developing a large Django application
Tricks & challenges developing a large Django applicationSimon Willison
 
Advanced Aspects of the Django Ecosystem: Haystack, Celery & Fabric
Advanced Aspects of the Django Ecosystem: Haystack, Celery & FabricAdvanced Aspects of the Django Ecosystem: Haystack, Celery & Fabric
Advanced Aspects of the Django Ecosystem: Haystack, Celery & FabricSimon Willison
 
How Lanyrd uses Twitter
How Lanyrd uses TwitterHow Lanyrd uses Twitter
How Lanyrd uses TwitterSimon Willison
 
Building Things Fast - and getting approval
Building Things Fast - and getting approvalBuilding Things Fast - and getting approval
Building Things Fast - and getting approvalSimon Willison
 
Rediscovering JavaScript: The Language Behind The Libraries
Rediscovering JavaScript: The Language Behind The LibrariesRediscovering JavaScript: The Language Behind The Libraries
Rediscovering JavaScript: The Language Behind The LibrariesSimon Willison
 
Building crowdsourcing applications
Building crowdsourcing applicationsBuilding crowdsourcing applications
Building crowdsourcing applicationsSimon Willison
 
Evented I/O based web servers, explained using bunnies
Evented I/O based web servers, explained using bunniesEvented I/O based web servers, explained using bunnies
Evented I/O based web servers, explained using bunniesSimon Willison
 
Cowboy development with Django
Cowboy development with DjangoCowboy development with Django
Cowboy development with DjangoSimon Willison
 
Crowdsourcing with Django
Crowdsourcing with DjangoCrowdsourcing with Django
Crowdsourcing with DjangoSimon Willison
 
Web App Security Horror Stories
Web App Security Horror StoriesWeb App Security Horror Stories
Web App Security Horror StoriesSimon Willison
 
Web Security Horror Stories
Web Security Horror StoriesWeb Security Horror Stories
Web Security Horror StoriesSimon Willison
 
When Zeppelins Ruled The Earth
When Zeppelins Ruled The EarthWhen Zeppelins Ruled The Earth
When Zeppelins Ruled The EarthSimon Willison
 
When Ajax Attacks! Web application security fundamentals
When Ajax Attacks! Web application security fundamentalsWhen Ajax Attacks! Web application security fundamentals
When Ajax Attacks! Web application security fundamentalsSimon Willison
 

Plus de Simon Willison (20)

How Lanyrd does Geo
How Lanyrd does GeoHow Lanyrd does Geo
How Lanyrd does Geo
 
Cheap tricks for startups
Cheap tricks for startupsCheap tricks for startups
Cheap tricks for startups
 
The Django Web Framework (EuroPython 2006)
The Django Web Framework (EuroPython 2006)The Django Web Framework (EuroPython 2006)
The Django Web Framework (EuroPython 2006)
 
Building Lanyrd
Building LanyrdBuilding Lanyrd
Building Lanyrd
 
How we bootstrapped Lanyrd using Twitter's social graph
How we bootstrapped Lanyrd using Twitter's social graphHow we bootstrapped Lanyrd using Twitter's social graph
How we bootstrapped Lanyrd using Twitter's social graph
 
Web Services for Fun and Profit
Web Services for Fun and ProfitWeb Services for Fun and Profit
Web Services for Fun and Profit
 
Tricks & challenges developing a large Django application
Tricks & challenges developing a large Django applicationTricks & challenges developing a large Django application
Tricks & challenges developing a large Django application
 
Advanced Aspects of the Django Ecosystem: Haystack, Celery & Fabric
Advanced Aspects of the Django Ecosystem: Haystack, Celery & FabricAdvanced Aspects of the Django Ecosystem: Haystack, Celery & Fabric
Advanced Aspects of the Django Ecosystem: Haystack, Celery & Fabric
 
How Lanyrd uses Twitter
How Lanyrd uses TwitterHow Lanyrd uses Twitter
How Lanyrd uses Twitter
 
ScaleFail
ScaleFailScaleFail
ScaleFail
 
Building Things Fast - and getting approval
Building Things Fast - and getting approvalBuilding Things Fast - and getting approval
Building Things Fast - and getting approval
 
Rediscovering JavaScript: The Language Behind The Libraries
Rediscovering JavaScript: The Language Behind The LibrariesRediscovering JavaScript: The Language Behind The Libraries
Rediscovering JavaScript: The Language Behind The Libraries
 
Building crowdsourcing applications
Building crowdsourcing applicationsBuilding crowdsourcing applications
Building crowdsourcing applications
 
Evented I/O based web servers, explained using bunnies
Evented I/O based web servers, explained using bunniesEvented I/O based web servers, explained using bunnies
Evented I/O based web servers, explained using bunnies
 
Cowboy development with Django
Cowboy development with DjangoCowboy development with Django
Cowboy development with Django
 
Crowdsourcing with Django
Crowdsourcing with DjangoCrowdsourcing with Django
Crowdsourcing with Django
 
Web App Security Horror Stories
Web App Security Horror StoriesWeb App Security Horror Stories
Web App Security Horror Stories
 
Web Security Horror Stories
Web Security Horror StoriesWeb Security Horror Stories
Web Security Horror Stories
 
When Zeppelins Ruled The Earth
When Zeppelins Ruled The EarthWhen Zeppelins Ruled The Earth
When Zeppelins Ruled The Earth
 
When Ajax Attacks! Web application security fundamentals
When Ajax Attacks! Web application security fundamentalsWhen Ajax Attacks! Web application security fundamentals
When Ajax Attacks! Web application security fundamentals
 

Dernier

Michael Vidyakin: Introduction to PMO (UA)
Michael Vidyakin: Introduction to PMO (UA)Michael Vidyakin: Introduction to PMO (UA)
Michael Vidyakin: Introduction to PMO (UA)Lviv Startup Club
 
AMAZON SELLER VIRTUAL ASSISTANT PRODUCT RESEARCH .pdf
AMAZON SELLER VIRTUAL ASSISTANT PRODUCT RESEARCH .pdfAMAZON SELLER VIRTUAL ASSISTANT PRODUCT RESEARCH .pdf
AMAZON SELLER VIRTUAL ASSISTANT PRODUCT RESEARCH .pdfJohnCarloValencia4
 
Borderless Access - Global Panel book-unlock 2024
Borderless Access - Global Panel book-unlock 2024Borderless Access - Global Panel book-unlock 2024
Borderless Access - Global Panel book-unlock 2024Borderless Access
 
Project Brief & Information Architecture Report
Project Brief & Information Architecture ReportProject Brief & Information Architecture Report
Project Brief & Information Architecture Reportamberjiles31
 
Cracking the ‘Business Process Outsourcing’ Code Main.pptx
Cracking the ‘Business Process Outsourcing’ Code Main.pptxCracking the ‘Business Process Outsourcing’ Code Main.pptx
Cracking the ‘Business Process Outsourcing’ Code Main.pptxWorkforce Group
 
Introduction to The overview of GAAP LO 1-5.pptx
Introduction to The overview of GAAP LO 1-5.pptxIntroduction to The overview of GAAP LO 1-5.pptx
Introduction to The overview of GAAP LO 1-5.pptxJemalSeid25
 
Live-Streaming in the Music Industry Webinar
Live-Streaming in the Music Industry WebinarLive-Streaming in the Music Industry Webinar
Live-Streaming in the Music Industry WebinarNathanielSchmuck
 
Q2 2024 APCO Geopolitical Radar - The Global Operating Environment for Business
Q2 2024 APCO Geopolitical Radar - The Global Operating Environment for BusinessQ2 2024 APCO Geopolitical Radar - The Global Operating Environment for Business
Q2 2024 APCO Geopolitical Radar - The Global Operating Environment for BusinessAPCO
 
MC Heights construction company in Jhang
MC Heights construction company in JhangMC Heights construction company in Jhang
MC Heights construction company in Jhangmcgroupjeya
 
A flour, rice and Suji company in Jhang.
A flour, rice and Suji company in Jhang.A flour, rice and Suji company in Jhang.
A flour, rice and Suji company in Jhang.mcshagufta46
 
Plano de marketing- inglês em formato ppt
Plano de marketing- inglês  em formato pptPlano de marketing- inglês  em formato ppt
Plano de marketing- inglês em formato pptElizangelaSoaresdaCo
 
Borderless Access - Global B2B Panel book-unlock 2024
Borderless Access - Global B2B Panel book-unlock 2024Borderless Access - Global B2B Panel book-unlock 2024
Borderless Access - Global B2B Panel book-unlock 2024Borderless Access
 
BCE24 | Virtual Brand Ambassadors: Making Brands Personal - John Meulemans
BCE24 | Virtual Brand Ambassadors: Making Brands Personal - John MeulemansBCE24 | Virtual Brand Ambassadors: Making Brands Personal - John Meulemans
BCE24 | Virtual Brand Ambassadors: Making Brands Personal - John MeulemansBBPMedia1
 
Anyhr.io | Presentation HR&Recruiting agency
Anyhr.io | Presentation HR&Recruiting agencyAnyhr.io | Presentation HR&Recruiting agency
Anyhr.io | Presentation HR&Recruiting agencyHanna Klim
 
7movierulz.uk
7movierulz.uk7movierulz.uk
7movierulz.ukaroemirsr
 
PDT 88 - 4 million seed - Seed - Protecto.pdf
PDT 88 - 4 million seed - Seed - Protecto.pdfPDT 88 - 4 million seed - Seed - Protecto.pdf
PDT 88 - 4 million seed - Seed - Protecto.pdfHajeJanKamps
 
Intellectual Property Licensing Examples
Intellectual Property Licensing ExamplesIntellectual Property Licensing Examples
Intellectual Property Licensing Examplesamberjiles31
 
Harvard Business Review.pptx | Navigating Labor Unrest (March-April 2024)
Harvard Business Review.pptx | Navigating Labor Unrest (March-April 2024)Harvard Business Review.pptx | Navigating Labor Unrest (March-April 2024)
Harvard Business Review.pptx | Navigating Labor Unrest (March-April 2024)tazeenaila12
 
HELENE HECKROTTE'S PROFESSIONAL PORTFOLIO.pptx
HELENE HECKROTTE'S PROFESSIONAL PORTFOLIO.pptxHELENE HECKROTTE'S PROFESSIONAL PORTFOLIO.pptx
HELENE HECKROTTE'S PROFESSIONAL PORTFOLIO.pptxHelene Heckrotte
 
Developing Coaching Skills: Mine, Yours, Ours
Developing Coaching Skills: Mine, Yours, OursDeveloping Coaching Skills: Mine, Yours, Ours
Developing Coaching Skills: Mine, Yours, OursKaiNexus
 

Dernier (20)

Michael Vidyakin: Introduction to PMO (UA)
Michael Vidyakin: Introduction to PMO (UA)Michael Vidyakin: Introduction to PMO (UA)
Michael Vidyakin: Introduction to PMO (UA)
 
AMAZON SELLER VIRTUAL ASSISTANT PRODUCT RESEARCH .pdf
AMAZON SELLER VIRTUAL ASSISTANT PRODUCT RESEARCH .pdfAMAZON SELLER VIRTUAL ASSISTANT PRODUCT RESEARCH .pdf
AMAZON SELLER VIRTUAL ASSISTANT PRODUCT RESEARCH .pdf
 
Borderless Access - Global Panel book-unlock 2024
Borderless Access - Global Panel book-unlock 2024Borderless Access - Global Panel book-unlock 2024
Borderless Access - Global Panel book-unlock 2024
 
Project Brief & Information Architecture Report
Project Brief & Information Architecture ReportProject Brief & Information Architecture Report
Project Brief & Information Architecture Report
 
Cracking the ‘Business Process Outsourcing’ Code Main.pptx
Cracking the ‘Business Process Outsourcing’ Code Main.pptxCracking the ‘Business Process Outsourcing’ Code Main.pptx
Cracking the ‘Business Process Outsourcing’ Code Main.pptx
 
Introduction to The overview of GAAP LO 1-5.pptx
Introduction to The overview of GAAP LO 1-5.pptxIntroduction to The overview of GAAP LO 1-5.pptx
Introduction to The overview of GAAP LO 1-5.pptx
 
Live-Streaming in the Music Industry Webinar
Live-Streaming in the Music Industry WebinarLive-Streaming in the Music Industry Webinar
Live-Streaming in the Music Industry Webinar
 
Q2 2024 APCO Geopolitical Radar - The Global Operating Environment for Business
Q2 2024 APCO Geopolitical Radar - The Global Operating Environment for BusinessQ2 2024 APCO Geopolitical Radar - The Global Operating Environment for Business
Q2 2024 APCO Geopolitical Radar - The Global Operating Environment for Business
 
MC Heights construction company in Jhang
MC Heights construction company in JhangMC Heights construction company in Jhang
MC Heights construction company in Jhang
 
A flour, rice and Suji company in Jhang.
A flour, rice and Suji company in Jhang.A flour, rice and Suji company in Jhang.
A flour, rice and Suji company in Jhang.
 
Plano de marketing- inglês em formato ppt
Plano de marketing- inglês  em formato pptPlano de marketing- inglês  em formato ppt
Plano de marketing- inglês em formato ppt
 
Borderless Access - Global B2B Panel book-unlock 2024
Borderless Access - Global B2B Panel book-unlock 2024Borderless Access - Global B2B Panel book-unlock 2024
Borderless Access - Global B2B Panel book-unlock 2024
 
BCE24 | Virtual Brand Ambassadors: Making Brands Personal - John Meulemans
BCE24 | Virtual Brand Ambassadors: Making Brands Personal - John MeulemansBCE24 | Virtual Brand Ambassadors: Making Brands Personal - John Meulemans
BCE24 | Virtual Brand Ambassadors: Making Brands Personal - John Meulemans
 
Anyhr.io | Presentation HR&Recruiting agency
Anyhr.io | Presentation HR&Recruiting agencyAnyhr.io | Presentation HR&Recruiting agency
Anyhr.io | Presentation HR&Recruiting agency
 
7movierulz.uk
7movierulz.uk7movierulz.uk
7movierulz.uk
 
PDT 88 - 4 million seed - Seed - Protecto.pdf
PDT 88 - 4 million seed - Seed - Protecto.pdfPDT 88 - 4 million seed - Seed - Protecto.pdf
PDT 88 - 4 million seed - Seed - Protecto.pdf
 
Intellectual Property Licensing Examples
Intellectual Property Licensing ExamplesIntellectual Property Licensing Examples
Intellectual Property Licensing Examples
 
Harvard Business Review.pptx | Navigating Labor Unrest (March-April 2024)
Harvard Business Review.pptx | Navigating Labor Unrest (March-April 2024)Harvard Business Review.pptx | Navigating Labor Unrest (March-April 2024)
Harvard Business Review.pptx | Navigating Labor Unrest (March-April 2024)
 
HELENE HECKROTTE'S PROFESSIONAL PORTFOLIO.pptx
HELENE HECKROTTE'S PROFESSIONAL PORTFOLIO.pptxHELENE HECKROTTE'S PROFESSIONAL PORTFOLIO.pptx
HELENE HECKROTTE'S PROFESSIONAL PORTFOLIO.pptx
 
Developing Coaching Skills: Mine, Yours, Ours
Developing Coaching Skills: Mine, Yours, OursDeveloping Coaching Skills: Mine, Yours, Ours
Developing Coaching Skills: Mine, Yours, Ours
 

Advanced Django

  • 2. Today’s topics Unit testing newforms Ajax And if we have time... OpenID
  • 4. Hard core TDD Test Driven Development as taught by Kent Beck “Write new code only if an automated test has failed” Revolutionises the way you write code Pretty hard to adopt
  • 5. I don’t do test driven development. I do stupidity driven testing... I wait until I do something stupid, and then write ” tests to avoid doing it again. Titus Brown
  • 6. What NOT to do No matter how tough the deadline, don't let your test suite start breaking and say “I’ll fix it later” This will hurt. A lot.
  • 7. Testing in Django Testing web apps is HARD, but Django helps out with a bunch of features: Fixtures Doctests Test Client E-mail capture
  • 8. Doctests Used by Django for both ORM testing and generated documentation You are encouraged to add them to your own models.py manage.py test to execute them Great for regression tests - just copy and paste from an interpreter session
  • 9. >>> p = Person(name=quot;Bobquot;, dob=date(1980, 1, 1)) >>> p.age(date(1980, 1, 1)) 0 >>> p.age(date(1979, 1, 1)) -1 >>> p.age(date(1981, 6, 1)) 1 >>> p.age(date(2000, 1, 1)) 20 >>> p.age(date(1999, 12, 31)) 19 >>> p2 = Person(name=quot;Barryquot;, dob=date(1981, 5, 5)) >>> p2.age(date(1981, 5, 5)) 0 >>> p2.age(date(1982, 5, 4)) 0 >>> p2.age(date(1982, 5, 5)) 1 >>> p2.age(date(1982, 5, 6)) 1
  • 10. class Person(models.Model): quot;quot;quot; ... tests here ... quot;quot;quot; name = models.CharField(maxlength=100) dob = models.DateField() def age(self, age=False): return 1
  • 11. $ python manage.py test ...F ====================================================================== FAIL: Doctest: peopleage.models.Person ---------------------------------------------------------------------- Traceback (most recent call last): File quot;/usr/lib/python2.5/site-packages/django/test/_doctest.pyquot;, line 2161, in runTest raise self.failureException(self.format_failure(new.getvalue())) AssertionError: Failed doctest test for peopleage.models.Person File quot;/home/simon/Projects/Django/oscon07/peopleage/models.pyquot;, line 4, in Person ---------------------------------------------------------------------- File quot;/home/simon/Projects/Django/oscon07/peopleage/models.pyquot;, line 7, in peopleage.models.Person Failed example: p.age(date(1980, 1, 1)) Expected: 0 Got: 1
  • 12. def age(self, age=False): if not age: age = date.today() delta = age - self.dob return int(math.floor(delta.days / float(365)))
  • 13. File quot;/home/simon/Projects/Django/oscon07/peopleage/models.pyquot;, line 16, in peopleage.models.Person Failed example: p.age(date(1999, 12, 31)) Expected: 19 Got: 20
  • 14. def age(self, age=False): if not age: age = date.today() years = age.year - self.dob.year this_year_birthday = self.dob.replace(year=age.year) birthday_has_passed = age >= this_year_birthday if not birthday_has_passed: years = years - 1 return years
  • 15. Fixtures It’s useful to be able to clear and populate your test database in between tests Fixtures let you do that (they also let you populate your database with real data when you deploy your application)
  • 17. “ Normalised data is for sissies ” Cal Henderson
  • 18. A forum, where each thread can have one or more replies Maintain a separate counter in the Forum table of number of replies, to speed up queries
  • 19. class Thread(models.Model): subject = models.CharField(maxlength=100) num_replies = models.IntegerField(default=0) class Reply(models.Model): thread = models.ForeignKey(Thread) message = models.TextField()
  • 20. [{ quot;modelquot;: quot;forum.threadquot;, quot;pkquot;: quot;1quot;, quot;fieldsquot;: { quot;num_repliesquot;: 0, quot;subjectquot;: quot;First threadquot; } },{ quot;modelquot;: quot;forum.threadquot;, quot;pkquot;: quot;2quot;, quot;fieldsquot;: { quot;num_repliesquot;: 1, quot;subjectquot;: quot;Second threadquot; } },{ quot;modelquot;: quot;forum.replyquot;, quot;pkquot;: quot;1quot;, quot;fieldsquot;: { quot;threadquot;: 2, quot;messagequot;: quot;First post!1quot; } }]
  • 21. from django.test import TestCase from models import Thread, Reply class ThreadCountTestCase(TestCase): fixtures = ['threadcount.json'] def test_add_reply(self): thread = Thread.objects.get(pk=2) self.assertEqual(thread.num_replies, 1) thread.reply_set.create(message=quot;Another postquot;) thread = Thread.objects.get(pk=2) self.assertEqual(thread.reply_set.count(), 2) self.assertEqual(thread.num_replies, 2) def test_delete_reply(self): thread = Thread.objects.get(pk=2) self.assertEqual(thread.num_replies, 1) Reply.objects.get(pk=1).delete() thread = Thread.objects.get(pk=2) self.assertEqual(thread.reply_set.count(), 0) self.assertEqual(thread.num_replies, 0)
  • 22. ====================================================== FAIL: test_add_reply (forum.tests.ThreadCountTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File quot;/home/simon/Projects/Django/oscon07/forum/tests.pyquot;, line 16, in test_add_reply self.assertEqual(thread.num_replies, 2) AssertionError: 1 != 2 ====================================================== FAIL: test_delete_reply (forum.tests.ThreadCountTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File quot;/home/simon/Projects/Django/oscon07/forum/tests.pyquot;, line 23, in test_delete_reply self.assertEqual(thread.num_replies, 0) AssertionError: 1 != 0 ----------------------------------------------------------------------
  • 23. class Reply(models.Model): ... def save(self): super(Reply, self).save() self.thread.num_replies = self.thread.reply_set.count() self.thread.save() def delete(self): super(Reply, self).delete() self.thread.num_replies = self.thread.reply_set.count() self.thread.save()
  • 25. Testing views Django’s TestClient lets you simulate a browser interacting with your site It also provides hooks in to the underlying application, so you can test against the template and context that was used to generate a page
  • 26. from django.test import TestCase class RegistrationTest(TestCase): def test_slash(self): response = self.client.get('/register') self.assertEqual(response.status_code, 301) response = self.client.get('/register/') self.assertEqual(response.status_code, 200) def test_register(self): response = self.client.get('/register/') self.assertEqual(response.template[0].name, 'register.html') self.assertTemplateUsed(response, 'register.html')
  • 27. def test_signup_done_page(self): self.assertEqual(len(mail.outbox), 0) data = { 'email': 'simon@example.com', 'username': 'example', 'firstname': 'Example', 'lastname': 'User', 'password': 'password1', 'password2': 'password1', } response = self.client.post('/signup/', data) self.assertEquals(response.status_code, 302) self.assertEquals(response['Location'], '/welcome/') # Check that the confirmation e-mail was sent self.assertEqual(len(mail.outbox), 1) sent = mail.outbox[0] self.assertEqual(sent.subject, 'Welcome to example.com')
  • 28. More on testing with Django: http://www.djangoproject.com/documentation/testing/
  • 30. The perfect form Display a form User fills it in and submits it Validate their entered data If errors, redisplay form with previously entered data and contextual error messages Continue until their submission is valid Convert submission to appropriate Python types
  • 33. Forms are declarative from django import newforms as forms class UserProfileForm(forms.Form): name = forms.CharField(max_length=100) email = forms.EmailField() bio = forms.CharField(widget=forms.Textarea) dob = forms.DateField(required=False) receive_newsletter = forms.BooleanField(required=False)
  • 34. A form instance can... validate a set of data against itself render itself (and its widgets) re-render itself with errors convert to Python types
  • 35. Simple form handling view def create_profile(request): if request.method == 'POST': form = UserProfileForm(request.POST) if form.is_valid(): # ... save the user’s profile return HttpResponseRedirect('/profile/saved/') else: form = UserProfileForm() return render_to_response('profile.html', {'form': form})
  • 36. Initial data form = UserProfileForm( initial = { 'name': 'Simon Willison', 'email': 'simon@simonwillison.net', } )
  • 37. Simple template <style type=quot;text/cssquot;> ul.errorlist { color: red; } </style> ... <form action=quot;/profile/create/quot; method=quot;POSTquot;> {{ form.as_p }} <input type=quot;submitquot; value=quot;Submitquot; /> </form> ...
  • 38. Output <form action=quot;/profile/create/quot; method=quot;POSTquot;> <p><label for=quot;id_namequot;>Name:</label> <input id=quot;id_namequot; type=quot;textquot; name=quot;namequot; maxlength=quot;100quot; /></p> <p><label for=quot;id_emailquot;>Email:</label> <input type=quot;textquot; name=quot;emailquot; id=quot;id_emailquot; /></p> <p><label for=quot;id_bioquot;>Bio:</label> <textarea id=quot;id_bioquot; rows=quot;10quot; cols=quot;40quot; name=quot;bioquot;></textarea></p> <p><label for=quot;id_dobquot;>Dob:</label> <input type=quot;textquot; name=quot;dobquot; id=quot;id_dobquot; /></p> <p><label for=quot;id_receive_newsletterquot;>Receive newsletter:</label> <input type=quot;checkboxquot; name=quot;receive_newsletterquot; id=quot;id_receive_newsletterquot; /></p> <input type=quot;submitquot; value=quot;Submitquot; /> </form>
  • 39. Output <form action=quot;/profile/create/quot; method=quot;POSTquot;> <p><label for=quot;id_namequot;>Name:</label> <input id=quot;id_namequot; type=quot;textquot; name=quot;namequot; maxlength=quot;100quot; /></p> <p><label for=quot;id_emailquot;>Email:</label> <input type=quot;textquot; name=quot;emailquot; id=quot;id_emailquot; /></p> <p><label for=quot;id_bioquot;>Bio:</label> <textarea id=quot;id_bioquot; rows=quot;10quot; cols=quot;40quot; name=quot;bioquot;></textarea></p> <p><label for=quot;id_dobquot;>Dob:</label> <input type=quot;textquot; name=quot;dobquot; id=quot;id_dobquot; /></p> <p><label for=quot;id_receive_newsletterquot;>Receive newsletter:</label> <input type=quot;checkboxquot; name=quot;receive_newsletterquot; id=quot;id_receive_newsletterquot; /></p> <input type=quot;submitquot; value=quot;Submitquot; /> </form>
  • 40. Output <form action=quot;/profile/create/quot; method=quot;POSTquot;> <p><label for=quot;id_namequot;>Name:</label> <input id=quot;id_namequot; type=quot;textquot; name=quot;namequot; maxlength=quot;100quot; /></p> <p><label for=quot;id_emailquot;>Email:</label> <input type=quot;textquot; name=quot;emailquot; id=quot;id_emailquot; /></p> <p><label for=quot;id_bioquot;>Bio:</label> <textarea id=quot;id_bioquot; rows=quot;10quot; cols=quot;40quot; name=quot;bioquot;></textarea></p> <p><label for=quot;id_dobquot;>Dob:</label> <input type=quot;textquot; name=quot;dobquot; id=quot;id_dobquot; /></p> <p><label for=quot;id_receive_newsletterquot;>Receive newsletter:</label> <input type=quot;checkboxquot; name=quot;receive_newsletterquot; id=quot;id_receive_newsletterquot; /></p> <input type=quot;submitquot; value=quot;Submitquot; /> </form>
  • 41. Custom validation from django import newforms as forms from django.newforms.util import ValidationError class UserProfileForm(forms.Form): name = forms.CharField(max_length=100) email = forms.EmailField() bio = forms.CharField(widget=forms.Textarea) dob = forms.DateField(required=False) receive_newsletter = forms.BooleanField(required=False) def clean_email(self): if self.cleaned_data['email'].split('@')[1] == 'hotmail.com': raise ValidationError, quot;No hotmail.com emails, please.quot;
  • 42. Custom validation from django import newforms as forms from django.newforms.util import ValidationError class UserProfileForm(forms.Form): name = forms.CharField(max_length=100) email = forms.EmailField() bio = forms.CharField(widget=forms.Textarea) dob = forms.DateField(required=False) receive_newsletter = forms.BooleanField(required=False) def clean_email(self): if self.cleaned_data['email'].split('@')[1] == 'hotmail.com': raise ValidationError, quot;No hotmail.com emails, please.quot;
  • 43. Custom rendering <ol class=quot;formItems longFormquot;> <li{% if form.email.errors %} class=quot;errorsquot;{% endif %}> <label for=quot;id_emailquot;>Email: </label> {{ form.email }} {{ form.email.errors }} <p class=quot;infoquot;>Your e-mail address.</p> </li> ... </ol>
  • 44. Model shortcuts DRY: You’ve already declared your models; you shouldn’t have to repeat yourself in your forms UserForm = form_for_model(User) ############################### page = Page.objects.get(pk=1) PageForm = form_for_instance(page) form = PageForm(request.POST) ... if form.is_valid(): form.save()
  • 45. Full documentation: http://www.djangoproject.com/documentation/newforms/ django/trunk/tests/regressiontests/forms/tests.py
  • 46. Ajax
  • 47. First things first If you're going to do Ajax, you need a JavaScript library You could use Yet Another XMLHttpRequest abstraction... but the popular libraries offer fantastic convenience Good libraries include YUI, Dojo, MochiKit and (controversial) Prototype...
  • 48. .. and jQuery I'm going to be using jQuery Almost everything is done in terms of CSS selectors and chained methods It looks like a gimmick, but it isn't http://simonwillison.net/2007/Aug/15/jquery/
  • 49. Ajax formats... Django has great support for any and every Ajax format HTML fragments XML JSON
  • 50. Username available? from django.contrib.auth.models import User def check_username(request): reply = quot;quot; username = request.GET.get('username', '') if username: if User.objects.filter(username=username).count(): reply = 'Unavailable' else: reply = 'Available' return HttpResponse(reply)
  • 51. jQuery('span#msg').load( '/check_username/?username=' + input.val() );
  • 52. $('span#msg').load( '/check_username/?username=' + input.val() );
  • 53. var input = $('input#id_username') input.keyup(function() { $('span#msg').load( '/check_username/?username=' + input.val() ); });
  • 54. $(document).ready(function() { var input = $('input#id_username') input.keyup(function() { $('span#msg').load( '/check_username/?username=' + input.val() ); }); });
  • 55. $(function() { var input = $('input#id_username') input.keyup(function() { $('span#msg').load( '/check_username/?username=' + input.val() ); }); });
  • 56. Recycling server-side form validation
  • 57. from django import newforms as forms class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField(widget=forms.Textarea()) sender = forms.EmailField()
  • 58. def validate_contact(request): quot;Validate post data, return errors as jsonquot; form = ContactForm(request.POST) if (request.GET.has_key('field')): # Validate a single field errors = form.errors[request.GET['field']] else: errors = form.errors return JsonResponse({ 'valid': not errors, 'errors': errors })
  • 59. from django.utils import simplejson class JsonResponse(HttpResponse): def __init__(self, data): HttpResponse.__init__( self, simplejson.dumps(data), mimetype='application/json' )
  • 60. function validateInput(input) { $.post('/contact/validate/?field=' + input.attr('id').replace('id_', ''), $('form').formToArray(), function(data) { var json = eval('(' + data + ')'); showErrors(input, json.errors); } ); } $(function() { $(':input').blur(function() { validateInput($(this)); }); });
  • 61. function relatedErrorList(input) { var prevUL = $(input).parent().prev(); if (prevUL && prevUL.attr('class') == 'errorlist') { return prevUL; } var errorlist = $('<ul class=quot;errorlistquot;></ul>'); input.parent().before(errorlist); return errorlist; } function showErrors(input, errors) { var errorlist = relatedErrorList(input); errorlist.empty(); $.each(errors, function(i, error) { errorlist.append('<li>' + error + '</li>'); }); }
  • 62. Django philosophy Django often gets marked down in “framework comparisons” due to the lack of built in Ajax support Personally I think that shipping without a recommended library is a feature, not a bug
  • 65. OpenID is a decentralised mechanism for Single Sign On
  • 66. An OpenID is a URL http://simonwillison.myopenid.com/ http://simonwillison.net/ http://swillison.livejournal.com/ http://openid.aol.com/simonwillison
  • 67. How it works You enter your OpenID on a site (instead of the usual username and password) It redirects you back to your OpenID provider They authenticate you in some way They redirect you back to the original site
  • 68. Simple registration (an optional but useful extension)
  • 69. Consumers can also ask... Your preferred username Your e-mail address Your first and last name Your date of birth Your language, country and timezone
  • 70. How do you use OpenID in a Django application?
  • 71. The hard way Use the JanRain OpenID library Pretty much a reference implementation for the OpenID spec Well written, well tested but takes a while to get the hang of www.openidenabled.com/openid/libraries/python/
  • 72. The easy way Use django-openid A simple wrapper around JanRain A middleware component, some models and a few pre-written views http://code.google.com/p/django-openid/
  • 73. Installation Add 'django_openidconsumer' to your INSTALLED_APPS setting manage.py syncdb Add the OpenIDMiddleware to your MIDDLEWARE_CLASSES setting Add the views to your URLconf
  • 74. In urls.py ... (r'^openid/$', 'django_openidconsumer.views.begin'), (r'^openid/complete/$', 'django_openidconsumer.views.complete'), (r'^openid/signout/$', 'django_openidconsumer.views.signout'), ...
  • 75. request.openid The middleware adds an openid property to the Django request object If the user is not signed in, this will be None Otherwise, it will be an OpenID object; the str() representation will be the OpenID (or use request.openid.openid)
  • 76. def example_view(request): if request.openid: return HttpResponse(quot;OpenID is %squot; % escape(request.openid)) else: return HttpResponse(quot;No OpenIDquot;)
  • 77. request.openids The module supports users signing in with more than one OpenID at a time request.openids provides a list of all authenticated OpenIDs request.openid merely returns the most recent from this list
  • 78. For simple registration (r'^openid/$', 'django_openidconsumer.views.begin', { 'sreg': 'email,nickname' }), def example_sreg(request): if request.openid and request.openid.sreg.has_key('email'): return HttpResponse(quot;Your e-mail address is: %squot; % escape( request.openid.sreg['email'] )) else: return HttpResponse(quot;No e-mail addressquot;)
  • 79. Coming soon django_openidauth, providing tools to associate OpenIDs with existing django.contrib.auth accounts django_openidserver, to make it easy to provide OpenIDs for users of your Django application
  • 80. More information http://openid.net/ Also home to the OpenID mailing lists http://www.openidenabled.com/ http://simonwillison.net/tags/openid/ http://code.google.com/p/django-openid/