palestra pythonbrasil[8]

27
Django e Backbone.js Uma parceria de sucesso Thiago Garcia

Upload: thiago-da-silva

Post on 17-May-2015

291 views

Category:

Technology


1 download

DESCRIPTION

Minha palestra sobre Django e Backbone na PythonBrasil 8

TRANSCRIPT

Page 1: Palestra PythonBrasil[8]

Django e Backbone.js

Uma parceria de sucesso

Thiago Garcia

Page 2: Palestra PythonBrasil[8]

Quem?

Page 3: Palestra PythonBrasil[8]

thiagogds14

thiagogds

thiagogds

thiagogds

Page 4: Palestra PythonBrasil[8]

Disclaimer

Page 5: Palestra PythonBrasil[8]

Por que Backbone?

Page 6: Palestra PythonBrasil[8]
Page 7: Palestra PythonBrasil[8]

Model

Page 8: Palestra PythonBrasil[8]

Collection

Page 9: Palestra PythonBrasil[8]

Text

View

Page 10: Palestra PythonBrasil[8]

Testes!!• Jasmine.js: http://pivotal.github.com/jasmine/

• Jasmine-Jquery: https://github.com/velesin/jasmine-jquery

• Sinon.js: http://sinonjs.org/

• Jasmine-Sinon: https://github.com/froots/jasmine-sinon

Page 11: Palestra PythonBrasil[8]

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head> <title>Jasmine Spec Runner</title>

<link rel="shortcut icon" type="image/png" href="lib/jasmine-1.2.0/jasmine_favicon.png"> <link rel="stylesheet" type="text/css" href="lib/jasmine-1.2.0/jasmine.css">

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script type="text/javascript" src="../underscore-min.js"></script> <script type="text/javascript" src="../handlebars-1.0.0.beta.6.js"></script> <script type="text/javascript" src="../backbone-min.js"></script> <script src="../bootstrap-datepicker.js"></script> <script src="../bootstrap-datepicker.pt-BR.js"></script> <script src="../rrule.js"></script> <script src="../moment.min.js"></script>1 <script type="text/javascript" src="lib/jasmine-1.2.0/jasmine.js"></script> <script type="text/javascript" src="lib/jasmine-1.2.0/jasmine-html.js"></script> <script type="text/javascript" src="lib/sinon-1.5.0.js"></script> <script type="text/javascript" src="lib/jasmine-sinon.js"></script> <script type="text/javascript" src="lib/jasmine-jquery.js"></script>

<!-- include source files here... --> <script type="text/javascript" src="../absenteeism.js"></script>

<!-- include spec files here... --> <script type="text/javascript" src="spec/AbsenteeismSpec.js"></script>

<script type="text/javascript"> (function() { var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000;

var htmlReporter = new jasmine.HtmlReporter();

jasmineEnv.addReporter(htmlReporter);

jasmineEnv.specFilter = function(spec) { return htmlReporter.specFilter(spec); };

var currentWindowOnload = window.onload;

window.onload = function() { if (currentWindowOnload) { currentWindowOnload(); } execJasmine(); };

function execJasmine() { jasmineEnv.execute(); }

})(); </script>

</head>

<body></body></html>

Page 12: Palestra PythonBrasil[8]

