Font End Development + Automation with Django

Post on 12-Nov-2014

5.381 views 2 download

Tags:

description

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

Transcript of Font End Development + Automation with Django

Tips & Tricks for Front End Development Automation

Evan Reiser

Front End Automation? Tools / Tricks to speed up front end dev work

Why talk about this? Same reason we all like Django:▪ More time solving our biz/eng problems▪ Less time doing the same repeatable patterns

We want to: Build web apps + add features Not: waste time deploying media,

organizing assets, minifying js/css, making sprites etc.

django experience

Co-founder @ GamerNook Social Network for Gamers▪ 100k+ Members

Co-founder @ BloomSpot Luxury Daily Deals▪ 3rd Largest Daily Deals site

Co-founder @ AdStack Algorithmically Managed

Facebook Advertising

Overview

Pains with Front end Development Deploying Media Files Compressing / Combine media files Generating Sprites Automate all of this

Problems / Pains

Annoying parts of front end development Deploying media to media server / CDN Combining JS / CSS files Minimizing JS / CSS Cache-busting media files in prod Building sprite images Referencing sprites in CSS Having to do this all manually

These are all common patterns We shouldn’t have to waste time on them

Tools

django.contrib.staticfiles django_storages boto

django_compressor YUI JS/CSS Compressor

django_media_bundler PIL pngcrush

django.contrib.staticfiles

Pulls media from various places Places them in another place

Different folder Media Server Or any custom storage backend▪ S3 / CloudFront▪CDN

Media Files => CDN

1. #settings.py

2. STATICFILES_DIRS = (3. os.path.join(os.getcwd(),"media"),4. )5. STATICFILES_FINDERS = (6. 'django.contrib.staticfiles.finders.FileSystemFinder',7. 'django.contrib.staticfiles.finders.AppDirectoriesFinder',8. )9. STATIC_ROOT = os.path.join(os.getcwd(),"static")

1) Set up static files to find

Media Files => CDN

1. #settings.py

2. STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage '

2) Set Storage backend to be S3 / CloudFront

• boto• Is a python library for interacting with

Amazon Web Services• django_storages• A series of storage backends including a

S3

Media Files => CDN

python manage.py collectstatic

2) Push media files to CDN

Media Files => CDN

1. #settings.py2. TEMPLATE_CONTEXT_PROCESSORS += (3. 'django.core.context_processors.static',4. )

4) Reference media in your templates

