Font End Development + Automation with Django

5,486 views
5,351 views

Published on

Tips and tricks for front end development automation using python and django by Evan Reiser

Published in: Technology, Education
0 Comments
18 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,486
On SlideShare
0
From Embeds
0
Number of Embeds
14
Actions
Shares
0
Downloads
72
Comments
0
Likes
18
Embeds 0
No embeds

No notes for slide
  • How does this relate to Continuous Integration?
  • I’ve worked on a broad range of startups and I’ve w
  • We need this custom storage backend so that it writes both locally and pushes it to S3. I think its local because it will then check to see if it exists on disk and if so it will assume it exists on the CDN
  • You can have your system compress these things on the fly, but I imagine in most cases you actually want it to compress all your media as part of your deploy process
  • Font End Development + Automation with Django

    1. 1. Tips & Tricks for Front End Development Automation<br />Evan Reiser<br />
    2. 2. Front End Automation?<br />Tools / Tricks to speed up front end dev work<br />Why talk about this?<br />Same reason we all like Django:<br />More time solving our biz/eng problems<br />Less time doing the same repeatable patterns<br />We want to: Build web apps + add features<br />Not: waste time deploying media, organizing assets, minifying js/css, making sprites etc.<br />
    3. 3. django experience<br />Co-founder @ GamerNook<br />Social Network for Gamers<br />100k+ Members<br />Co-founder @ BloomSpot<br />Luxury Daily Deals<br />3rd Largest Daily Deals site<br />Co-founder @ AdStack<br />Algorithmically Managed Facebook Advertising<br />
    4. 4. Overview<br />Pains with Front end Development<br />Deploying Media Files<br />Compressing / Combine media files<br />Generating Sprites<br />Automate all of this<br />
    5. 5. Problems / Pains<br />Annoying parts of front end development<br />Deploying media to media server / CDN<br />Combining JS / CSS files<br />Minimizing JS / CSS<br />Cache-busting media files in prod<br />Building sprite images<br />Referencing sprites in CSS<br />Having to do this all manually<br />These are all common patterns<br />We shouldn’t have to waste time on them<br />
    6. 6. Tools<br />django.contrib.staticfiles<br />django_storages<br />boto<br />django_compressor<br />YUI JS/CSS Compressor<br />django_media_bundler<br />PIL<br />pngcrush<br />
    7. 7. django.contrib.staticfiles<br />Pulls media from various places<br />Places them in another place<br />Different folder<br />Media Server<br />Or any custom storage backend<br />S3 / CloudFront<br />CDN <br />
    8. 8. Media Files => CDN<br />1) Set up static files to find <br />#settings.py<br />STATICFILES_DIRS = (<br />os.path.join(os.getcwd(),"media"),<br /> )<br />STATICFILES_FINDERS = (<br /> 'django.contrib.staticfiles.finders.FileSystemFinder',<br /> 'django.contrib.staticfiles.finders.AppDirectoriesFinder',<br />)<br />STATIC_ROOT = os.path.join(os.getcwd(),"static")<br />
    9. 9. Media Files => CDN <br />2) Set Storage backend to be S3 / CloudFront<br />#settings.py<br />STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage '<br /><ul><li>boto
    10. 10. Is a python library for interacting with Amazon Web Services
    11. 11. django_storages
    12. 12. A series of storage backends including a S3</li></li></ul><li>Media Files => CDN<br />2) Push media files to CDN<br />python manage.py collectstatic<br />
    13. 13. Media Files => CDN <br />4) Reference media in your templates<br />#settings.py<br />TEMPLATE_CONTEXT_PROCESSORS += (<br /> 'django.core.context_processors.static',<br />)<br />{# Some Template #}<br /><link rel="stylesheet" href="{{STATIC_URL}}style.css" /><br /><link rel="stylesheet" href=“http://cdn.adstack.com/style.css" /><br />
    14. 14. Managing Media<br />Managing Media<br />CSS / JavaScript assets<br />We want to:<br />Reduce Http requests<br />Reduce bandwidth<br />We don’t want to:<br />Think about front-end performance while coding<br />Interrupt our development<br />
    15. 15. Managing Media<br />1. We want to reduce the number of HTTP requests for serving JS + CSS<br /><link rel="stylesheet" href="{{STATIC_URL}}css/admin/base.css" /><br /><link rel="stylesheet" href="{{STATIC_URL}}css/admin/changelists.css" /><br /><link rel="stylesheet" href="{{STATIC_URL}}css/icon_sprites.css"/><br /><link rel="stylesheet" href="{{STATIC_URL}}css/common_10.css" /><br /><link rel="stylesheet" href="{{STATIC_URL}}css/tablesorter.css" /><br /><link rel="stylesheet" href="{{STATIC_URL}}css/multiselect.css" /><br /><link rel="stylesheet" href="{{STATIC_URL}}css/superfish.css" /><br /><link rel="stylesheet" href="{{STATIC_URL}}css/all.css" /><br />
    16. 16. Managing Media<br />2. We want to minify css / js content and only include what’s needed (reduce bandwidth)<br />/* <br /> Make sure to rename this file before you deploy to break client caching!!!<br />*/<br />/* Headers */<br />H1 { <br />font-weight:bold;<br />}<br />H2 {<br />font-weight;bold;<br />}<br />H1,H2 {font-weight:bold}<br />Obviously the same thing goes for JS<br />
    17. 17. Managing Media<br />3. Don’t want to have to change the asset names to prevent client side caching<br />Style.css<br />Style2.css<br />Style3.css<br />Style4.css<br />Style5.css<br />
    18. 18. Managing Media<br />One way to do this is django_compressor<br />Converts & combines linked or inline css/js into cacheable static files<br />Compression / minification support for: CSS Tidy, YUI CSS + JS, Google’s Closure Compiler, JSmin, cssmin<br />Builds unique static file names to bust cache<br />No need to heavily modify existing templates<br />Plus:<br />Extendable for custom post processing / filtering via python<br />
    19. 19. Managing Media<br />Template changes are minimal<br />{% compress css %}<br /><link href="{{STATIC_URL}}css/admin/base.css" /><br /><link href="{{STATIC_URL}}css/admin/changelists.css" /><br /><link href="{{STATIC_URL}}css/icon_sprites.css"/><br /><link href="{{STATIC_URL}}css/common_10.css" /><br /><link href="{{STATIC_URL}}css/tablesorter.css" /><br /><link href="{{STATIC_URL}}css/multiselect.css" /><br /><link href="{{STATIC_URL}}css/superfish.css" /><br />{% endcompress %}<br />{% compress js %}<br /><script src="{{STATIC_URL}}js/jquery-1.5.1.min.js"></script><br /><script src="{{ STATIC_URL }}js/jquery.hoverIntent.js"></script><br /><script src="{{STATIC_URL}}js/common.js"></script><br /><script src="{{STATIC_URL}}js/jquery.multiselect.min.js"></script><br /><script src="{{STATIC_URL}}js/jquery.cluetip.min.js"></script><br /><script src="{{STATIC_URL}}js/jquery.tablesorter.js"></script><br /><script src="{{ STATIC_URL }}js/jquery.tooltip.pack.js"></script><br /><script src="{{ STATIC_URL }}js/superfish.js"></script><br />{% endcompress %}<br />
    20. 20. Compressor Settings<br />We use a custom Storage Class to store the results on S3<br />from django.core.files.storage import get_storage_class<br />from storages.backends.s3boto import S3BotoStorage<br /> <br />#A special Storage class that saves to S3 and Locally<br />class CachedS3BotoStorage(S3BotoStorage):<br />   <br />    def __init__(self, *args, **kwargs):<br />        super(CachedS3BotoStorage, self).__init__(*args, **kwargs)<br />        self.local_storage = get_storage_class(<br />            "compressor.storage.CompressorFileStorage")()<br /> <br />    def save(self, name, content):<br />        name = super(CachedS3BotoStorage, self).save(name,content)<br />        self.local_storage._save(name, content)<br />        return name<br />
    21. 21. Compressor Settings<br />Compressor settings for offline / pre-greneration<br />import os<br />STATICFILES_FINDERS += (<br /> 'compressor.finders.CompressorFinder',<br />)<br />COMPRESS_OFFLINE = True<br />COMPRESS_STORAGE = "common.common_storages.CachedS3BotoStorage"<br />COMPRESS_ROOT = os.path.join(os.path.dirname(__file__), 'media')<br />COMPRESS_OUTPUT_DIR = "compress_cache"<br />COMPRESS_OFFLINE_CONTEXT = {"STATIC_URL":STATIC_URL}<br />COMPRESS_URL = STATIC_URL<br /> <br />#post processing filters<br />COMPRESS_CSS_FILTERS = ['compressor.filters.yui.YUICSSFilter‘]<br />COMPRESS_JS_FILTERS = ['compressor.filters.jsmin.JSMinFilter']<br />COMPRESS_YUI_BINARY = 'java -jar %s yuicompressor-2.4.6.jar'<br />
    22. 22. Managing Media<br />python manage.py compress<br /><ul><li>combines static content
    23. 23. applies post processing filters
    24. 24. creates unique name from hash of content
    25. 25. pushes to S3/CDN</li></li></ul><li> Managing Media<br />When template renders<br />Content is hashed => hash value stored in cache<br />Individual reference in template replaced<br />{% compress css %}<br /><link href="{{STATIC_URL}}css/admin/base.css" /><br /><link href="{{STATIC_URL}}css/admin/changelists.css" /><br /><link href="{{STATIC_URL}}css/icon_sprites.css"/><br /><link href="{{STATIC_URL}}css/common_10.css" /><br /><link href="{{STATIC_URL}}css/tablesorter.css" /><br /><link href="{{STATIC_URL}}css/multiselect.css" /><br /><link href="{{STATIC_URL}}css/superfish.css" /><br />{% endcompress %}<br /><link rel="stylesheet" href="http://cdn.adstack.com/compress_cache/css/32dd63bd423c.css" /><br />
    26. 26. Automatic Sprites<br />What are sprites?<br />Sprites are composite images <br /> made up of other images<br />Why?<br />Reduce # HTTP requests<br />How?<br />CSS is used to only show part of the composite image<br />
    27. 27. Automatic Sprites<br />How do we use sprites?<br />Set them as a css background on a fixed size element<br />Position them so only<br /> part of the larger<br /> image shows<br />Cool.<br />But this is really a pain<br />Creating images<br />Custom css for positioning<br />
    28. 28. Automatic Sprites<br /><ul><li>django_media_bundler
    29. 29. can help build the sprite automatically
    30. 30. Generates positioning CSS
    31. 31. Uses python + PIL to generate the sprite
    32. 32. Uses pngcrush to optimize the final image size
    33. 33. Use Case:
    34. 34. We have a folder full of icons
    35. 35. user.png, email.png, add.png
    36. 36. We want to easily put these in templates
    37. 37. {% icon "email" %}</li></li></ul><li>Automatic Sprites<br /><ul><li>Simple example: lots of icons</li></ul>*doing this can be a pain<br />
    38. 38. Automatic Sprites<br />1) Configure Media bundler settings<br />MEDIA_BUNDLES = (<br />    {"type": "png-sprite",<br />     "name": "icon",<br />     "path": os.path.join(MEDIA_ROOT,"icons"),<br />     "url": "../icons/",<br />     "css_file": os.path.join(MEDIA_ROOT,"css","icon_sprites.css"),<br />     "files": [<br /> “add.png”,<br />“delete.png”,<br />“user.png”,<br />“delete.png”,<br />“group.png”,<br /> ]<br />    },<br />)<br />
    39. 39. Automatic Sprites<br />1) Configure Media bundler settings<br />MEDIA_BUNDLES = (<br />    {"type": "png-sprite",<br />     "name": "icon",<br />     "path": os.path.join(MEDIA_ROOT,"icons"),<br />     "url": "../icons/",<br />     "css_file": os.path.join(MEDIA_ROOT,"css","icon_sprites.css"),<br />     "files": filter(lambda x: x[-4:]== '.png',          <br />               os.listdir(os.path.join(MEDIA_ROOT,"icons")))<br />    },<br />)<br />
    40. 40. Automatic Sprites<br />2) Bundle our media<br />python manage.py bundle_media<br />3) This creates a sprite.png + sprite.css<br />.icon {background-image: url('http://cdn.adstack.com/icons/sprites/icon110701154833.png?675d7dfaa1e1');}<br />.icon_user {width: 16px;background-position: 0px 0px;height: 16px;}<br />.icon_add {width: 16px;background-position: -16px 0px;height: 16px;}<br />Etc…<br />
    41. 41. <style><br />.icon {width: 16px;height: 16px;margin: 0;padding: 0;display: inline-block;position: relative;top: 2px;}<br /></style><br /><div><span class=“icon icon_user”></span> Hello Evan</div><br />Automatic Sprites<br />4) We can then show icons using css<br />Hello Evan<br />
    42. 42. Automatic Sprites<br />5) We can easily write template tags to make this easier<br />#front_end_tags.py<br />from django import template<br />register = template.Library()<br />@register.inclusion_tag("common/front_end/icon.html")<br />def icon(icon_name):<br /> return locals()<br />{# common/front_end/icon.html #}<br /><span class="icon icon_{{ icon_name }}"></span><br />6) Now it’s a bit easier to develop<br /><div> {% icon "user" %} Hello Evan</div><br />
    43. 43. Automation<br />Fabric Deploy Script <br />from fabric.api import *<br />defcdn_deploy():<br />run('python manage.py bundle_media')<br />run('python manage.py collectstatic --noinput')<br />run('python manage.py compress')<br />fab cdn_deploy<br />
    44. 44. References<br />django.contrib.staticfiles<br />https://docs.djangoproject.com/en/dev/howto/static-files/<br />Boto<br />http://code.google.com/p/boto/<br />Fabric<br />http://docs.fabfile.org/en/1.1.1/index.html<br />django_compressor<br />http://code.google.com/p/django-compressor/<br />Boto<br />http://code.google.com/p/boto/<br />django_storages<br />https://bitbucket.org/david/django-storages/wiki/Home<br />django_media_bundler<br />https://github.com/fabiocorneti/django-media-bundler<br />Pngcrush<br />http://pmt.sourceforge.net/pngcrush/<br />YUI compressor<br />http://developer.yahoo.com/yui/compressor/<br />
    45. 45. Questions?<br />Evan Reiser<br />evan@adstack.com<br />

    ×