Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Upcoming SlideShare
What to Upload to SlideShare
What to Upload to SlideShare
Loading in …3
×
1 of 40

Django + Vue, JavaScript de 3ª generación para modernizar Django

15

Share

Slides de la charla que di en la PyConEs 2017 en Cáceres, el 24 de Septiembre.

Explicaba cómo montar un entorno de desarrollo ágil con Django en el back, Vue en el front y webpack para empaquetar el front y proporcionar Hot Module Reloading

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Django + Vue, JavaScript de 3ª generación para modernizar Django

  1. 1. Django + Vue.JS JavaScript de 3ª generación para modernizar Django @javierabadia PyConES 2017 Cáceres
  2. 2. SPAs - Single Page Applications initial request HTML GET url HTML POST form HTML initial request HTML GET url (AJAX) JSON POST url (AJAX) JSON page reload Ciclo de Vida Tradicional de una Página Web Ciclo de Vida de una App SPA page reload
  3. 3. GET url (AJAX) JSON POST url (AJAX) JSON El problema fundamental en el ‘front’
  4. 4. Frameworks de JS 1ª generación 2ª generación 3ª generación
  5. 5. Un poco de Vue.js
  6. 6. ¡ Se dice /vjuː/ ! ¿ Sabes que estoy aprendiendo vue ?
  7. 7. Lo simple es fácil var app = new Vue({ el: '#app', data: { msg: ’Hi there!', frameworks: ['React', 'Vue.js', 'Angular.io'], }, methods: { isCool(framework) { return framework === 'Vue.js'; }, }, }); <div id="app"> {{msg}} <ul> <li v-for="framework in frameworks"> {{framework}} <span v-if="isCool(framework)"> , so cool! </span> </li> </ul> </div> HTML JavaScript Declarative Rendering Condicionales y Bucles
  8. 8. Eventos var app = new Vue({ ... methods: { isCool(framework) { return this.cool.indexOf(framework) !== -1; }, toggleCool(framework) { if( this.isCool(framework) ) this.cool.splice(this.cool.indexOf(framework),1); else this.cool.push(framework); } }, }); <ul> <li v-for="framework in frameworks" @click="toggleCool(framework)"> {{framework}} <span v-if="isCool(framework)"> , so cool! </span> </li> </ul> HTML JavaScript
  9. 9. Enlazando (binding) atributos <ul> <li class="framework-item" :class="{cool: isCool(framework)}" v-for="framework in frameworks" @click="toggleCool(framework)"> {{framework}}<span v-if="isCool(framework)">, so cool!</span> </li> </ul>
  10. 10. Vue.js
  11. 11. Componentes vue instances
  12. 12. Single-File components // MyComponent.vue <template> <div></div> </template> <script> export default { data() { return {}; }, }; </script> <style lang="less"> </style> js css webpack (o browserify)
  13. 13. Single-File components // MyComponent.vue <template> <div></div> </template> <script> export default { data() { return {}; }, }; </script> <style lang="less"> </style> js css webpack (o browserify)
  14. 14. Arquitectura de una SPA de Vue.js $ vue init webpack-simple frontend $ cd frontend $ yarn $ yarn run dev localhost:8080
  15. 15. DEMO 1
  16. 16. Ejemplo: Catálogo de GIFs para IoT http://localhost:8000/ http://localhost:8000/detail/323
  17. 17. La estructura clásica de Django Vistas Templates Modelos ORM Autenticación Middleware Formularios Administración HTML
  18. 18. DEMO 2
  19. 19. Django :8000 Webpack :8080
  20. 20. La experiencia ‘óptima’ de desarrollo • Trabajar de forma unificada (mismo IDE) • Desarrollo en el backend • debugging • breakpoints, etc • Desarrollo en el frontend • con agilidad • usando Hot Module Replacement (HMR)
  21. 21. El puente entre Django y Webpack $ cd frontend $ npm install --save-dev webpack-bundle-tracker # (en un virtualenv, por supuesto) $ pip install django-webpack-loader
  22. 22. Todo junto var path = require('path') var webpack = require('webpack') var BundleTracker = require('webpack-bundle-tracker'); module.exports = { … plugins: [ new BundleTracker({filename: './webpack-stats.json'}) ] } // frontend/webpack.conf.js {% extends 'base.html' %} {% load render_bundle from webpack_loader %} {% block content %} <div id="app"></div> {% render_bundle 'main' %} {% endblock %} {# backend/templates/index.html #} { "status": "done", "publicPath": "http://localhost:8080/dist/", "chunks": { "main": [ { "name": "build.js", "publicPath": "http://localhost:8080/dist/build.js", "path": "/Users/jami/…/gif_catalog/frontend/dist/build.js" } ] } } // frontend/webpack-stats.json … WEBPACK_LOADER = { 'DEFAULT': { 'BUNDLE_DIR_NAME': 'dist/', 'STATS_FILE': os.path.join(BASE_DIR, 'frontend/webpack-stats.json'), } } # settings.py
  23. 23. def index(request): return render(request, 'index.html', {}) # backend/views.py {% extends 'base.html' %} {% load render_bundle from webpack_loader %} {% block content %} <div id="app"></div> {% render_bundle 'main' %} {% endblock %} {# backend/templates/index.html #} Django :8000 Webpack :8080 urlpatterns = [ url(r'^', views.index), ] # backend/urls.py App.vue main.js *.vue localhost:8000/ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> … </head> <body> <div id="app"></div> <script type="text/javascript" src="http://localhost:8080/dist/build.js"> </script> </body </html> HMRbuild.js
  24. 24. DEMO 3
  25. 25. Webpack: detalles de configuración var path = require('path') var webpack = require('webpack') var BundleTracker = require('webpack-bundle-tracker'); module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, './dist'), publicPath: 'http://localhost:8080/dist/', filename: 'build.js' }, module: { ... }, devServer: { historyApiFallback: true, noInfo: true, headers: { 'Access-Control-Allow-Origin': '*' } }, plugins: [ new BundleTracker({filename: './webpack-stats.json'}) ] } url absoluta incluyendo puerto activar CORS para que el cliente HMR pueda hacer peticiones al devServer de webpack // webpack.config.js
  26. 26. Django :8000 localhost:8000/ HTML localhost:8000/api/* JSON Webpack :8080 HMR
  27. 27. Implementar una API ¿REST? urlpatterns = [ url(r'^api/pics', api.pics), url(r'^', views.index), ] # backend/urls.py def pics(request): count = GifPicture.objects.all().count() all_ids = range(count) random.shuffle(all_ids) picked_ids = all_ids[:18] gif_pictures = GifPicture.objects .filter(id__in=picked_ids) .order_by('-upload_date') result = { 'pics': gif_pictures, } return JsonResponse(result) # backend/api.py import axios from 'axios'; export default { getRandomPics() { return axios.get('/api/pics') .then(response => { return response.data.pics; }); }, } // gifPicsApi.js Django :8000 … <script> import gifPicsApi from '../services/gifPicsApi.js'; export default { … mounted() { gifPicsApi.getRandomPics().then(pics => { this.pics = pics; }); }, }; </script> // GifHome.vue
  28. 28. DEMO 4
  29. 29. Autenticación Una posible implementación: 2PA http://localhost:8000/login http://localhost:8000/* create session redirect set cookie vue-routing
  30. 30. Django + auth + sessions <script> export default { name: 'app', data() { return { msg: 'Welcome to Your Vue.js App!', user: {}, } }, created() { this.user = window.user; }, } </script> // App.vue @login_required def index(request): context = { 'user': request.user, } return render(request, 'index.html', context) # backend/views.py {% extends 'base.html' %} {% load render_bundle from webpack_loader %} {% block content %} <div id="app"></div> <script> var user = { username: "{{ user.username }}", email: "{{ user.email }}", }; </script> {% render_bundle 'main' %} {% endblock %} # backend/templates/index.html
  31. 31. DEMO 5
  32. 32. Routing
  33. 33. DEMO 6
  34. 34. Rutas urlpatterns = [ url(r'^api/suggestions/$', api.suggestions), url(r'^api/search/$', api.search), url(r'^api/pics/(?P<id>[0-9]+)$', api.pic_details), url(r'^api/pics/$', api.pics), url(r'^', views.index), ] # backend/urls.py Vue.use(Router); const router = new Router({ mode: 'history', routes: [ { path: '/', name: 'home', component: GifHome },{ path: '/detail/:id', name: 'detail', component: GifDetail, props:true },{ path: '*', component: Error404 }, // Not found ], }); # router.js urlpatterns = [ url(r'^admin/', admin.site.urls), url('^', include('django.contrib.auth.urls')), url(r'^', include('backend.urls')) ] # urls.py /login /logout
  35. 35. ¿Es Django el mejor backend? código isomórfico server-side rendering async I/O
  36. 36. Comentarios Finales • APIs • REST? • ‘a pelo’ • django-tastypie • django-rest-framework • GraphQL • graphene (django) • apollo (vue) • (no lo he probado) • Server Side Rendering • nope • seeding • sip • SEO • pre-render • inyectar contenido en Django
  37. 37. Conclusión
  38. 38. Referencias • Doc de Vue: https://vuejs.org/v2/guide/single-file-components.html • Doc de Webpack: https://webpack.js.org/ • SurviveJS: https://survivejs.com/webpack/ • webpack-bundle-tracker: https://github.com/ezhome/webpack-bundle-tracker • django-webpack-loader: https://github.com/ezhome/django-webpack-loader • hello-vue + Django project: https://github.com/rokups/hello-vue-django • modernize Django frontend: http://owaislone.org/blog/modern-frontends-with-django/ • Django + REACT with HMR: http://owaislone.org/blog/webpack-plus-reactjs-and-django/ • Django-angular: http://django-angular.readthedocs.io/en/latest/index.html
  39. 39. Gracias! @javierabadia

Editor's Notes

  • v-if
    también hay v-show
  • como el ng-click
    funciona con todos los eventos: @hover @load
    ver las herramientas de desarrollo
  • $ npm install -g vue-cli
    $ vue init webpack-simple my-project
    $ cd my-project
    $ npm install
    $ npm run dev
  • $ npm install -g vue-cli
    $ vue init webpack-simple my-project
    $ cd my-project
    $ npm install
    $ npm run dev
  • webpack
    HMR
    DevTools
    ver el index.html
  • Vistas, Templates
    ORM, Modelos: migraciones, esquemas…
    Middleware, Autenticación
    Formularios, Administración

    Django has neatly packaged the best of web development in a very convenient project
  • yo uso PyCharm
    pero funciona igual lanzando los comandos desde el terminal y usando SublimeText u otro editor
  • webpack-bundle-tracker
    plug-in de webpack
    genera un fichero ’webpack-stats.json’ con los resultados de la compilación
    django-webpack-loader
    módulo de django
    lee el fichero ‘webpack-stats.json’ y renderiza las tags <script> apropiadas en una plantilla de Django



    Django debe servir el index.html de nuestra app
    El index.html ‘tira’ de los bundles generados por webpack
    webpack escucha, implementa HMR
  • En el servidor
    Simplemente usar JsonResponse()
    Django REST Framework
    no es necesario
    experiencia similar a los formularios de Django
    En el cliente
    axios
  • Django sirve 2 vistas
    vista de login
    vista de SPA

    Django mantiene las sesiones
    que se comparten con el cliente mediante una cookie
    Django comprueba la cookie/sesión en cada llamada
    podemos embeber la identidad en la plantilla

    Hay otras formas
    SPA -> TokenAuthentication (Django REST Framework)



    Types of authentication
    SessionAuthentication
    TokenAuthentication

    https://stackoverflow.com/a/27582256/79536

    http://www.django-rest-framework.org/topics/ajax-csrf-cors/
  • la vista principal tiene un <router-view></router-view>

    el router selecciona un componente para renderizar en ese punto a partir de la URL

    y mucho más: rutas anidadas, parámetros, guardas…
  • Django devuelve lo mismo para todas las rutas
    El vue-router selecciona la vista correcta
  • node es la elección natural cuando pensamos en un backend para vue
    un solo lenguaje
    isomorphic code
    server-side-rendering
    async I/O

    django
    Django has neatly packaged the best of web development in a very convenient project
    Access to other libs
    p.ej. pandas, machine learning, image recognition, ORM…

    laravel: default front-end for laravel (Jul 2016), made the framework take off



  • https://www.pydanny.com/choosing-an-api-framework-for-django.html

    https://django-tastypie.readthedocs.io/en/latest/
    http://www.django-rest-framework.org/

    https://github.com/graphql-python/graphene


    https://vuejsdevelopers.com/2017/04/01/vue-js-prerendering-node-laravel/
  • ×