1. {# Some Template #}2. <link rel="stylesheet" href="{{STATIC_URL}}style.css" />

1. <link rel="stylesheet" href=“http://cdn.adstack.com/style.css" />

Managing Media

Managing Media CSS / JavaScript assets

We want to: Reduce Http requests Reduce bandwidth

We don’t want to: Think about front-end performance while

coding Interrupt our development

Managing Media

1. We want to reduce the number of HTTP requests for serving JS + CSS

<link rel="stylesheet" href="{{STATIC_URL}}css/admin/base.css" /><link rel="stylesheet" href="{{STATIC_URL}}css/admin/changelists.css" /><link rel="stylesheet" href="{{STATIC_URL}}css/icon_sprites.css"/><link rel="stylesheet" href="{{STATIC_URL}}css/common_10.css" /><link rel="stylesheet" href="{{STATIC_URL}}css/tablesorter.css" /><link rel="stylesheet" href="{{STATIC_URL}}css/multiselect.css" /><link rel="stylesheet" href="{{STATIC_URL}}css/superfish.css" />

<link rel="stylesheet" href="{{STATIC_URL}}css/all.css" />

Managing Media

2. We want to minify css / js content and only include what’s needed (reduce bandwidth)/*

Make sure to rename this file before you deploy to break client caching!!!*/

/* Headers */H1 {

font-weight:bold;}H2 {

font-weight;bold;}

H1,H2 {font-weight:bold}

Obviously the same thing goes for JS

Managing Media

3. Don’t want to have to change the asset names to prevent client side caching

Style.css Style2.css Style3.css Style4.css Style5.css

Managing Media

One way to do this is django_compressor Converts & combines linked or inline css/js

into cacheable static files Compression / minification support for: CSS

Tidy, YUI CSS + JS, Google’s Closure Compiler, JSmin, cssmin

Builds unique static file names to bust cache No need to heavily modify existing

templates Plus:▪ Extendable for custom post processing / filtering

via python

Managing Media Template changes are minimal

{% compress css %}<link href="{{STATIC_URL}}css/admin/base.css" /><link href="{{STATIC_URL}}css/admin/changelists.css" /><link href="{{STATIC_URL}}css/icon_sprites.css"/><link href="{{STATIC_URL}}css/common_10.css" /><link href="{{STATIC_URL}}css/tablesorter.css" /><link href="{{STATIC_URL}}css/multiselect.css" /><link href="{{STATIC_URL}}css/superfish.css" />{% endcompress %}

{% compress js %}<script src="{{STATIC_URL}}js/jquery-1.5.1.min.js"></script><script src="{{ STATIC_URL }}js/jquery.hoverIntent.js"></script><script src="{{STATIC_URL}}js/common.js"></script><script src="{{STATIC_URL}}js/jquery.multiselect.min.js"></script><script src="{{STATIC_URL}}js/jquery.cluetip.min.js"></script><script src="{{STATIC_URL}}js/jquery.tablesorter.js"></script><script src="{{ STATIC_URL }}js/jquery.tooltip.pack.js"></script><script src="{{ STATIC_URL }}js/superfish.js"></script>{% endcompress %}

Compressor Settings

1. from django.core.files.storage import get_storage_class2. from storages.backends.s3boto import S3BotoStorage3.  4. #A special Storage class that saves to S3 and Locally5. class CachedS3BotoStorage(S3BotoStorage):6.    7.     def __init__(self, *args, **kwargs):8.         super(CachedS3BotoStorage, self).__init__(*args, **kwargs)9.         self.local_storage = get_storage_class(10.            "compressor.storage.CompressorFileStorage")()11. 12.    def save(self, name, content):13.        name = super(CachedS3BotoStorage, self).save(name,content)14.        self.local_storage._save(name, content)15.        return name

We use a custom Storage Class to store the results on S3

Compressor Settings

1. import os2. STATICFILES_FINDERS += (3. 'compressor.finders.CompressorFinder',4. )

5. COMPRESS_OFFLINE = True6. COMPRESS_STORAGE = "common.common_storages.CachedS3BotoStorage"

7. COMPRESS_ROOT = os.path.join(os.path.dirname(__file__), 'media')8. COMPRESS_OUTPUT_DIR = "compress_cache"

9. COMPRESS_OFFLINE_CONTEXT = {"STATIC_URL":STATIC_URL}10.COMPRESS_URL = STATIC_URL11. 12.#post processing filters13.COMPRESS_CSS_FILTERS = ['compressor.filters.yui.YUICSSFilter‘]14.COMPRESS_JS_FILTERS = ['compressor.filters.jsmin.JSMinFilter']15.COMPRESS_YUI_BINARY = 'java -jar %s yuicompressor-2.4.6.jar'

Compressor settings for offline / pre-greneration

Managing Media

python manage.py compress

• combines static content• applies post processing filters• creates unique name from hash of

content• pushes to S3/CDN

Managing Media

{% compress css %}<link href="{{STATIC_URL}}css/admin/base.css" /><link href="{{STATIC_URL}}css/admin/changelists.css" /><link href="{{STATIC_URL}}css/icon_sprites.css"/><link href="{{STATIC_URL}}css/common_10.css" /><link href="{{STATIC_URL}}css/tablesorter.css" /><link href="{{STATIC_URL}}css/multiselect.css" /><link href="{{STATIC_URL}}css/superfish.css" />{% endcompress %}

<link rel="stylesheet" href="http://cdn.adstack.com/compress_cache/css/32dd63bd423c.css" />

When template renders Content is hashed => hash value stored

in cache Individual reference in template replaced

Automatic Sprites

What are sprites? Sprites are composite images made up of other images

Why? Reduce # HTTP requests

How? CSS is used to only show part of

the composite image

Automatic Sprites

How do we use sprites? Set them as a css background on a fixed

size element Position them so only part of the larger image shows

Cool. But this is really a pain▪ Creating images▪ Custom css for positioning

Automatic Sprites

• django_media_bundler • can help build the sprite automatically• Generates positioning CSS• Uses python + PIL to generate the sprite• Uses pngcrush to optimize the final

image size

• Use Case:• We have a folder full of icons• user.png, email.png, add.png

• We want to easily put these in templates• {% icon "email" %}

Automatic Sprites

• Simple example: lots of icons

*doing this can be a pain

Automatic Sprites

1. MEDIA_BUNDLES = (2.     {"type": "png-sprite",3.      "name": "icon",4.      "path": os.path.join(MEDIA_ROOT,"icons"),5.      "url": "../icons/",6.      "css_file": os.path.join(MEDIA_ROOT,"css","icon_sprites.css"),7.      "files": [8. “add.png”,9. “delete.png”,10. “user.png”,11. “delete.png”,12. “group.png”,13. ]14.    },15.)

1) Configure Media bundler settings