describe("Absenteeism", function() { beforeEach(function() { this.initial_data = { 'atested_begin': "03/01/2012", 'atested_end': "04/01/2012", 'revised_begin': "03/01/2012", 'revised_end': "04/01/2012", };

this.absenteeism = new Absenteeism(this.initial_data); this.absenteeismCollection = new AbsenteeismCollection([this.absenteeism]) this.absenteeismReportView = new AbsenteeismReport({collection: this.absenteeismCollection}) });

Page 13: Palestra PythonBrasil[8]

describe("AbsenteeismModel", function() { beforeEach(function() { this.eventSpy = sinon.spy(); }); describe("When validating", function() { it("should check that first atested date is lower then second date", function() { this.absenteeism.bind("error", this.eventSpy);

this.initial_data["atested_end"] = "01/01/2012"; this.absenteeism.set(this.initial_data);

expect(this.eventSpy).toHaveBeenCalledOnce(); expect(this.eventSpy).toHaveBeenCalledWith( this.absenteeism, "Data Final Atestadada não pode ser menor que a Data Inicial Atestada" ); });

Page 14: Palestra PythonBrasil[8]

describe("AbsenteeismReport View", function() { beforeEach(function() { loadFixtures("absenteeism.html"); });

describe("When rendering", function() { it("should show the correct components", function() { this.absenteeismReportView.render(); var atested_begin = $("input[name=atested_begin]"); var atested_end = $("input[name=atested_end]"); var revised_begin = $("input[name=revised_begin]"); var revised_end = $("input[name=revised_end]"); var dias_negados = $("span.difference");

expect(atested_begin).toHaveValue("03/01/2012"); expect(atested_end).toHaveValue("04/01/2012"); expect(revised_begin).toHaveValue("03/01/2012"); expect(revised_end).toHaveValue("04/01/2012"); }); });

Page 15: Palestra PythonBrasil[8]

Handlebarshttp://handlebarsjs.com/

var abs_template_html = $('#absenteeism_form').html(); var abs_template = Handlebars.compile(abs_template_html);

$(this.el).html(abs_template(this.model.toJSON()));

{% verbatim %}<script id="absenteeism_form" type="text/x-handlebars-template"> <td><button class="close remove">&times;</button></td> <td> <input type="text" name="atested_begin" class="input-small" value="{{ atested_begin }}"><br> <input type="text" name="atested_end" class="input-small" value="{{ atested_end }}"> </td> <td> <input type="text" name="revised_begin" class="input-small" value="{{ revised_begin }}"><br> <input type="text" name="revised_end" class="input-small" value="{{ revised_end }}"> </td></script>{% endverbatim %}

Page 16: Palestra PythonBrasil[8]

var strdt = function(str) { return moment(str, "DD/MM/YYYY").toDate(); }

window.Absenteeism = Backbone.Model.extend({ validate: function(attributes) { var data_inicio_atestado = strdt(attributes.atested_begin); var data_fim_atestado = strdt(attributes.atested_end); var data_inicio_revisado = strdt(attributes.revised_begin); var data_fim_revisado = strdt(attributes.revised_end);

if(data_inicio_atestado.getYear() < 0 || data_fim_atestado.getYear() < 0 || data_inicio_revisado.getYear() < 0 || data_fim_revisado.getYear() < 0 ) { return "Data invÃálida"; }

if (data_fim_atestado < data_inicio_atestado) { return "Data Final Atestadada não pode ser menor que a Data Inicial Atestada"; } if (data_fim_revisado < data_inicio_revisado) { return "Data Final Abonada não pode ser menor que a Data Inicial Abonada"; } if (data_inicio_revisado < data_inicio_atestado) { return "Data Incial Abonada não pode ser menor que a Data Inicial Atestada"; } if (data_fim_revisado > data_fim_atestado){ return "Data Final Abonada não pode ser maior que a Data Final Atestada"; } } });

http://momentjs.com/

Page 17: Palestra PythonBrasil[8]

save: function() { var self = this; var post_data = {}; var absenteeism = {}; var values = []; this.collection.each(function(model) { values.push(model.toJSON()); }); absenteeism['adm_obs'] = $(this.el).find('textarea[name="adm_obs"]').val(); absenteeism['medical_obs'] = $(this.el).find('textarea[name="medical_obs"]').val(); absenteeism['entries'] = values; post_data['absenteeism'] = absenteeism; $.ajax({ type: 'POST', url: window.post_url, data: {data: JSON.stringify(post_data)}, success: function(data) { self.process_success(data); }, error: function(data) { self.process_error(data); }, dataType: 'json' }); },

Page 18: Palestra PythonBrasil[8]

window.Absenteeism = Backbone.Model.extend({ validate: function(attributes) { var data_inicio_atestado = strdt(attributes.atested_begin); var data_fim_atestado = strdt(attributes.atested_end); var data_inicio_revisado = strdt(attributes.revised_begin); var data_fim_revisado = strdt(attributes.revised_end);

if(data_inicio_atestado.getYear() < 0 || data_fim_atestado.getYear() < 0 || data_inicio_revisado.getYear() < 0 || data_fim_revisado.getYear() < 0 ) { return "Data invÃálida"; }

if (data_fim_atestado < data_inicio_atestado) { return "Data Final Atestadada não pode ser menor que a Data Inicial Atestada"; } if (data_fim_revisado < data_inicio_revisado) { return "Data Final Abonada não pode ser menor que a Data Inicial Abonada"; } if (data_inicio_revisado < data_inicio_atestado) { return "Data Incial Abonada não pode ser menor que a Data Inicial Atestada"; } if (data_fim_revisado > data_fim_atestado){ return "Data Final Abonada não pode ser maior que a Data Final Atestada"; } } });

Page 19: Palestra PythonBrasil[8]

window.AbsenteeismView = Backbone.View.extend({ events: { 'click .remove': 'remove', 'blur input[name=atested_begin]': 'update_value', 'blur input[name=atested_end]': 'update_value', 'blur input[name=revised_begin]': 'update_value', 'blur input[name=revised_end]': 'update_value' }, tagName: 'tr', initialize: function() { _.bindAll(this, 'render'); _.bindAll(this, 'remove'); _.bindAll(this, 'update_value'); _.bindAll(this, 'restore_view'); this.model.on('error', this.restore_view); }, render: function() { var abs_template_html = $('#absenteeism_form').html(); var abs_template = Handlebars.compile(abs_template_html);

$(this.el).html(abs_template(this.model.toJSON()));

var self = this; $(this.el).find('input[type=text]').datepicker({ format: 'dd/mm/yyyy', autoclose: true, language: 'pt-BR', }).on('changeDate', function(ev) { self.update_value(ev); });

return this; }, remove: function() { $(this.el).remove(); this.model.collection.remove(this.model); }, update_value: function(event) { var data_inicio_atestado = $(this.el).find('input[name=atested_begin]').val(); var data_fim_atestado = $(this.el).find('input[name=atested_end]').val(); var data_inicio_revisado = $(this.el).find('input[name=revised_begin]').val(); var data_fim_revisado = $(this.el).find('input[name=revised_end]').val();

this.model.set({ 'atested_begin': data_inicio_atestado, 'atested_end': data_fim_atestado, 'revised_begin': data_inicio_revisado, 'revised_end': data_fim_revisado, }); }, restore_view: function(model, message) { $(this.el).find('input[name=atested_begin]').val(this.model.get('atested_begin')); $(this.el).find('input[name=atested_end]').val(this.model.get('atested_end')); $(this.el).find('input[name=revised_begin]').val(this.model.get('revised_begin')); $(this.el).find('input[name=revised_end]').val(this.model.get('revised_end')); $('#myModal .modal-body p').html(message); $('#myModal').modal('toggle'); }, });

Page 20: Palestra PythonBrasil[8]

window.AbsenteeismReport = Backbone.View.extend({ events: { 'click .add': 'add_entry', 'click .confirm': 'save', }, el: function () { return $('#main'); }, initialize: function() { _.bindAll(this, 'render'); _.bindAll(this, 'add'); _.bindAll(this, 'add_entry'); _.bindAll(this, 'save'); _.bindAll(this, 'process_success'); _.bindAll(this, 'process_error'); this.collection.on('add', this.add); }, render: function() { var view = $(this.el).find('#table_body'); this.collection.each(function(model) { var model_view = new AbsenteeismView({model: model}); view.append(model_view.render().el); }); return this; }, add: function(item) { var entry = new AbsenteeismView({model:item}); $(this.el).find('#table_body').append(entry.render().el); }, add_entry: function() { this.collection.add(new_absenteeism()); }, process_success: function (data) { alert(data.message); }, process_error: function (data) { alert(data.responseText); }, save: function() { var self = this; var post_data = {}; var absenteeism = {}; var values = []; this.collection.each(function(model) { values.push(model.toJSON()); }); absenteeism['adm_obs'] = $(this.el).find('textarea[name="adm_obs"]').val(); absenteeism['medical_obs'] = $(this.el).find('textarea[name="medical_obs"]').val(); absenteeism['entries'] = values; post_data['absenteeism'] = absenteeism; $.ajax({ type: 'POST', url: window.post_url, data: {data: JSON.stringify(post_data)}, success: function(data) { self.process_success(data); }, error: function(data) { self.process_error(data); }, dataType: 'json' }); }, });

Page 21: Palestra PythonBrasil[8]

window.AbsenteeismCollection = Backbone.Collection.extend({ model: Absenteeism });

Page 22: Palestra PythonBrasil[8]

Tá, e o Django?

Page 23: Palestra PythonBrasil[8]

def save(request): absenteeism_data = json.loads(request.POST['data']) absenteeism = absenteeism_data['absenteeism']

absenteeism_entries = absenteeism['entries'] absenteeism.pop('entries')

Page 24: Palestra PythonBrasil[8]

class AbsenteeismEntryForm(forms.ModelForm):

class Meta: model = AbsenteeismEntry exclude = ('absenteeism',)

def clean(self): super(AbsenteeismEntryForm, self).clean()

if self.cleaned_data.get('atested_begin') > \ self.cleaned_data.get('atested_end'): raise forms.ValidationError( u'Data Final Atestadada não pode ser menor que a Data Inicial Atestada')

if self.cleaned_data.get('revised_begin') > \ self.cleaned_data.get('revised_end'): raise forms.ValidationError( u'Data Final Abonada não pode ser menor que a Data Inicial Abonada')

if self.cleaned_data.get('atested_begin') > \ self.cleaned_data.get('revised_begin'): raise forms.ValidationError( u'Data Incial Abonada não pode ser menor que a Data Inicial Atestada')

if self.cleaned_data.get('revised_end') > \ self.cleaned_data.get('atested_end'): raise forms.ValidationError( u'Data Final Abonada não pode ser maior que a Data Final Atestada')

return self.cleaned_data

Page 25: Palestra PythonBrasil[8]

return HttpResponse(json.dumps(dict(url="/success/")), mimetype="application/json")

Page 26: Palestra PythonBrasil[8]

def save(request): absenteeism_data = json.loads(request.POST['data']) absenteeism = absenteeism_data['absenteeism']

absenteeism_entries = absenteeism['entries'] absenteeism.pop('entries')

absenteeism_form = AbsenteeismForm(absenteeism) if absenteeism_form.is_valid(): entries_form = [] for entry in absenteeism_entries: entry_form = AbsenteeismEntryForm(entry) if entry_form.is_valid(): entries_form.append(entry_form.cleaned_data) else: return HttpResponseServerError(json.dumps(entry_form.errors), mimetype="application/json")

else: return HttpResponseServerError(json.dumps(absenteeism_form.errors), mimetype="application/json")

new_absenteeism = absenteeism_form.save() for entry in entries_form: new_absenteeism.absenteeismentry_set.create(**entry)

return HttpResponse(json.dumps(dict(message="Salvou com sucesso!")), mimetype="application/json")