pluggable django application patterns pycon 2011

61
Pluggable Patterns For Reusable Django Applications Friday, March 11, 2011

Upload: corey-oordt

Post on 17-May-2015

6.924 views

Category:

Technology


3 download

DESCRIPTION

12 Ways to get your apps pluggable or reusable given at PyCon 2011

TRANSCRIPT

Page 1: Pluggable Django Application Patterns PyCon 2011

Pluggable PatternsFor Reusable Django Applications

Friday, March 11, 2011

Page 2: Pluggable Django Application Patterns PyCon 2011

An app should not be a monolithic pile of code

For example, most blog “apps” available provide too much functionality

MyBlog App• Categories• Custom Tagging• Custom Comments • Comment

Moderation• Assumption of text

markup type• Single blogs• Multiple Sites

ACME MONOLITHS

Friday, March 11, 2011

Page 3: Pluggable Django Application Patterns PyCon 2011

An application should be “pluggable”

Friday, March 11, 2011

Page 4: Pluggable Django Application Patterns PyCon 2011

FocusedWrite programs that do one thing and do it well.

— Doug McIlroy (inventor of UNIX pipes)

A “pluggable” app is

Friday, March 11, 2011

Page 5: Pluggable Django Application Patterns PyCon 2011

Self-ContainedBatteries are included

Dependencies are declared

A “pluggable” app is

Friday, March 11, 2011

Page 6: Pluggable Django Application Patterns PyCon 2011

Easily AdaptableA “pluggable” app is

Corey’s Law: The less adaptable you make your code, the sooner you will be tasked to adapt it.

Friday, March 11, 2011

Page 7: Pluggable Django Application Patterns PyCon 2011

Easily InstalledA “pluggable” app is

pip install coolappYou did declare your dependencies, right?

Friday, March 11, 2011

Page 8: Pluggable Django Application Patterns PyCon 2011

How do we make a “pluggable” application?

Friday, March 11, 2011

Page 9: Pluggable Django Application Patterns PyCon 2011

Stop thinking like this

http://upload.wikimedia.org/wikipedia/commons/archive/a/aa/20090315161532!Ferrari_Enzo_Ferrari.JPGFriday, March 11, 2011

Page 10: Pluggable Django Application Patterns PyCon 2011

and think like this

Friday, March 11, 2011

Page 11: Pluggable Django Application Patterns PyCon 2011

Applications can have very different purposes

http://www.flickr.com/photos/tiemposdelruido/4051083769/Friday, March 11, 2011

Page 12: Pluggable Django Application Patterns PyCon 2011

Application Types

• Data. Manages specific data and access to itdjango.contrib.auth

• Utility. Provide a way of handling a specific problem for any applicationdjango-pagination, django.contrib.webdesign

• Decorator. Adds functionality to one or aggregates functionality of many applicationsdjango-mptt, django-tagging

Friday, March 11, 2011

Page 13: Pluggable Django Application Patterns PyCon 2011

Situation 1

You want to configure your app without modifying its code

(e.g. API keys)

Friday, March 11, 2011

Page 14: Pluggable Django Application Patterns PyCon 2011

Configurable Options

from django.conf import settings

MODULES = getattr(settings, 'SUPERTAGGING_MODULES', {})

Django Supertagging http://github.com/josesoa

Internal Name Setting Name Default Value

Friday, March 11, 2011

Page 15: Pluggable Django Application Patterns PyCon 2011

Configurable Options

from django.conf import settings

TOOLBAR_CONFIG = { 'INTERCEPT_REDIRECTS': True, 'SHOW_TOOLBAR_CALLBACK': default_show_toolbar, 'EXTRA_SIGNALS': [], 'HIDE_DJANGO_SQL': True, 'SHOW_TEMPLATE_CONTEXT': True, 'TAG': 'body',}

TOOLBAR_CONFIG.update(getattr(settings, 'DEBUG_TOOLBAR_CONFIG', {}))

Django Debug Toolbar http://github.com/robhudson

Friday, March 11, 2011

Page 16: Pluggable Django Application Patterns PyCon 2011

Data Apps

http://www.flickr.com/photos/29276244@N03/3200630853/Friday, March 11, 2011

