Slide 1

Slide 1 text

Django Tricks José Ignacio Galarza @igalarzab Carlos Hernando @chernando

Slide 2

Slide 2 text

django crispy forms https://github.com/maraujop/django-crispy-forms

Slide 3

Slide 3 text

Crispy forms! {% load crispy_forms_tags %} {{ my_formset|crispy }}

Slide 4

Slide 4 text

Crispy forms! {% load crispy_forms_tags %} {{ my_formset|crispy }} El formulario es compatible Twitter Bootstrap

Slide 5

Slide 5 text

Un formulario from crispy_forms.helper import FormHelper class ExampleForm(forms.Form): [...] def __init__(self, *args, **kwargs): self.helper = FormHelper() super(ExampleForm, self).__init__(*args, **kwargs)

Slide 6

Slide 6 text

Personalizamos el formulario [...] self.helper.form_id = 'id-exampleForm' self.helper.form_class = 'blueForms' self.helper.form_method = 'post' self.helper.form_action = 'submit_survey' self.helper.add_input(Submit('submit', 'Submit'))

Slide 7

Slide 7 text

Y el template queda {% load crispy_forms_tags %} {% crispy example_form %}

Slide 8

Slide 8 text

Aún más personalizado from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit self.helper.layout = Layout( Fieldset( 'first arg is the legend of the fieldset', 'field1', 'field2' ), ButtonHolder( Submit('submit', 'Submit', css_class='button white') )

Slide 9

Slide 9 text

Y el template queda igual {% load crispy_forms_tags %} {% crispy example_form %} WIN

Slide 10

Slide 10 text

Más de crispy forms FormSets Cambiar layouts al vuelo Personalizar... todo :-) http://django-crispy-forms.readthedocs.org/

Slide 11

Slide 11 text

south

Slide 12

Slide 12 text

Nuestro modelo cambia class Persona(models.Model): nombre = models.CharField(...) apellidos = models.CharField(...)

Slide 13

Slide 13 text

Nuestro modelo cambia class Persona(models.Model): nombre = models.CharField(...) apellidos = models.CharField(...) email = models.EmailField(...)

Slide 14

Slide 14 text

Ok, hago un syncdb :) ./manage.py syncdb Creating tables ... Creating table tutsouth_persona Installing custom SQL ... Installing indexes ... Installed 0 object(s) from 0 fixture(s) :-D

Slide 15

Slide 15 text

Pero... >>> p = Persona() >>> p.save() Traceback (most recent call last): ... DatabaseError: table tutsouth_persona has no column named email ;-(

Slide 16

Slide 16 text

FAIL syncdb no modifica

Slide 17

Slide 17 text

Soluciones Django ○ Borrón y cuenta nueva manage flush && manage syncdb ○ Modificar manualmente las tablas manage sql

Slide 18

Slide 18 text

Soluciones Django ○ Borrón y cuenta nueva manage flush && manage syncdb ○ Modificar manualmente las tablas manage sql South

Slide 19

Slide 19 text

south South brings migrations to Django applications. ● Automatic migration creation ● Database independence ● App-savvy ● VCS-proof

Slide 20

Slide 20 text

Migraciones RAE: Acción y efecto de pasar de un país a otro para establecerse en él. south: [...] a way of changing your database schema from one version into another [...]

Slide 21

Slide 21 text

Migraciones RAE: Acción y efecto de pasar de un país a otro para establecerse en él. south: [...] a way of changing your database schema from one version into another [...] En ambos sentidos.

Slide 22

Slide 22 text

south en dos patadas Migraciones: Primera: manage.py schemamigration APP --initial Siguientes: manage.py schemamigration APP --auto Aplicar: manage.py migrate [APP]

Slide 23

Slide 23 text

Migraciones complicadas ? The field 'Votacion.autor' does not have a default specified, yet is NOT NULL. ? Since you are making this field non-nullable, you MUST specify a default ? value to use for existing rows. Would you like to: ? 1. Quit now, and add a default to the field in models.py ? 2. Specify a one-off value to use for existing columns now ? Please select a choice:

Slide 24

Slide 24 text

Durante el desarrollo Reutilizar la migración: manage.py schemamigration --update --auto Conflictos manage.py migrate --merge Listar: manage.py migrate --list

Slide 25

Slide 25 text

Más cosas interesantes Datamigrations ORM Freezing Dependencias ... http://south.readthedocs.org/

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Pruebas :-? ● When you’re writing new code, you can use tests to validate your code works as expected. ● When you’re refactoring or modifying old code, you can use tests to ensure your changes haven’ t affected your application’s behavior unexpectedly.

Slide 28

Slide 28 text

Pruebas :-? ● When you’re writing new code, you can use tests to validate your code works as expected. ● When you’re refactoring or modifying old code, you can use tests to ensure your changes haven’ t affected your application’s behavior unexpectedly. Sí o sí 0:-)

