django as your backbone

36
"DJANGO AS YOUR BACKBONE" AN INTRODUCTION By Roderick Schaefer (We handle IT) 0

Upload: roderick-schaefer

Post on 18-Jan-2015

2.553 views

Category:

Technology


0 download

DESCRIPTION

Using (Python) Django and Backbone together, in order to "move to the frontend" with your web applications. Working API driven. Also benefits from plugins like require.js. Presentation given at Python Meetup.

TRANSCRIPT

Page 1: Django as your Backbone

"DJANGO AS YOURBACKBONE"

AN INTRODUCTION

By Roderick Schaefer (We handle IT)

0

Page 2: Django as your Backbone

I'm a 31 year old freelance developer, currently working forSchuberg Philis.

My focus is development in Python and PHP accompanied bygoodies like Backbone and supporting tools.

Page 3: Django as your Backbone

WHERE ARE YOUSchuberg PhilisMission Critical OutsourcingDevOps<3 Python and Django

Page 4: Django as your Backbone

WHY ARE WE HEREPythonDjangoBackbone ?

Moving to the frontend

Page 5: Django as your Backbone

OLD SCHOOL WEB APP DEVELOPMENTDjango with the MTV ("MVC") pattern

Page 6: Django as your Backbone

(M) models.pyclass CandidatePhase(models.Model): created = models.DateTimeField(auto_now_add=True) candidate = models.ForeignKey(Candidate)

def save(self, *args, **kwargs): super(CandidatePhase, self).save(*args, **kwargs)

self.candidate.name = 'Django'

def __unicode__(self): return unicode(self.candidate.name)

Page 7: Django as your Backbone

(T) index.html<html> <head> <title>My very own website</title> </head> <body> <div id="content"> <ul> {% for candidate in candidates %} <li>{{ candidate.name }}</li> {% endfor %} </ul> </div> </body></html>

Page 8: Django as your Backbone

(V) views.py@render_to('myapp/index.html')def list_candidates(request): candidates = Candidate.objects.all() phases = Phase.objects.filter(active=True)

return { candidates: candidates, phases: phases, }

Page 9: Django as your Backbone

NEW: API DRIVEN, FRONTEND MVCPythonDjangojQueryTastyPieBackboneRequire.jsUnderscoreHandlebarsBackbone-tastypie ?

Wait.. Why ?

Page 10: Django as your Backbone

BECAUSE WE CAN!It is not only awesome to work API driven...

IT JUST MAKES SENSE

API: Securely expose databases

Frontend(s): Consume, process, present

Separation of concerns

Single Page Apps

Page 11: Django as your Backbone

TASTYPIE: AN INTRODUCTIONAPI webservice for DjangoAllows RESTful implementationORM exposureNon-ORMAuthenticationAuthorizationSerialization (JSON!)Pagination

Page 12: Django as your Backbone

BACKBONE: AN INTRODUCTIONFrontend MVCLeverages Underscore.js and friendsRouterModelsCollectionsViewsEventsTemplates (using plugins)

Page 13: Django as your Backbone

I CAN HAZ CODEBasic frontend app, powered by Django and Backbone

Page 14: Django as your Backbone

your_app/urls.pyfrom django.conf.urls import patterns, url

from apps.appraisal import views

urlpatterns = patterns( '', url(r'̂$', views.index, name='index'),)

No need to touch this anymore from now on.

Page 15: Django as your Backbone

your_app/views.pyfrom annoying.decorators import render_to

@render_to('appraisal/index.html')def index(request): return {}

No need to touch this anymore from now on.

Page 16: Django as your Backbone

your_app/templates/index.html{% extends "base.html" %}

{% block content %} <script data-main="appraisal/bootstrap" src="js/require.js"></script>

<div id="content"></div>{% endblock %}

No need to touch this anymore from now on.

Page 17: Django as your Backbone

your_app/api.pyclass FeedbackResource(PatchableModelResource): review = ToOneField( 'apps.appraisal.api.ReviewResource', 'review' )

class Meta: resource_name = 'appraisal/feedback' authentication = ConnectAuthentication() authorization = FeedbackAuthorization() queryset = Feedback.objects.prefetch_related('review') serializer = Serializer(formats=('json', ))

def hydrate(self, bundle): bundle.data.get('content') = awesome(bundle.data.get('content'))

return bundle

This is where you do the heavy lifting in Django.

Page 18: Django as your Backbone

your_app/static/bootstrap.jsrequire.config({ baseUrl: '/static', paths: { 'backbone' : 'plugins/backbone-min', 'handlebars': 'plugins/handlebars-1.0.0-rc.3.min' }, shim: { 'backbone': { deps : ['jquery', 'underscore'], exports: 'Backbone' }, 'handlebars': { exports: 'Handlebars' } }});require(['appraisal/app'], function(App) { App.initialize(); });

Convention > configuration, only specify for non-standard pathsor shim (Non-AMD).

Page 19: Django as your Backbone

your_app/static/app.jsdefine([ 'appraisal/router'], function(menu, Router) { var initialize = function() { Router.initialize(); };

return { initialize: initialize };});

Small example, just load router..

Page 20: Django as your Backbone

