Pluggable patterns
-
Upload
corey-oordt -
Category
Documents
-
view
1.561 -
download
0
Transcript of Pluggable patterns
Project
TemplatesURL routingConfiguration
Application Application Application
Application Application Application
Application Application Application
Project
TemplatesURL routingConfiguration
Application Application Application
Application Application Application
Application Application Application
Here there be power!
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
FocusedWrite programs that do one thing and do it well.
— Doug McIlroy (inventor of UNIX pipes)
A “pluggable” app is
Easily AdaptableA “pluggable” app is
Corey’s Law: The less adaptable you make your code, the
sooner you will be tasked to adapt it.
Stop thinking like this
http://upload.wikimedia.org/wikipedia/commons/archive/a/aa/20090315161532!Ferrari_Enzo_Ferrari.JPG
Applications can have very different purposes
http://www.photoshoproadmap.com/imagenes/blog/lego-brushes/lego-bricks-high-resolution.jpg
Application Types
• Data. Manages specific data and access to it
django.contrib.auth
• Utility. Provide a way of handling a specific
problem for any application
django-pagination, django.contrib.webdesign
• Decorator. Adds functionality to one or
aggregates functionality of many applications
django-mptt, django-tagging
Abstract Models
EntryBase
FeaturableEntryMixin
StatusableEntryMixin
TaggableEntryMixin
HTMLFormattableEntryMixin
GLAMKit http://www.glamkit.org/
class PRBlog(EntryBase, StatusableEntryMixin):
subhead = models.CharField(… pdf = models.FileField(…
Optional Field Settings
from django.conf import settingsfrom django.db import models
MULTIPLE_SITES = getattr(settings, 'BLOG_MULTIPLE_SITES', False)
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)
Optional Integration
from django.conf import settingsfrom django.db import models
try: from tagging.fields import TagField HAS_TAGGING = Trueexcept ImportError: HAS_TAGGING = False
Optional Integration
from django.conf import settingsfrom django.db import modelsfrom myapp.settings import USE_TAGGING
if USE_TAGGING and 'tagging' in settings.INSTALLED_APPS: from tagging.fields import TagField HAS_TAGGING = Trueelse: HAS_TAGGING = False
class Entry(models.Model): title = models.CharField(max_length=100) … if HAS_TAGGING: tags = TagField()
Configurable Foreign Keys
from django.db.models import get_modelfrom django.conf import settings
model_string = getattr(settings, 'AUTHOR', 'auth.User')AUTHOR_MODEL = get_model(*model_string.split('.'))
class Entry(models.Model): title = models.CharField(max_length=100) author = models.ForeignKey(AUTHOR_MODEL) …
Viewpoint http://github.com/washingtontimes
Emit Signals
In signals.pyimport django.dispatchsatchmo_cart_add_complete = django.dispatch.Signal()satchmo_cart_changed = django.dispatch.Signal()
In a view in views.pysatchmo_cart_add_complete.send( cart, cart=cart, cartitem=added_item, product=product, request=request, form=formdata)
satchmo_cart_changed.send(cart, cart=cart, request=request)
Satchmo http://www.satchmoproject.com/
Does it need to be in INSTALLED_APPS?
Yes, if you have:
• Templates
• Template Tags
• Management Commands
Simple Model Registry
registry = []
def register(model): if model in registry: return registry.append(model)
from coolapp import registerfrom models import MyModel
register(MyModel)
Model-Data Registry
registry = {}
def register(model, handler): if model in registry: return registry[model] = handler
from coolapp import registerfrom models import MyModel, my_handler
register(MyModel, my_handler)
Lazy RegistrationTINYMCE_ADMIN_FIELDS = { 'app1.model1': ('body',), 'app1.model2': ('blog_text', 'blog_teaser')}
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
Simple Model Registryfrom django.db.models import FieldDoesNotExist, ForeignKeyfrom categories.models import Category
registry = []
def register(model, category_field='category'): if model in registry: return registry.append(model)
opts = model._meta try: opts.get_field(category_field) except FieldDoesNotExist: ForeignKey(Category).contribute_to_class( model, category_field)
Adding methodsfrom django.db.models import FieldDoesNotExist, IntegerFieldfrom coolcategories import functions
registry = []
def register(model, ...): if model in registry: return registry.append(model) TreeManager(parent_attr, left_attr, right_attr, tree_id_attr, level_attr).contribute_to_class(model, tree_manager_attr)
setattr(model, 'insert_at', functions.insert_at)
Lazy Registration of a Custom ModelAdmin
TINYMCE_ADMIN_FIELDS = { 'app1.model1': ('body',), 'app1.model2': ('blog_text', 'blog_teaser')}
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
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)
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],} ) )
Touch Points of an App
The parts of an application that usually need tweaking• URLs• Templates• Configurable options• View responses
Name your URLs
from django.conf.urls.defaults import *
urlpatterns = patterns('', url(r'^$', 'coolapp_app.views.index', name='coolapp_index'),)
Reference your URLs by name
<p>Go to the <a href="{% url index %}">Index</a></p>
from django.core.urlresolvers import reverse
def myview(request): return HttpResponseRedirect(reverse('index', args=[]))
“Namespace” Templates
coolapp
templates
coolapp
base.html
This template simply
{% extends “base.html” %}
Import your blocks
{% extends "base.html" %}
{% block extra_head %} {{ block.super }} {% import "coolapp/extra_head.html" %}{% endblock %}
{% block content %} {% import "coolapp/content.html" %}{% endblock %}
extra_head.html
content.html
base.html
Allows you to override any of the templates
Configurable Options
from django.conf import settings
MODULES = getattr(settings, 'SUPERTAGGING_MODULES', {})
Django Supertagging http://github.com/josesoa
Internal Name Setting Name Default Value
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
Define a Storage Option# How to store the files. The ``DEFAULT_STORAGE`` is used for all media types# unless overridden by another setting.## Should be a string in the format: 'module.Class'# **Default:** 'django.core.files.storage.FileSystemStorage'DEFAULT_STORAGE = getattr(settings, "MMEDIA_DEFAULT_STORAGE", settings.DEFAULT_FILE_STORAGE)
from massmedia import settings as appsettingsfrom django.core.files.storage import get_storage_class
IMAGE_STORAGE = get_storage_class(appsettings.DEFAULT_STORAGE)
class Image(models.Model): file = models.FileField( upload_to = appsettings.IMAGE_UPLOAD_TO, blank = True, null = True, storage=IMAGE_STORAGE())
Add keyword arguments
def register(request, backend, success_url=None, form_class=None, disallowed_url='registration_disallowed', template_name='registration/registration_form.html', extra_context=None):
Django Registration http://bitbucket.org/ubernostrum
urlpatterns = patterns('', url(r'^register/$', 'registration.views.register', {'template_name': 'my_registration_form.html'}, name='coolapp_index'),)
Class-based Viewshttp://github.com/bfirsh/django-class-based-views
http://github.com/pegasus/django-baseviews
http://code.djangoproject.com/ticket/6735
http://codysoyland.com/2010/feb/3/thread-safe-object-
oriented-views-django/
http://www.toddreed.name/content/django-view-class/
My Info
• @coordt
• github.com/coordt
• github.com/washingtontimes
• opensource.washingtontimes.com