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

Pluggable Django Application Patterns PyCon 2011

Embed Size (px)

DESCRIPTION

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

Citation preview

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