your_app/static/router.jsdefine([ 'backbone', 'views/dashboard'], function(Backbone, DashboardView) { var AppRouter = Backbone.Router.extend({ routes: { 'dashboard': 'dashboard' // many more routes.. },

dashboard: function() { var dashboardView = new DashboardView();

dashboardView.render(); }, });

var initialize = function(){ var app_router = new AppRouter();

Backbone.history.start(); };

return { initialize: initialize };});

Try not to do anything besides routing.

Page 21: Django as your Backbone

your_app/static/views/peers.jsdefine([ 'jquery', 'backbone', 'handlebars', 'collections/persons', 'text!templates/peers.html',], function($, Backbone, Handlebars, PersonCollection, peerTemplate) { var ReviewListingView = Backbone.View.extend({ el: '#content',

initialize: function() { this.personCollection = new PersonCollection();

this.personCollection.on('reset', this.updatePerson, this); this.personCollection.fetch();

_.bindAll(this, 'render', 'remove', 'help'); },

events: { 'click .help': 'help' },

template: Handlebars.compile(peerTemplate),

render: function() { this.$el.html(this.template({ forUrl: forUrl }));

Page 22: Django as your Backbone

your_app/static/models/SomeModel.jsdefine([ 'backbone'], function(Backbone) { var PersonModel = Backbone.Model.extend({ urlRoot: '/api/v1/team/person/' });

return PersonModel;});

Page 23: Django as your Backbone

your_app/static/templates/SomeTemplate.html<div class="btn-group"> <a href="#peers/add{{forUrl}}" class="btn">Add Peer</a> <button id="peers_done" class="btn">I am done</button></div>

<div> <table id="appraisal_reviews_grid"></table></div>

Event binding in your Views. {{forUrl}} is a variable given toHandlebars template compiler.

Page 24: Django as your Backbone

BACKBONE <3 TASTYPIEAdd PATCH support to ModelResource

class PatchableModelResource(ModelResource): def patch_detail(self, request, **kwargs): ### ---snip--- ###

# This is where the magic happens. request._read_started = False body = request.body # This is where it stops.

### ---snip--- ###

Page 25: Django as your Backbone

BACKBONE <3 TASTYPIEPATCH part 2

class FormValidation(TastyPieFormValidation): def uri_to_pk(self, uri): if uri is None: return None

# This is where the magic happens. if isinstance(uri, Bundle): uri = uri.data['resource_uri'] elif isinstance(uri, list) and uri: if isinstance(uri[0], Bundle): for i in xrange(len(uri)): uri[i] = uri[i].data['resource_uri'] # This is where it stops.

### ---snip--- ###

Page 26: Django as your Backbone

BACKBONE <3 TASTYPIEAdd File Upload support to ModelResource

class MultipartResource(object): def deserialize(self, request, data, format=None): if not format: format = request.META.get('CONTENT_TYPE', 'application/json')

if 'application/x-www-form-urlencoded' == format: return request.POST

if format.startswith('multipart'): data = request.POST.copy() data.update(request.FILES)

return data

return super(MultipartResource, self).deserialize(request, data, format)

class CandidateResource(MultipartResource, PatchableModelResource): # Implementation of API endpoint supporting PATCH and File uploads.

Page 27: Django as your Backbone

BACKBONE <3 TASTYPIEGlue: Backbone-Tastypie.js

<script src="{{ STATIC_URL }}js/plugins/underscore.min.js"></script><script src="{{ STATIC_URL }}js/plugins/backbone-min.js"></script>

<script src="{{ STATIC_URL }}js/plugins/backbone-tastypie.js"></script>

Page 28: Django as your Backbone

BACKBONE <3 TASTYPIEForm generation: Backbone-Forms.js

You could map a Django ModelForm to the frontendvar User = Backbone.Model.extend({ schema: { title: { type: 'Select', options: ['Mr', 'Mrs', 'Ms'] }, name: 'Text', email: { validators: ['required', 'email'] }, birthday: 'Date', password: 'Password', address: { type: 'NestedModel', model: Address }, notes: { type: 'List', itemType: 'Text' } }});

var user = new User();

var form = new Backbone.Form({ model: user}).render();

Page 29: Django as your Backbone

TRANSITIONING TO FRONTEND MVCTHIS IS CONNECT

Page 30: Django as your Backbone

TRANSITIONING TO FRONTEND MVCExisting applicationA collection of Apps, reallyShared libs, code, authentication layers etc

"Can I haz Backbone coolness?!"

Product is already live, actively in use.

Page 31: Django as your Backbone

TRANSITIONING TO FRONTEND MVC

Yes you can!

Wrap your base.html's javascripts in a block. Empty that block inyour Backbone powered apps.

Include some "legacy-scripts" template from your backboneapp's templates, for compatibility with generic stuff like the main

navigation.

Page 32: Django as your Backbone

TRANSITIONING TO FRONTEND MVCyour_app/templates/index.html

{% extends "page.base.page.html" %}{% block content %} {% block "javascripts" %}{% endblock %} {% include "legacy-scripts.html" %}

<script data-main="YourApp/bootstrap" src="js/require.js"></script>

<div id="content"></div>{% endblock %}

This is all you need for awesome.

Page 33: Django as your Backbone

CHALLENGES

URL reversal (API endpoints in Backbone Model)Session state awareness in the frontend (like Permissions,Request object)

Page 34: Django as your Backbone

ALTERNATIVESTastyPie vs Django REST Framework.

Backbone vs Angular vs Ember vs .. Check out TodoMVC.com!

Page 35: Django as your Backbone

WHAT'S NEXT?Currently looking into javascript package management, sockets,

..

1. Grunt2. Bower3. NPM4. Node JS5. So much to do.....

Page 36: Django as your Backbone

QUESTIONS?See you around, enjoy your evening at Schuberg Philis!

Don't forget to grab a couple of beers whileyou're at it...