+ All Categories
Home > Documents > Code Lab - Django

Code Lab - Django

Date post: 02-Apr-2018
Category:
Upload: roulis-paparoulis
View: 233 times
Download: 0 times
Share this document with a friend

of 132

Transcript
  • 7/27/2019 Code Lab - Django

    1/132

    django

    Code Lab

    James Bennett Adrian Holovaty Jacob Kaplan-Moss

    PyCon 2008Thursday, March 13, 2008

  • 7/27/2019 Code Lab - Django

    2/132

    Introductions

  • 7/27/2019 Code Lab - Django

    3/132

    James BennettDeveloper, Lawrence Journal-World

    Release Manager, Django

    Author, Practical Django Projects

    Professional Asshole

  • 7/27/2019 Code Lab - Django

    4/132

    Adrian HolovatyFounder, EveryBlock

    BDFL, Django

    Author, The Definitive Guide to Django

    YouTube Phenomenon

  • 7/27/2019 Code Lab - Django

    5/132

    Jacob Kaplan-MossPartner, Whiskey Media

    BDFL, Django

    Author, The Definitive Guide to Django

  • 7/27/2019 Code Lab - Django

    6/132

    Schedule

    6:20 - 7:40 Code lab, part 17:40 - 8:00 Break

    8:00 - 9:00 Code lab, part 2

    9:00 - 9:30 Stump the Chumps!

  • 7/27/2019 Code Lab - Django

    7/132

    Code lab, part 1

    Pim Van Heuven: URL design

    Justin Lilly: unit testing and TDD

    Richard House: model design

    Peter Herndon: searching, optimization

    J. Clifford Dyer: next/previous links

    Dave Lowe: when not to use the admin

  • 7/27/2019 Code Lab - Django

    8/132

    Code lab, part 2

    Bob Haugen: preparing for deployment

    Wiley Kestner: signals

    Eric St-Jean: REST APIs

    Sean OConnor: order_by(?)

    Honza Krl: many questions, many answers?

  • 7/27/2019 Code Lab - Django

    9/132

    Pim van HeuvenURL design

  • 7/27/2019 Code Lab - Django

    10/132

    The case of the monsterURLConf

  • 7/27/2019 Code Lab - Django

    11/132

    urls.py

    2032 lines

    600+ URL patterns

    94 dictionaries of keyword arguments

  • 7/27/2019 Code Lab - Django

    12/132

    Worst-case matching

    Every time you try to match a regex and

    fail, it takes timeWorst case is a 404 which matchesnothing

    Has to fail over 600 regexes before it canreturn

  • 7/27/2019 Code Lab - Django

    13/132

    Lets break things up

  • 7/27/2019 Code Lab - Django

    14/132

    urlpatterns = urlpatterns + patterns('d3a.ajax.views',

    (r'^pricegroup/json/validate/$', 'validate_generic', {'form' : PricegroupForm } ),

    )

    urlpatterns = urlpatterns + patterns('ledger.views',

    (r'^pricegroup/add/$', 'create_object', pricegroup_dict),

    (r'^pricegroup/print_or_email/(?P\w+)/(?P\w*)/(?P\d+)/$',

    'print_or_email', pricegroup_dict),

    (r'^pricegroup/(?P\d+)/$', 'update_object', pricegroup_dict),

    (r'^pricegroup/(?P\d+)/delete/$', 'delete_object', dict(pricegroup_dict,

    post_delete_redirect='../../')),

    )

    urlpatterns = urlpatterns + patterns('ledger.views',

    (r'^partsvendor/$', 'object_list', partsvendor_dict),

    (r'^partsvendor/json_table/$', 'json_table', partsvendor_dict),

    )

    urlpatterns = urlpatterns + patterns('d3a.ajax.views',

    (r'^partsvendor/json/validate/$', 'validate_generic', {'form' : PartsvendorForm } ),

    )

  • 7/27/2019 Code Lab - Django

    15/132

    These are alreadylogically separate

  • 7/27/2019 Code Lab - Django

    16/132

    Split into multiple files,use include()

  • 7/27/2019 Code Lab - Django

    17/132

    Worst-case matching

    Break the URLConf up into logical bits,several levels deep

    Worst case will be a couple dozen fails

  • 7/27/2019 Code Lab - Django

    18/132

    And now for somethingcompletely different...

  • 7/27/2019 Code Lab - Django

    19/132

    Forms with extraparameters

  • 7/27/2019 Code Lab - Django

    20/132

    Use cases

    You want to pass an existing object to a

    form and derive initial data from it

    You want to pass a user into a form andhave the saved object be related to it

    etc., etc.

  • 7/27/2019 Code Lab - Django

    21/132

    Getting initial data

    For each field on the object which willhave a field in the form, get the value

    Build a dictionary of field name -> field

    value mappings

  • 7/27/2019 Code Lab - Django

    22/132

    Django can do this

    django.newforms.models.model_to_dict()

    Takes an object, and optionally lists offields to include/exclude

  • 7/27/2019 Code Lab - Django

    23/132

    obj = SomeModel.objects.get(pk=5)

    initial_data = model_to_dict(obj)

    form = MyForm(initial=initial_data)

  • 7/27/2019 Code Lab - Django

    24/132

    How about extraparameters?

  • 7/27/2019 Code Lab - Django

    25/132

    Simple pattern

    Override __init__()

    Take any extra arguments you want, plus*args and **kwargs

    Then call superclass __init__(), passing*args and **kwargs

  • 7/27/2019 Code Lab - Django

    26/132

    class MyForm(forms.Form):

    def __init__(self, user, *args, **kwargs)

    self.user = user

    super(MyForm, self).__init__(*args,

    **kwargs)

  • 7/27/2019 Code Lab - Django

    27/132

    super() is important;dont leave it out

  • 7/27/2019 Code Lab - Django

    28/132

    Other tricks

    After super() call, mess around withself.fields

    Its just a dictionary of field name -> field

    objects

  • 7/27/2019 Code Lab - Django

    29/132

    Justin LillyUnit Testing and TDD

  • 7/27/2019 Code Lab - Django

    30/132

    Tests are theProgrammers stone,

    transmuting fear intoboredom.

    Kent Beck

  • 7/27/2019 Code Lab - Django

    31/132

    Hardcore TDD

  • 7/27/2019 Code Lab - Django

    32/132

    I dont do test drivendevelopment. I do stupiditydriven testing I wait until

    I do something stupid, andthen write tests to avoiddoing it again.

    Titus Brown

  • 7/27/2019 Code Lab - Django

    33/132

    Whatever happens, dont letyour test suite break thinking,Ill go back and fix this later.

  • 7/27/2019 Code Lab - Django

    34/132

    Testing Django

    Doctests

    Unit testsFixtures

    Test client

    Email capture

  • 7/27/2019 Code Lab - Django

    35/132

    Testing Django

    Doctests

    Unit testsFixtures

    Test client

    Email capture

  • 7/27/2019 Code Lab - Django

    36/132

    """

    >>> from django.test import Client>>> c = Client()

    >>> r = c.get("/calendar/")

    >>> r.status_code200

    >>> r.context[1].date

    datetime.datetime(...)

    """

  • 7/27/2019 Code Lab - Django

    37/132

  • 7/27/2019 Code Lab - Django

    38/132

    $ ./manage.py runtests

    Ran 1 test in 0.019s

    OK

  • 7/27/2019 Code Lab - Django

    39/132

    Testing Django

    Doctests

    Unit testsFixtures

    Test client

    Email capture

  • 7/27/2019 Code Lab - Django

    40/132

    Unit tests

  • 7/27/2019 Code Lab - Django

    41/132

    from datetime import datetime

    from django.test import TestCase

    from django.template import Template, Context

    class ScheduleTestCase(TestCase):

    def setUp(self):

    template.add_to_builtins("schedule.templatetags.schedule_cal")

    def render(t, **c):return Template(t).render(Context(c))

    def testTag(self):t = "{% schedule_cal date items %}"

    c = {

    "date": datetime(2007, 1),"cal_items": [ ... ]

    }

    r = self.render(t, c)

    self.assert_("Jan 2007" in r)

  • 7/27/2019 Code Lab - Django

    42/132

    Fixtures

  • 7/27/2019 Code Lab - Django

    43/132

    class ScheduleTestCase(TestCase):

    fixtures = ["project_testdata"]

    ...

  • 7/27/2019 Code Lab - Django

    44/132

    model: projects.project

    pk: 1fields:

    name: Foo

    due: 20071001 11:59

    model: schedule.meeting

    pk: 1

    fields:

    projects: [1]

    date: 20070903 20:00

    [

  • 7/27/2019 Code Lab - Django

    45/132

    [

    {

    "model": "projects.project",

    "pk": 1,

    "fields": {

    "name": "Foo",

    "due": "20071001 11:59"

    }

    },{

    "model": "schedule.meeting",

    "pk": 1,

    "fields": {

    "projects": [1],

    "date": "20070903 20:00",

    }

    }

    ]

  • 7/27/2019 Code Lab - Django

    46/132

    Foo

    20071001 11:59

    2007093 20:00

  • 7/27/2019 Code Lab - Django

    47/132

    Doctests or Unit Tests?

  • 7/27/2019 Code Lab - Django

    48/132

    Richard HouseModel design

  • 7/27/2019 Code Lab - Django

    49/132

    We just need one more

    field...

  • 7/27/2019 Code Lab - Django

    50/132

    send_confirmatory_email = models.BooleanField(default=True,

    null=True, )telephone = models.BooleanField(default=False, null=True, )text_message = models.BooleanField(default=False, null=True, )

    address = models.BooleanField(default=False, null=True, )

    car_reg = models.BooleanField(default=False, null=True, )

    nationality = models.BooleanField(default=False, null=True, )

    dietary = models.BooleanField(default=False, null=True, )dinner = models.BooleanField(default=False, null=True, )

    company = models.BooleanField(default=False, null=True, )

    ni_number = models.BooleanField(default=False, null=True, )

    date_of_birth = models.BooleanField(default=False, null=True, )

    place_of_birth = models.BooleanField(default=False, null=True, )guests = models.BooleanField(default=False, null=True, )

  • 7/27/2019 Code Lab - Django

    51/132

    Represent this information relationally

    Dont put all the fields on all the events

    An easier way

  • 7/27/2019 Code Lab - Django

    52/132

    Two pairs of models

  • 7/27/2019 Code Lab - Django

    53/132

    First pair

    EventType encapsulates a type of event

    EventTypeOption will represent a piece ofinformation

  • 7/27/2019 Code Lab - Django

    54/132

    class EventType(models.Model):

    name = models.CharField(max_length=255)

    class EventTypeOption(models.Model):

    name = models.CharField(max_length=255)field_label = models.CharField(max_length=255)

    event_type = models.ForeignKey(EventType,

    related_name=option)

  • 7/27/2019 Code Lab - Django

    55/132

    Event encapsulates an event, and has allthe fields common to all events

    EventOption will be a piece of informationfor a specific event

    Second pair

  • 7/27/2019 Code Lab - Django

    56/132

    class Event(models.Model):

    # core fields here...

    event_type = models.ForeignKey(EventType)

    class EventOption(models.Model):

    event = models.ForeignKey(Event, related_name=option)

    option = models.ForeignKey(EventTypeOption)

    value = models.BooleanField()

  • 7/27/2019 Code Lab - Django

    57/132

    Lets build a form

  • 7/27/2019 Code Lab - Django

    58/132

    def __init__(self, event_type, *args, **kwargs):

    self.event_type = event_type

    super(EventForm, self).__init__(*args, **kwargs)for option in self.event_type.option_set.all():

    self.fields[option.name] = forms.BooleanField(label=option.field_label)

  • 7/27/2019 Code Lab - Django

    59/132

    Lets save an event

  • 7/27/2019 Code Lab - Django

    60/132

    def save(self):

    # Construct an Event object and save it first...

    for option in self.event_type.option_set.all():if option.name in self.cleaned_data:

    new_event.option_set.create(option=option,

    value=self.cleaned_data[option.name])

  • 7/27/2019 Code Lab - Django

    61/132

    Lets display an event

  • 7/27/2019 Code Lab - Django

    62/132

    {# show normal fields first #}

    {% for event_option in event.option_set.all %}

    {{ event_option.option.name }}:{{ event_option.value }}

    {% endfor %}

  • 7/27/2019 Code Lab - Django

    63/132

    Now you just add

    EventTypeOptions foreach new request

  • 7/27/2019 Code Lab - Django

    64/132

    No more schemachanges

  • 7/27/2019 Code Lab - Django

    65/132

    Yay!

  • 7/27/2019 Code Lab - Django

    66/132

    Peter HerndonSearching: query optimization, executing raw SQL, andchoosing the right tool for the job.

  • 7/27/2019 Code Lab - Django

    67/132

    The problem

  • 7/27/2019 Code Lab - Django

    68/132

    /people/jacobkaplanmoss/

  • 7/27/2019 Code Lab - Django

    69/132

    Person.objects.filter(slug="jacobkaplanmoss")

  • 7/27/2019 Code Lab - Django

    70/132

    /documents/search/?keywords=foo

  • 7/27/2019 Code Lab - Django

    71/132

    QuerySet?

    def search_all(search_data):q = Q()key_documents = Noneresults = None

  • 7/27/2019 Code Lab - Django

    72/132

    results None

    if search_data['author'] and not search_data['author'] == 'Last Name, First':# parse author names# ex: 'Norton, L' or 'Norton, L; Begg, C'authors = search_data['author'].rstrip()authors = authors.rstrip(';')authors = authors.split(';')# ex: ['Norton, L'] or ['Norton, L', ' Begg, C']authors = map(string.strip, authors)document_id_lists = []intersection_set = set()

    for author in authors:document_ids = []pubs = []name_parts = author.split(', ')if len(name_parts) == 2:

    lname, fname = name_partsemployees = Employee.objects.filter(last_name__iexact=lname,

    first_name__iexact=fname)else:

    lname = name_parts[0]employees = Employee.objects.filter(last_name__iexact=lname)

    for employee in employees:if pubs:

    pubs = pubs | employee.publication_set.all()else:

    pubs = employee.publication_set.all()for pub in pubs:

    document_ids.append(pub.document.id)document_id_lists.append(document_ids)for document_id_list in document_id_lists:

    if document_id_list:document_id_set = set(document_id_list)if intersection_set:

    intersection_set =intersection_set.intersection(document_id_set)

    else:intersection_set = document_id_set

    q = q & Q(id__in=intersection_set)

    if search_data['journal'] and not search_data['journal'] == 'Ex: Blood':journals = search_data['journal'].split(';')journals = map(string.strip, journals)print "journals:", journalsfor journal in journals:

    q = q & Q(source__name__iexact=journal)

    if search_data['dmt'] and not search_data['dmt'] == '0':q = q & Q(dmt__id__exact=search_data['dmt'])

    if (search_data['year_start'] and not search_data['year_start'] == 'BLANK') or

    (search_data['year_end'] and not search_data['year_end'] == 'BLANK'):start = search_data['year_start']end = search_data['year_end']if not start or start == 'BLANK':

    start = datetime.datetime.now().yearif not end or end == 'BLANK':

    end = datetime.datetime.now().year

    q = q & Q(publish_year__range=(start, end))

    if search_data['doc_type']:

    if isinstance(search_data['doc_type'], list):q = q & Q(document_type__in=search_data['doc_type'])

    else:q = q & Q(document_type__istartswith=search_data['doc_type'])

    if search_data['keywords'] and not search_data['keywords'] == 'Ex: melanoma':

    keywords = search_data['keywords'].split()title_documents = Noneabstract_documents = Nonekeyword_documents = None

    for word in keywords:title_docs = Document.objects.filter(title__icontains=word)abstract_docs = Document.objects.filter(abstract__icontains=word)kwords = Keyword.objects.filter(term__icontains=word)keyword_doc_ids = [kw.document.id for kw in kwords]keyword_docs = Document.objects.filter(id__in=keyword_doc_ids)if title_documents:

    title_documents = title_docs & title_documentselse:

    title_documents = title_docsif abstract_documents:

    abstract_documents = abstract_docs & abstract_documentselse:

    abstract_documents = abstract_docsif keyword_documents:

    keyword_documents = keyword_documents & keyword_docs

    else:keyword_documents = keyword_docs

    if title_documents:if key_documents:

    key_documents = key_documents | title_documentselse:

    key_documents = title_documents

    if abstract_documents:if key_documents:

    key_documents = key_documents | abstract_documentselse:

    key_documents = abstract_documentsif keyword_documents:

    if key_documents:key_documents = key_documents | keyword_documents

    else:key_documents = keyword_documents

    if key_documents:results = Document.objects.filter(q).select_related().order_by('

    publish_year') & key_documentselif (hasattr(q, 'kwargs') and q.kwargs) or (hasattr(q, 'args') and q.args):

    results = Document.objects.filter(q).select_related().order_by('publish_year')

    else:results = []

    return results

  • 7/27/2019 Code Lab - Django

    73/132

    if search_data['dmt'] and not search_data['dmt'] == '0':

    q = q & Q(dmt__id__exact=search_data['dmt'])

    ...

    results = Document.objects.filter(q)

  • 7/27/2019 Code Lab - Django

    74/132

  • 7/27/2019 Code Lab - Django

    75/132

    Why so slow?

    Opaque data structures

    Cloning QuerySets

    Inefficient SQL

    Wrong data structure

  • 7/27/2019 Code Lab - Django

    76/132

    Raw SQL

  • 7/27/2019 Code Lab - Django

    77/132

    from django import db

    sql = "SELECT FROM WHERE end = %s "

    params = [end_date, ]

    cursor = db.connection.cursor()

    cursor.execute(sql, params)

    for row in cursor:

    ...

  • 7/27/2019 Code Lab - Django

    78/132

  • 7/27/2019 Code Lab - Django

    79/132

    Linus Torvalds

    Bad programmers worryabout the code. Good

    programmers worry aboutdata structures....

  • 7/27/2019 Code Lab - Django

    80/132

    Search is asolved problem

  • 7/27/2019 Code Lab - Django

    81/132

    Tsearch2 MySQL FULLTEXT

  • 7/27/2019 Code Lab - Django

    82/132

    http://code.google.com/p/djapian/

    http://code.google.com/p/django-sphinx/

    http://code.google.com/p/djangosearch

    http://code.google.com/p/django-sphinx/http://code.google.com/p/django-sphinx/http://code.google.com/p/django-sphinx/http://code.google.com/p/djapian/http://code.google.com/p/djapian/
  • 7/27/2019 Code Lab - Django

    83/132

    from djangosearch.indexer import ModelIndex

    class Document(models.Model):

    index = ModelIndex()

  • 7/27/2019 Code Lab - Django

    84/132

    results = Document.index.search(query)

  • 7/27/2019 Code Lab - Django

    85/132

    J. Clifford DyerHandling previous/next links

  • 7/27/2019 Code Lab - Django

    86/132

    Dave LoweWhen not to use the admin

  • 7/27/2019 Code Lab - Django

    87/132

    Break

  • 7/27/2019 Code Lab - Django

    88/132

    Bob HaugenPrepping for deployment

  • 7/27/2019 Code Lab - Django

    89/132

    Coding for deployment

  • 7/27/2019 Code Lab - Django

    90/132

    if settings.DEBUG:

    urlpatterns += patterns('',

    (r'^site_media/(?P.*)$',

    'django.views.static.serve',

    {'document_root': '...'}),

    )

  • 7/27/2019 Code Lab - Django

    91/132

    HOME = os.path.abspath(os.path.dirname(__file__))

    TEMPLATE_DIRS = (HOME + "templates/",

    )

  • 7/27/2019 Code Lab - Django

    92/132

    HOME = os.path.abspath(os.path.dirname(__file__))

    TEMPLATE_DIRS = (os.path.join(HOME, "templates/"),

    )

  • 7/27/2019 Code Lab - Django

    93/132

    return HttpResponseRedirect(

    '%s/%s/' % ('order', new_order.id))

  • 7/27/2019 Code Lab - Django

    94/132

    return HttpResponseRedirect(

    urlresolvers.reverse(order, args=[new_order.id]))

  • 7/27/2019 Code Lab - Django

    95/132

    raise Http404

  • 7/27/2019 Code Lab - Django

    96/132

    raise Http404("Invalid order ID")

    D l D l

  • 7/27/2019 Code Lab - Django

    97/132

    Develop Deploymanage.py runserver Apache + mod_python

    mod_wsgi

    SQLite PostgreSQL/MySQL

    DEBUG = True DEBUG = False

    views.static.serve lighttpd

    Django Django + other stuff*

  • 7/27/2019 Code Lab - Django

    98/132

    * Other stuff

  • 7/27/2019 Code Lab - Django

    99/132

    Perlbalreverse proxy load balancer and web serverhttp://danga.com/perlbal/

    Wh ?

    http://danga.com/perlbal/http://danga.com/perlbal/
  • 7/27/2019 Code Lab - Django

    100/132

    Why?

    SpoonfeedingAbstraction of hardware resources

    Load balancing

  • 7/27/2019 Code Lab - Django

    101/132

    http://danga.com/perlbal/

    http://code.sixapart.com/svn/perlbal/trunk/doc/

    http://lists.danga.com/mailman/listinfo/perlbal

    HOWTO

    CREATE POOL ljservers

    set nodefile = /etc/perlbal/nodes/ljworld

    http://lists.danga.com/mailman/listinfo/perlbalhttp://lists.danga.com/mailman/listinfo/perlbalhttp://lists.danga.com/mailman/listinfo/perlbalhttp://code.sixapart.com/svn/perlbal/trunk/doc/http://code.sixapart.com/svn/perlbal/trunk/doc/http://danga.com/perlbal/http://danga.com/perlbal/
  • 7/27/2019 Code Lab - Django

    102/132

    set nodefile = /etc/perlbal/nodes/ljworld

    CREATE SERVICE ljworld

    SET role = reverse_proxySET pool = ljservers

    SET buffer_size = 80k

    ENABLE ljworld

    CREATE SERVICE vhosts

    SET listen = 0.0.0.0:80

    SET role = selectorSET plugins = vhosts

    SET persist_client = on

    VHOST ljworld.com = ljworldVHOST *.ljworld.com = ljworld

    ENABLE vhosts

  • 7/27/2019 Code Lab - Django

    103/132

    Memcachedhigh-performance, distributed memory object caching systemhttp://danga.com/memcached/

    Wh ?

    http://danga.com/memcached/http://danga.com/memcached/
  • 7/27/2019 Code Lab - Django

    104/132

    Why?

    Anything less would be uncivilized.

    Seriously, just use it.

  • 7/27/2019 Code Lab - Django

    105/132

    root@example# memcached d m 2048 l 10.0.0.40 p 11211

  • 7/27/2019 Code Lab - Django

    106/132

    CACHE_BACKEND = "memcached://10.0.0.40:11211/"

  • 7/27/2019 Code Lab - Django

    107/132

    Wiley KestnerSignals

  • 7/27/2019 Code Lab - Django

    108/132

    B k d

  • 7/27/2019 Code Lab - Django

    109/132

    Background

    Sometimes called the Observer patternin communities with design-pattern focus

    Usually handled with some sort ofdispatcher

  • 7/27/2019 Code Lab - Django

    110/132

    Django has a dispatcher

    Dispatcher 101

  • 7/27/2019 Code Lab - Django

    111/132

    Some piece of code sends a signal

    A signal is just a pre-defined Pythonobject

    Django defines a bunch of useful signalsfor you

    django.dispatch.dispatcher.send()

    Dispatcher 101

    Dispatcher 101

  • 7/27/2019 Code Lab - Django

    112/132

    Dispatcher 101

    Functions use the dispatcher to connectto a signal

    Once connected, that function will becalled whenever something else sendsthat signal

    django.dispatch.dispatcher.connect()

  • 7/27/2019 Code Lab - Django

    113/132

    from django.dispatch import dispatcher

    from django.db.models import signals

    def save_callback():

    print Something just got saved

    dispatcher.connect(save_callback,

    signal=signals.post_save)

    The tricky bit

  • 7/27/2019 Code Lab - Django

    114/132

    Your call to dispatcher.connect() has tobe executed

    Good place to put it: models.py

    The tricky bit

  • 7/27/2019 Code Lab - Django

    115/132

    Lets write a simplelistener

  • 7/27/2019 Code Lab - Django

    116/132

    class NotificationManager(models.Manager):

    def notify_comment(self, sender, instance):for n in self.filter(object_id=instance.object_id,

    content_type__pk=instance.content_type_id):

    n.notify()

    class Notification(models.Model):# ...

    objects = NotificationManager()

    def notify(self):

    # ...

  • 7/27/2019 Code Lab - Django

    117/132

    The secret sauce

  • 7/27/2019 Code Lab - Django

    118/132

    from django.db.models import signals

    from django.dispatch import dispatcher

    from notifications.models import Notification

    from django.contrib.comments.models import Comment

    dispatcher.connect(Notification.objects.notify_comment,

    sender=Comment,

    signal=signals.post_save)

    Extending it

  • 7/27/2019 Code Lab - Django

    119/132

    Extending it

    Use the sender argument to control themodel you listen to

    Or omit it to listen to everything

  • 7/27/2019 Code Lab - Django

    120/132

    About genericlisteners

    Should it be generic?

  • 7/27/2019 Code Lab - Django

    121/132

    Should it be generic?

    Well, itd be more reusableBut Django already provides a generic wayto do this: the dispatcher

  • 7/27/2019 Code Lab - Django

    122/132

    Coupling site-specific

    functions to a specificsite isnt wrong

  • 7/27/2019 Code Lab - Django

    123/132

    Eric St-JeanREST APIs

  • 7/27/2019 Code Lab - Django

    124/132

    Terminology

  • 7/27/2019 Code Lab - Django

    125/132

    APIApplication Programming Interface

  • 7/27/2019 Code Lab - Django

    126/132

    RESTRepresentational State Transfer

    UR DOIN IT RITE

  • 7/27/2019 Code Lab - Django

    127/132

    UR DOIN IT RITE

    Proper URIs

    Correct use of HTTP

    Valid, semantically-rich formats

    Resources, not methods

  • 7/27/2019 Code Lab - Django

    128/132

  • 7/27/2019 Code Lab - Django

    129/132

    class Something(Model):

    ...

    @ws.web_method(scope='object', return_type='object', method='POST')

    @ws.web_arg(name='body', type='string')

    def reply(self, body, _user):

    @ws.web_method(scope='type', return_type='object', method='POST')

    @ws.web_arg(name='content_object', type='object')

    @ws.web_arg(name='user', type='object')

    @ws.web_arg(name='subject', type='string')

    @ws.web_arg(name='message', type='string')

    def recommend(self, request, input_data, queryset):

  • 7/27/2019 Code Lab - Django

    130/132

    Sean OConnororder_by(?)

  • 7/27/2019 Code Lab - Django

    131/132

    Honza KrlMany questionsMany answers?

  • 7/27/2019 Code Lab - Django

    132/132

    Stump

    theChumps!


Recommended