Slide 29

Slide 29 text

Métodos disponibles unittest class MyFuncTestCase(unittest.TestCase): def testBasic(self): a = ['larry', 'curly', 'moe'] self.assertEqual(my_func(a, 0), 'larry') doctest def my_func(a_list, idx): """ >>> a = ['larry', 'curly', 'moe'] >>> my_func(a, 0) 'larry'

Slide 30

Slide 30 text

unittest Forma parte de Python Cumplir dos condiciones: a. Heredar de unittest.TestCase b. El método empieza por test Django enriquece con django.utils.unittest

Slide 31

Slide 31 text

Ejemplo import unittest class TestDePrueba(unittest.TestCase): def test_prueba_1_1(self): self.assertEquals(1 + 1, 2) def test_con_error(self): self.assertEquals(1 + 1, 2.1, "Intel detected!")

Slide 32

Slide 32 text

Pruebas en Django Por defecto: APP/tests.py manage.py test [[[APP].TestCase].test_method]

Slide 33

Slide 33 text

Modelos Bases de datos de prueba By default the test databases get their names by prepending test_ to the value of the NAME settings for the databases defined in DATABASES. Ejercicio para el lector: fixtures

Slide 34

Slide 34 text

Vistas Test Client simula un navegador from django.test.client import Client class SimpleTest(unittest.TestCase): def setUp(self): self.client = Client() def test_details(self): response = self.client.get('/customer/details/') self.assertEqual(response.status_code, 200) self.assertEqual(len(response.context['customers']), 5)

Slide 35

Slide 35 text

Selenium Utiliza un navegador real 1. Acciones que realiza 2. Cosas que espera encontrar

Slide 36

Slide 36 text

Introducción a Python + Selenium from selenium import webdriver from selenium.webdriver.common.keys import Keys driver = webdriver.Firefox() driver.get("http://www.python.org") assert "Python" in driver.title elem = driver.find_element_by_name("q") elem.send_keys("selenium") elem.send_keys(Keys.RETURN) assert "Google" in driver.title driver.close()

Slide 37

Slide 37 text

Django + Selenium from django.test import LiveServerTestCase from selenium.webdriver.firefox.webdriver import WebDriver class MySeleniumTests(LiveServerTestCase): @classmethod def setUpClass(cls): cls.selenium = WebDriver() super(MySeleniumTests, cls).setUpClass() @classmethod def tearDownClass(cls): cls.selenium.quit() super(MySeleniumTests, cls).tearDownClass()

Slide 38

Slide 38 text

Django + Selenium def test_home(self): self.selenium.get(self.live_server_url) votacion = self.selenium. find_element_by_link_text("Nueva") self.assertIsNotNone(votacion) votacion.click() self.selenium.save_screenshot('votacion.png')

Slide 39

Slide 39 text

Django + Selenium self.selenium.get('%s%s' % (self.live_server_url, '/nueva_votacion/')) titulo_input = self.selenium.find_element_by_id("id_titulo") titulo_input.send_keys('prueba selenium') autor_input = self.selenium.find_element_by_id("id_autor") autor_input.send_keys('selenium') self.selenium.find_element_by_id('enviar').click() self.selenium.save_screenshot('nueva_votacion.png') titulo = self.selenium.find_element_by_id('titulo') self.assertEquals(titulo.text, 'prueba selenium')

Slide 40

Slide 40 text

Finalizando @8-)

Slide 41

Slide 41 text

33 projects that make developing django apps awesome Más... http://elweb.co/programacion/33-projects-that-make-developing-django-apps-awesome/

Slide 42

Slide 42 text

Preguntas :-?

Slide 43

Slide 43 text

Gracias a todos! :-D