Page 17: Pluggable Django Application Patterns PyCon 2011

Situation 2

Lots of variationsEach implementation is different

(e.g. blogs)

Friday, March 11, 2011

Page 18: Pluggable Django Application Patterns PyCon 2011

Abstract Models

EntryBase

FeaturableEntryMixin

StatusableEntryMixin

TaggableEntryMixin

HTMLFormattableEntryMixin

GLAMKit http://www.glamkit.org/

class PRBlog(EntryBase, StatusableEntryMixin):

subhead = models.CharField(… pdf = models.FileField(…

Friday, March 11, 2011

Page 19: Pluggable Django Application Patterns PyCon 2011

Situation 3

A few, well-known of variations(e.g. Use django.contrib.sites?)

Friday, March 11, 2011

Page 20: Pluggable Django Application Patterns PyCon 2011

Optional Field Settings

from django.db import modelsfrom myapp.settings import MULTIPLE_SITES

if MULTIPLE_SITES: from django.contrib.sites.models import Site

class Entry(models.Model): title = models.CharField(max_length=100) … if MULTIPLE_SITES: sites = models.ManyToManyField(Site)

Friday, March 11, 2011

Page 21: Pluggable Django Application Patterns PyCon 2011

Situation 4

Optionally use another application

(e.g. Use django-tagging?)

Friday, March 11, 2011

Page 22: Pluggable Django Application Patterns PyCon 2011

Optional Integration

from django.db import modelsfrom myapp.settings import USE_TAGGING

if USE_TAGGING: from tagging.fields import TagField

class Entry(models.Model): title = models.CharField(max_length=100) … if USE_TAGGING: tags = TagField()

Friday, March 11, 2011

Page 23: Pluggable Django Application Patterns PyCon 2011

Situation 5

You want to reference different models

(e.g. Customizable author field)

Friday, March 11, 2011

Page 24: Pluggable Django Application Patterns PyCon 2011

Runtime Configurable Foreign Keys

from django.conf import settingsfrom django.db.models import get_model

model_string = getattr(settings, 'VIEWPOINT_AUTHOR_MODEL', 'auth.User')AUTHOR_MODEL = get_model(*model_string.split('.'))

Viewpoint http://github.com/washingtontimes

Friday, March 11, 2011

Page 25: Pluggable Django Application Patterns PyCon 2011

Runtime Configurable Foreign Keys

from viewpoint.settings import AUTHOR_MODEL

class Entry(models.Model): title = models.CharField(max_length=100) author = models.ForeignKey(AUTHOR_MODEL) …

Viewpoint http://github.com/washingtontimes

Friday, March 11, 2011

Page 26: Pluggable Django Application Patterns PyCon 2011

Utility Apps

http://www.flickr.com/photos/s8/3638531205/Friday, March 11, 2011

Page 27: Pluggable Django Application Patterns PyCon 2011

Required for template tags or

management commands

models.py

Friday, March 11, 2011

Page 28: Pluggable Django Application Patterns PyCon 2011

Decorator Apps

http://www.flickr.com/photos/yum9me/2109549869/Friday, March 11, 2011

Page 29: Pluggable Django Application Patterns PyCon 2011

New FieldNew Method

New AdminCustom Manager

Friday, March 11, 2011

Page 30: Pluggable Django Application Patterns PyCon 2011

Situation 6

You want to add a field to a model

Friday, March 11, 2011

Page 31: Pluggable Django Application Patterns PyCon 2011

Lazy Field InsertionDjango Categories http://github.com/washingtontimes

Friday, March 11, 2011

Page 32: Pluggable Django Application Patterns PyCon 2011

Lazy Field Insertion

CATEGORY_FOREIGNKEYS = { 'app1.Model': ('category',) 'app2.Model': ('primary_category', 'secondary_category')}

Django Categories http://github.com/washingtontimes

Friday, March 11, 2011

Page 33: Pluggable Django Application Patterns PyCon 2011

Lazy Field Insertionfrom django.db.models import get_modelimport django.conf import settings

FOREIGNKEYS = getattr(settings, 'CATEGORY_FOREIGNKEYS', {})

for model_name, cat_fields in FOREIGNKEYS.items(): if not isinstance(model_name, basestring): continue model = get_model(*model_name.split('.')) for category_field in list(cat_fields): try: model._meta.get_field(category_field) except FieldDoesNotExist: ForeignKey(Category).contribute_to_class( model, category_field)

Django Categories http://github.com/washingtontimes

Friday, March 11, 2011

Page 34: Pluggable Django Application Patterns PyCon 2011

Situation 7

You want to add a custom manager to a model

Friday, March 11, 2011

Page 35: Pluggable Django Application Patterns PyCon 2011

Lazy Manager Insertion

COOLAPP_MODELS = { 'app1.Model': 'cool_manager', 'app2.Model': 'cool_manager',}

Django MPTT http://github.com/django-mptt

Friday, March 11, 2011

Page 36: Pluggable Django Application Patterns PyCon 2011

Adding a manager

from django.db.models import get_modelimport django.conf import settingsfrom coolapp.managers import CustomManager

MODELS = getattr(settings, 'COOLAPP_MODELS', {})

for model_name, mgr_name in MODELS.items(): if not isinstance(model_name, basestring): continue model = get_model(*model_name.split('.')) if not getattr(model, mgr_name, False): manager = CustomManager() manager.contribute_to_class(model, mgr_name)

Django MPTT http://github.com/django-mptt

Friday, March 11, 2011

Page 37: Pluggable Django Application Patterns PyCon 2011

Situation 8

You want to customize a model’s ModelAdmin

(e.g. Change the widget of a field)

Friday, March 11, 2011

Page 38: Pluggable Django Application Patterns PyCon 2011

Lazy Registration of a Custom ModelAdmin

TINYMCE_ADMIN_FIELDS = { 'app1.model1': ('body',), 'app1.model2': ('blog_text', 'blog_teaser')}

Django TinyMCE http://github.com/justquick

project’s settings.pyFriday, March 11, 2011

Page 39: Pluggable Django Application Patterns PyCon 2011

Lazy Registration of a Custom ModelAdmin

from django.db.models import get_modelimport django.conf import settings

REGISTRY = {}ADMIN_FIELDS = getattr(settings, 'TINYMCE_ADMIN_FIELDS', {})

for model_name, field in ADMIN_FIELDS.items(): if isinstance(model_name, basestring): model = get_model(*model_name.split('.')) if model in registry: return REGISTRY[model] = field

Django TinyMCE http://github.com/justquick

Django-TinyMCE’s models.pyFriday, March 11, 2011

Page 40: Pluggable Django Application Patterns PyCon 2011

Lazy Registration of a Custom ModelAdmin

# Define a new ModelAdmin subclass

class TinyMCEAdmin(admin.ModelAdmin): editor_fields = ()

def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name in self.editor_fields: return db_field.formfield(widget=TinyMCE()) return super(TinyMCEAdmin, self).formfield_for_dbfield( db_field, **kwargs)

Django TinyMCE http://github.com/justquick

Django-TinyMCE’s admin.pyFriday, March 11, 2011

Page 41: Pluggable Django Application Patterns PyCon 2011

Lazy Registration of a Custom ModelAdmin

for model, modeladmin in admin.site._registry.items(): if model in REGISTRY: admin.site.unregister(model) admin.site.register( model, type('newadmin', (TinyMCEAdmin, modeladmin.__class__), {'editor_fields': REGISTRY[model],} ) )

Django TinyMCE http://github.com/justquick

bottom of Django-TinyMCE’s admin.pyFriday, March 11, 2011

Page 42: Pluggable Django Application Patterns PyCon 2011

Touch Points

Friday, March 11, 2011

Page 43: Pluggable Django Application Patterns PyCon 2011

Touch Points of an App

The parts of an application that usually need tweaking• URLs• Templates• View responses

Friday, March 11, 2011

Page 44: Pluggable Django Application Patterns PyCon 2011

Situation 9

You want the URLs of your app to live under any prefix

(e.g. /blogs/ vs. /weblogs/)

Friday, March 11, 2011

Page 45: Pluggable Django Application Patterns PyCon 2011

Name your URLs

from django.conf.urls.defaults import *

urlpatterns = patterns('', (r'^$', 'coolapp_app.views.index'),)

Friday, March 11, 2011

Page 46: Pluggable Django Application Patterns PyCon 2011

Name your URLs

from django.conf.urls.defaults import *

urlpatterns = patterns('', url(r'^$', 'coolapp_app.views.index', name='coolapp_index'),)

url Function name

Friday, March 11, 2011

Page 47: Pluggable Django Application Patterns PyCon 2011

Reference your URLs by name

<p>Go to the <a href="{% url coolapp_index %}">Index</a></p>

from django.core.urlresolvers import reverse

def myview(request): return HttpResponseRedirect(reverse('coolapp_index', args=[]))

Friday, March 11, 2011

Page 48: Pluggable Django Application Patterns PyCon 2011

Situation 10

You want your templates to be easily overridable

Friday, March 11, 2011

Page 49: Pluggable Django Application Patterns PyCon 2011

“Namespace” Templates

coolapp

templates

coolapp

All templates in a template

“name space”

base.html

Friday, March 11, 2011

Page 50: Pluggable Django Application Patterns PyCon 2011

“Namespace” Templates

coolapp

templates

coolapp

Referenced as “coolapp/base.html”

base.html

Friday, March 11, 2011

Page 51: Pluggable Django Application Patterns PyCon 2011

Extend one template

index.html

base.html

detail.htmlsummary.html

{% extends “base.html” %}

site_base.html

Friday, March 11, 2011

Page 52: Pluggable Django Application Patterns PyCon 2011

Extend one template

index.html

base.html

detail.htmlsummary.html

{% extends “coolapp/base.html” %}base.html

site_base.html

Friday, March 11, 2011

Page 53: Pluggable Django Application Patterns PyCon 2011

Extend one template

index.html

base.html

detail.htmlsummary.html

{% extends “coolapp/base.html” %}base.html

site_base.html

Friday, March 11, 2011

Page 54: Pluggable Django Application Patterns PyCon 2011

Extend one template

coolapp/base.html

{% extends "base.html" %}

{% block head %}{% endblock %}

{% block body %}{% endblock %}

Friday, March 11, 2011

Page 55: Pluggable Django Application Patterns PyCon 2011

Extend one template

coolapp/base.html

{% extends "base.html" %}

{% block head %}{% endblock %}

{% block body %}{% endblock %}

{% extends "site_base.html" %}

{% block extra_head %} {% block head %} {% endblock %}{% endblock %}

{% block content %} {% block body %} {% endblock %}{% endblock %}

Friday, March 11, 2011

Page 56: Pluggable Django Application Patterns PyCon 2011

Import your blocks

{% extends "coolapp/base.html" %}

{% block extra_head %} {{ block.super }} {% import "coolapp/extra_head.html" %}{% endblock %}

{% block content %} {# Important content stuff here #}

{% endblock %}

extra_head.html

coolapp/detail.html

Allows you to override any of the templates

Friday, March 11, 2011

Page 57: Pluggable Django Application Patterns PyCon 2011

Situation 11

You want flexibility storing uploaded files

Friday, March 11, 2011

Page 58: Pluggable Django Application Patterns PyCon 2011

Define a Storage Optionfrom django.conf import settingsfrom django.core.files.storage import get_storage_class

DEFAULT_STORAGE = get_storage_class(getattr(settings, "MMEDIA_DEFAULT_STORAGE", settings.DEFAULT_FILE_STORAGE)

)

from massmedia.settings import IMAGE_UPLOAD_TO, DEFAULT_STORAGE

class Image(models.Model): file = models.FileField( upload_to = IMAGE_UPLOAD_TO, blank = True, null = True, storage=DEFAULT_STORAGE())

Friday, March 11, 2011

Page 59: Pluggable Django Application Patterns PyCon 2011

Situation 12

You want to alter the data your views use

(e.g. Extra context, different template)

Friday, March 11, 2011

Page 60: Pluggable Django Application Patterns PyCon 2011

100% more class-based views!

django-cbv for backwards

compatibility!

Friday, March 11, 2011

Page 61: Pluggable Django Application Patterns PyCon 2011

My Info

[email protected]

• @coordt

• github.com/coordt

• github.com/washingtontimes

• opensource.washingtontimes.com

Friday, March 11, 2011