Automatic Sprites

1. MEDIA_BUNDLES = (2.     {"type": "png-sprite",3.      "name": "icon",4.      "path": os.path.join(MEDIA_ROOT,"icons"),5.      "url": "../icons/",6.      "css_file": os.path.join(MEDIA_ROOT,"css","icon_sprites.css"),7.      "files": filter(lambda x: x[-4:]== '.png',          8.                os.listdir(os.path.join(MEDIA_ROOT,"icons")))9.     },10.)

1) Configure Media bundler settings

Automatic Sprites

python manage.py bundle_media

2) Bundle our media

3) This creates a sprite.png + sprite.css

.icon {background-image: url('http://cdn.adstack.com/icons/sprites/icon110701154833.png?675d7dfaa1e1');}

.icon_user {width: 16px;background-position: 0px 0px;height: 16px;}.icon_add {width: 16px;background-position: -16px 0px;height: 16px;}

Etc…

<style>.icon {width: 16px;height: 16px;margin: 0;padding: 0;display: inline-block;position: relative;top: 2px;}</style>

<div><span class=“icon icon_user”></span> Hello Evan</div>

Automatic Sprites4) We can then show icons using css

Hello Evan

#front_end_tags.pyfrom django import templateregister = template.Library()

@register.inclusion_tag("common/front_end/icon.html")def icon(icon_name): return locals()

{# common/front_end/icon.html #}<span class="icon icon_{{ icon_name }}"></span>

<div> {% icon "user" %} Hello Evan</div>

5) We can easily write template tags to make this easier

6) Now it’s a bit easier to develop

Automatic Sprites

Automation

Fabric Deploy Script

from fabric.api import *def cdn_deploy():

run('python manage.py bundle_media')run('python manage.py collectstatic --noinput')run('python manage.py compress')

fab cdn_deploy

References django.contrib.staticfiles

https://docs.djangoproject.com/en/dev/howto/static-files/ Boto

http://code.google.com/p/boto/ Fabric

http://docs.fabfile.org/en/1.1.1/index.html django_compressor

http://code.google.com/p/django-compressor/ Boto

http://code.google.com/p/boto/ django_storages

https://bitbucket.org/david/django-storages/wiki/Home django_media_bundler

https://github.com/fabiocorneti/django-media-bundler Pngcrush

http://pmt.sourceforge.net/pngcrush/ YUI compressor

http://developer.yahoo.com/yui/compressor/

Questions?

Evan Reiserevan@adstack.com