Django Patterns
     James Tauber
   (and Brian Rosner)
Django Patterns

this is the start not the end of the matter
hope it will form basis for more conversation
during rest of ...
What do I mean by
      “patterns”?
not the same as best practices
not quite the same as snippets
sometimes just idioms (n...
Example
 environment-specific settings


try:
    from local_settings import *
except ImportError:
    pass
Example
         Change Log Model


class LogMessage(models.Model):

    thing = models.ForeignKey(Thing)
    user = model...
Example
           Change Log Model
 not a reusable model, more “parameterized snippet”

class LogMessage(models.Model):

...
Some patterns can be
turned in to constructs
generic views
render_to_response
new redirect in Django 1.1
view decorators
Django IS Python
I hope to give...
beginning Djangonauts
  something to try out

intermediate Djangonauts
  something to think about

advan...
Concepts
views            template tags
urlconfs         middleware
models           context processors
templates        m...
Model Patterns
The “Atom” Model
class Genre(models.Model):

    name = models.CharField(
        max_length=100, unique=True)

    def __...
Common Fields
slug
title
description
creator
                      sometimes just “user” and
creation_timestamp   “timesta...
“pseudo foreign key”

class Node(models.Model):

    ...
    subgraph = models.IntegerField(
        db_index=True)
entity attribute values
class GameInformation(models.Model):

   game = models.ForeignKey(Game)
   attribute = models.Char...
entity attribute values
class Attribute(models.Model):

    node = models.ForeignKey(Node)
    attribute_type = models.Cha...
zip choices


mood = models.CharField(
    blank=True, max_length=20,
    choices=zip(MOODS, MOODS))
top-level
   object creation helper
GAME_POINTS = {
    quot;RATED_GAMEquot;: 1,
    ...
}

def earn_game_points(user, rea...
Basic “Change Log”

class LogMessage(models.Model):

    thing = models.ForeignKey(Thing)
    user = models.ForeignKey(Use...
association models
    without ManyToManyField
class WishListItem(models.Model):

   user = models.ForeignKey(User,
      ...
symmetrical associations
    “friendships”
class Friendship(models.Model):

    to_user = models.ForeignKey(User,
        ...
class FriendshipManager(models.Manager):

   def friends_for_user(self, user):
       friends = []
       for friendship i...
Trees

class ForumCategory(models.Model):

    name = models.CharField(
        max_length=100)
    parent = models.Foreig...
multi-strata trees
class Forum(models.Model):

   title = models.CharField(max_length=100)
   description = models.TextFie...
lattice

class Node(models.Model):

    children = models.ManyToManyField('self',
        symmetrical=False,
        relat...
Denormalization and
  Calculated Field
     Patterns
username
       as well as user
class Profile(models.Model):
    user = models.ForeignKey(User,
        unique=True)
    u...
view counts

view_count = models.IntegerField(
    default=0, editable=False)

def inc_views(self):
    self.view_count +=...
count based on
             another model
class BlogComment(models.Model):
    blog_post = models.ForeignKey(BlogPost, rel...
value based on another
            model
class MediaItem(models.Model):

    overall_rating = models.DecimalField(max_digi...
denormalized
           foreign key

class Forum(models.Model):
    ...
    last_reply = models.ForeignKey(quot;ForumReply...
Advice on
        Calculated Fields
make sure they are editable=False
where possible make them re-calculable
(although doe...
markup fields

class TownHallUpdate(models.Model):

  content = models.TextField()
  content_html = models.TextField(editab...
one active at a time
class TownHallUpdate(models.Model):

    active = models.BooleanField(default=False,
        help_tex...
Query Patterns
within date range

.filter(
    Q(start_date__lt=now) |
         Q(start_date=None),
    Q(end_date__gt=now) |
         Q(...
ordering by nullables


top_blogs =
    Blog.objects.filter(overall_rating__isnull=False)
                     .order_by(q...
View Patterns
standard object
             retrieval

def community_blog(request, blog_id):
     blog = get_object_or_404(Blog, id=blog_...
standard render


return render_to_response(quot;app/bar.htmlquot;, {
    quot;fooquot;: foo,
 }, context_instance=Request...
most views
def community_blog(request, blog_id):
  blog = get_object_or_404(Blog, id=blog_id)

  ...extra queries...

  re...
passing in template
    name and form class

def create(request,
    form_class=TribeForm,
    template_name=quot;tribes/c...
building up
         context dictionary
ctx = {}

...
      if form.had_valid_code:
          ctx[quot;valid_codequot;] = ...
submit to different
  URL then redirect back
@login_required
@require_POST
def comment_submit(request, article_id):
    ......
lots of form submits
if request.POST[quot;actionquot;] == quot;add commentquot;:
    ...
elif request.POST[quot;actionquot...
handle next
def handle_next(request, default=None):
    if default is None:
        default = reverse(quot;homequot;)
    ...
poor man’s search
query = request.GET.get(quot;queryquot;, quot;quot;)
if query:
    search_results = Member.objects.filte...
conditional adding of
        filters to a chain
games = Game.objects.filter(platform__in=PLATFORMS)

query = request.GET.g...
can encapsulate that in
         a manager
class AnnouncementManager(models.Manager):
  def current(self, exclude=[], site...
get first (1)

top_reviews =
   game.user_reviews.order_by(quot;-timestampquot;)

if top_reviews:
  top_review = top_review...
get first (2)


top_reviews =
  game.user_reviews.order_by(quot;-timestampquot;)[:1]

# rely on template to do .0
Form Patterns
form handling (1)

if request.method == quot;POSTquot;:
    form = form_class(request.POST)
    if form.is_valid():
      ...
form handling (2)

form = form_class(request.POST or None)

if tribe_form.is_valid():
    ...

# relies on fact request.PO...
“already taken” pattern
def clean_username(self):
  try:
    user = User.objects.get(
        username__iexact =
         ...
the user form
class UserForm(forms.Form):

  def __init__(self, *args, **kwargs):
    self.user = kwargs.pop(quot;userquot...
order fields
class UserPrivacySettingForm(forms.ModelForm):
  class Meta:
    model = UserPrivacySetting
    fields = (
   ...
overriding a field type
class ComposeMessageForm(forms.ModelForm):

   class Meta:
       model = Message
       fields = (...
URL Patterns
serving media
if settings.SERVE_MEDIA:
  urlpatterns += patterns('',
    (r'^site_media/(?P<path>.*)$',
     'django.views...
per-app URLs


url(r'^community/', include('community.urls')),
named urls
url(r'^article/(d+)/$', ...,
name=quot;articlequot;)

{% url article article_id %}

# use of {% url %} keeps yo...
URL space

^article/$ or ^articles/$
^article/(d+)/$ or ^article/(w+)/$
^article/(d+)/comment/$
^article/add/$
...
Template Patterns
using app name as
 template folder name
myapp/
  templates/
    myapp/
       myapp_template.html

template_name =
    quo...
sections of a site have
  own base template

templates/
  base.html
  section1/
    base.html
    some_page.html
types of templates

top-level
extensions
includes
lists that may
             be empty
{% if results %}
    <ul>
         {% for result in results %}
             <li>...</...
provide an override
  AND extension point
{% block extra_body_base %}
  {% urchin %}
  <script src=quot;{{ MEDIA_URL }}bas...
Template Tag Patterns
types of template tags


generate content
modify context
basic inclusion tag

@register.inclusion_tag(quot;includes/media_item.htmlquot;)
def media_item(media_item):
    return {
...
slip in MEDIA_URL
@register.inclusion_tag(quot;community/
inclusion_tags/member_item.htmlquot;)
def member_item(member):
 ...
out-of-band
   calculations on objects
@register.simple_tag
def default_avatar(member):
    if member.gender == quot;guyqu...
the context, the whole context
 and nothing but the context

@register.inclusion_tag(quot;includes/
nav.htmlquot;, takes_c...
context processors

inbox counts
other values calculated off current user that
are in header or footer
especially if reques...
Settings Patterns
make relative file paths
          absolute
from os.path import join, dirname

MEDIA_ROOT = join(dirname(__file__), quot;si...
environment-specific
      settings (1)

try:
    from local_settings import *
except ImportError:
    pass
environment-specific
       settings (2)

have all per-environment settings files
checked-in to version control with symlink...
Meta Patterns
Endo Approach
(framework-like)
Exo Approach
 (library-like)
Parting Thoughts

keep the conversation going during conference
going to put these (and more) up on
eldarion.com
pattern l...
James Tauber
jtauber@jtauber.com
 http://jtauber.com/
  twitter: @jtauber


 Brian Rosner
brosner@gmail.com
http://oebfare...
EuroDjangoCon Django Patterns
Upcoming SlideShare
Loading in …5
×

EuroDjangoCon Django Patterns

3,765 views

Published on

by James Tauber and Bryan Rosner

Published in: Technology, Business
2 Comments
22 Likes
Statistics
Notes
No Downloads
Views
Total views
3,765
On SlideShare
0
From Embeds
0
Number of Embeds
69
Actions
Shares
0
Downloads
170
Comments
2
Likes
22
Embeds 0
No embeds

No notes for slide

EuroDjangoCon Django Patterns

  1. 1. Django Patterns James Tauber (and Brian Rosner)
  2. 2. Django Patterns this is the start not the end of the matter hope it will form basis for more conversation during rest of conference
  3. 3. What do I mean by “patterns”? not the same as best practices not quite the same as snippets sometimes just idioms (not proper “design patterns”) more descriptive than prescriptive
  4. 4. Example environment-specific settings try: from local_settings import * except ImportError: pass
  5. 5. Example Change Log Model class LogMessage(models.Model): thing = models.ForeignKey(Thing) user = models.ForeignKey(User) timestamp = models.DateTimeField( default=datetime.now) message = models.TextField()
  6. 6. Example Change Log Model not a reusable model, more “parameterized snippet” class LogMessage(models.Model): thing = models.ForeignKey(Thing) user = models.ForeignKey(User) timestamp = models.DateTimeField( default=datetime.now) message = models.TextField()
  7. 7. Some patterns can be turned in to constructs generic views render_to_response new redirect in Django 1.1 view decorators
  8. 8. Django IS Python
  9. 9. I hope to give... beginning Djangonauts something to try out intermediate Djangonauts something to think about advanced Djangonauts something to laugh at
  10. 10. Concepts views template tags urlconfs middleware models context processors templates management commands forms
  11. 11. Model Patterns
  12. 12. The “Atom” Model class Genre(models.Model): name = models.CharField( max_length=100, unique=True) def __unicode__(self): return self.name
  13. 13. Common Fields slug title description creator sometimes just “user” and creation_timestamp “timestamp” when an event
  14. 14. “pseudo foreign key” class Node(models.Model): ... subgraph = models.IntegerField( db_index=True)
  15. 15. entity attribute values class GameInformation(models.Model): game = models.ForeignKey(Game) attribute = models.CharField( max_length=50) value = models.CharField( max_length=50)
  16. 16. entity attribute values class Attribute(models.Model): node = models.ForeignKey(Node) attribute_type = models.CharField( max_length=20, db_index=True) value = models.CharField(max_length=50, db_index=True) class Meta: unique_together = ( ('node', 'attribute_type', 'value'), ) class Node(models.Model): ... def attribute(self, attr_type): return [v[quot;valuequot;] for v in Attribute.objects.filter(node=self, attribute_type=attr_type).values()]
  17. 17. zip choices mood = models.CharField( blank=True, max_length=20, choices=zip(MOODS, MOODS))
  18. 18. top-level object creation helper GAME_POINTS = { quot;RATED_GAMEquot;: 1, ... } def earn_game_points(user, reason): points = GAME_POINTS[reason] GamePointAward(user=user, points=points, reason=reason).save() ... earn_game_points(user, quot;RATED_GAMEquot;)
  19. 19. Basic “Change Log” class LogMessage(models.Model): thing = models.ForeignKey(Thing) user = models.ForeignKey(User) timestamp = models.DateTimeField( default=datetime.now) message = models.TextField()
  20. 20. association models without ManyToManyField class WishListItem(models.Model): user = models.ForeignKey(User, related_name=quot;wishlistquot;) game = models.ForeignKey(Game) timestamp = models.DateTimeField( default=datetime.now) ... user_wishlist_items = user.wishlist_items.all() # will avoid O(n) queries to the Game model: user_wishlist_items = user.wishlist_items.select_related(quot;gamequot;)
  21. 21. symmetrical associations “friendships” class Friendship(models.Model): to_user = models.ForeignKey(User, related_name=quot;_unused1quot;) from_user = models.ForeignKey(User, related_name=quot;_unused2_quot;) timestamp = models.DateTimeField(default=datetime.now) objects = FriendshipManager() class Meta: unique_together = ( ('to_user', 'from_user'), )
  22. 22. class FriendshipManager(models.Manager): def friends_for_user(self, user): friends = [] for friendship in self.filter(from_user=user) .select_related(depth=1): friends.append({quot;friendquot;: friendship.to_user, quot;friendshipquot;: friendship}) for friendship in self.filter(to_user=user) .select_related(depth=1): friends.append({quot;friendquot;: friendship.from_user, quot;friendshipquot;: friendship}) return friends def are_friends(self, user1, user2): if self.filter(from_user=user1, to_user=user2) .count() > 0: return True if self.filter(from_user=user2, to_user=user1) .count() > 0: return True return False
  23. 23. Trees class ForumCategory(models.Model): name = models.CharField( max_length=100) parent = models.ForeignKey('self', null=True, blank=True, related_name=quot;subcategoriesquot;)
  24. 24. multi-strata trees class Forum(models.Model): title = models.CharField(max_length=100) description = models.TextField() creation_date = models.DateTimeField(default=datetime.now) # must only have one of these (or neither): parent = models.ForeignKey('self', null=True, blank=True, related_name=quot;subforumsquot;) category = models.ForeignKey(ForumCategory, null=True, blank=True, related_name=quot;forumsquot;)
  25. 25. lattice class Node(models.Model): children = models.ManyToManyField('self', symmetrical=False, related_name='parents', blank=True)
  26. 26. Denormalization and Calculated Field Patterns
  27. 27. username as well as user class Profile(models.Model): user = models.ForeignKey(User, unique=True) username = models.CharField( max_length=50)
  28. 28. view counts view_count = models.IntegerField( default=0, editable=False) def inc_views(self): self.view_count += 1 self.save()
  29. 29. count based on another model class BlogComment(models.Model): blog_post = models.ForeignKey(BlogPost, related_name=quot;commentsquot;) ... class BlogPost(models.Model): ... commment_count = models.IntegerField(default=0, editable=False) def update_comment_count(self): self.comment_count = self.comments.count() self.save() def blog_comment_save(sender, instance=None, created=False, **kwargs): if instance and created: blog_post = instance.blog_post blog_post.update_comment_count() def blog_comment_delete(sender, instance=None, **kwargs): if instance: blog_post = instance.blog_post blog_post.update_comment_count() post_save.connect(blog_comment_save, sender=BlogComment) post_delete.connect(blog_comment_delete, sender=BlogComment)
  30. 30. value based on another model class MediaItem(models.Model): overall_rating = models.DecimalField(max_digits=3, decimal_places=1, default=0, editable=False) def update_rating(self): total_rating = 0 total_count = 0 for rating in self.ratings.all(): total_rating += rating.rating total_count += 1 self.overall_rating = str(1. * total_rating / total_count) self.save() class MediaRating(models.Model): media_item = models.ForeignKey(MediaItem, related_name=quot;ratingsquot;) user = models.ForeignKey(Member) rating = models.IntegerField(default=0) timestamp = models.DateTimeField(default=datetime.now) class Meta: unique_together = (quot;media_itemquot;, quot;userquot;)
  31. 31. denormalized foreign key class Forum(models.Model): ... last_reply = models.ForeignKey(quot;ForumReplyquot;, null=True, editable=False)
  32. 32. Advice on Calculated Fields make sure they are editable=False where possible make them re-calculable (although doesn’t alway make sense) be VERY careful with admin delete when calculated field is foreign key really really wish there was a way to say “if foreign key object is deleted, just null this field”
  33. 33. markup fields class TownHallUpdate(models.Model): content = models.TextField() content_html = models.TextField(editable=False) def save(self, **kwargs): self.content_html = textile.textile(sanitize_html(self.content)) super(TownHallUpdate, self).save(**kwargs)
  34. 34. one active at a time class TownHallUpdate(models.Model): active = models.BooleanField(default=False, help_text=quot;There can only be one active update. Marking the checkbox will mark any other update as inactive.quot;) def save(self, **kwargs): if self.active: # check for another active one and mark it inactive try: update = TownHallUpdate.objects.get(active=True) except TownHallUpdate.DoesNotExist: pass else: update.active = False update.save() super(TownHallUpdate, self).save(**kwargs) later in view: townhall_update = TownHallUpdate.objects.get(active=True)
  35. 35. Query Patterns
  36. 36. within date range .filter( Q(start_date__lt=now) | Q(start_date=None), Q(end_date__gt=now) | Q(end_date=None), )
  37. 37. ordering by nullables top_blogs = Blog.objects.filter(overall_rating__isnull=False) .order_by(quot;-overall_ratingquot;)[:3]
  38. 38. View Patterns
  39. 39. standard object retrieval def community_blog(request, blog_id): blog = get_object_or_404(Blog, id=blog_id)
  40. 40. standard render return render_to_response(quot;app/bar.htmlquot;, { quot;fooquot;: foo, }, context_instance=RequestContext(request))
  41. 41. most views def community_blog(request, blog_id): blog = get_object_or_404(Blog, id=blog_id) ...extra queries... return render_to_response(quot;app/bar.htmlquot;, { quot;blogquot;: blog, ... }, context_instance=RequestContext(request))
  42. 42. passing in template name and form class def create(request, form_class=TribeForm, template_name=quot;tribes/create.htmlquot;):
  43. 43. building up context dictionary ctx = {} ... if form.had_valid_code: ctx[quot;valid_codequot;] = True ctx[quot;formquot;] = form else: ctx[quot;valid_codequot;] = False ... if beta_code: valid_code = True ctx[quot;formquot;] = BetaSignupForm(initial={quot;beta_codequot;: code}) else: valid_code = False ctx[quot;valid_codequot;] = valid_code return render_to_response(quot;main/beta_signup.htmlquot;, ctx, context_instance=RequestContext(request))
  44. 44. submit to different URL then redirect back @login_required @require_POST def comment_submit(request, article_id): ... handle post ... return HttpResponseRedirect( reverse(article_page, args=[article_id]))
  45. 45. lots of form submits if request.POST[quot;actionquot;] == quot;add commentquot;: ... elif request.POST[quot;actionquot;] == quot;delete commentquot;: ... elif request.POST[quot;actionquot;] == quot;rename nodequot;: ... elif request.POST[quot;actionquot;] == quot;new nodequot;: ... elif request.POST[quot;actionquot;] == quot;detach nodequot;: ... elif request.POST[quot;actionquot;] == quot;put nodequot;: ... elif request.POST[quot;actionquot;] == quot;add attributequot;: ... elif request.POST[quot;actionquot;] == quot;delete attributequot;: ... elif request.POST[quot;actionquot;] == quot;promote attributequot;: ... elif request.POST[quot;actionquot;] == quot;demote attributequot;: ...
  46. 46. handle next def handle_next(request, default=None): if default is None: default = reverse(quot;homequot;) redirect_to = request.REQUEST.get(quot;nextquot;, default) if quot;://quot; in redirect_to: redirect_to = default return redirect_to @login_required def accept_friendship(request, friend_request_id): redirect_to = handle_next(request) FriendshipRequest.objects.get(pk=friend_request_id) .accept() return HttpResponseRedirect(redirect_to)
  47. 47. poor man’s search query = request.GET.get(quot;queryquot;, quot;quot;) if query: search_results = Member.objects.filter( Q(username__icontains=query) | Q(first_name__icontains=query) | Q(last_name__icontains=query) | Q(user__email__icontains=query) ) else: search_results = Member.objects.none() # note quot;quot; is used as query default as it is passed through to template for form
  48. 48. conditional adding of filters to a chain games = Game.objects.filter(platform__in=PLATFORMS) query = request.GET.get(quot;queryquot;, quot;quot;) letter = request.GET.get(quot;letterquot;, None) if query: filtered_games = games.filter( Q(title__icontains=query) | Q(description__icontains=query) ) else: filtered_games = games if letter: filtered_games = filtered_games.filter(title__istartswith=letter)
  49. 49. can encapsulate that in a manager class AnnouncementManager(models.Manager):   def current(self, exclude=[], site_wide=False, for_members=False):     queryset = self.all()     if site_wide:       queryset = queryset.filter(site_wide=True)     if exclude:       queryset = queryset.exclude(pk__in=exclude)     if not for_members:       queryset = queryset.filter(members_only=False)     queryset = queryset.order_by(quot;-creation_datequot;)     return queryset  
  50. 50. get first (1) top_reviews = game.user_reviews.order_by(quot;-timestampquot;) if top_reviews: top_review = top_reviews[0] else: top_review = None
  51. 51. get first (2) top_reviews = game.user_reviews.order_by(quot;-timestampquot;)[:1] # rely on template to do .0
  52. 52. Form Patterns
  53. 53. form handling (1) if request.method == quot;POSTquot;: form = form_class(request.POST) if form.is_valid(): ... else: form = form_class()
  54. 54. form handling (2) form = form_class(request.POST or None) if tribe_form.is_valid(): ... # relies on fact request.POST is False if # request.method != quot;POSTquot; # AND that is_valid fails if form is unbound
  55. 55. “already taken” pattern def clean_username(self): try: user = User.objects.get( username__iexact = self.cleaned_data[quot;usernamequot;]) except User.DoesNotExist: return self.cleaned_data[quot;usernamequot;] raise forms.ValidationError(quot;This username is already taken. Please choose another.quot;) # note iexact!
  56. 56. the user form class UserForm(forms.Form): def __init__(self, *args, **kwargs): self.user = kwargs.pop(quot;userquot;) super(UserForm, self).__init__(*args, **kwargs) # can be used for any additional info # can also pass in to save() but then # can’t use in validation
  57. 57. order fields class UserPrivacySettingForm(forms.ModelForm): class Meta: model = UserPrivacySetting fields = ( quot;publicquot;, quot;membersquot;, quot;groupsquot;, quot;friendsquot;) def __init__(self, *args, **kwargs): super(UserPrivacySettingForm, self) .__init__(*args, **kwargs) self.fields.keyOrder = self._meta.fields # in 1.1 fields ordering is now significant
  58. 58. overriding a field type class ComposeMessageForm(forms.ModelForm): class Meta: model = Message fields = (quot;to_userquot;, quot;subjectquot;, quot;bodyquot;) def __init__(self, member, *args, **kwargs): super(ComposeMessageForm, self) .__init__(*args, **kwargs) self.fields[quot;to_userquot;] = FriendChoiceField(member, label=uquot;Recipientquot;)
  59. 59. URL Patterns
  60. 60. serving media if settings.SERVE_MEDIA: urlpatterns += patterns('', (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT} ), ) # personally I prefer to separate SERVE_MEDIA # from DEBUG although former can default to # latter
  61. 61. per-app URLs url(r'^community/', include('community.urls')),
  62. 62. named urls url(r'^article/(d+)/$', ..., name=quot;articlequot;) {% url article article_id %} # use of {% url %} keeps you DRY # use of named-url makes it easier to # drop in replacement apps
  63. 63. URL space ^article/$ or ^articles/$ ^article/(d+)/$ or ^article/(w+)/$ ^article/(d+)/comment/$ ^article/add/$ ...
  64. 64. Template Patterns
  65. 65. using app name as template folder name myapp/ templates/ myapp/ myapp_template.html template_name = quot;myapp/myapp_template.htmlquot;
  66. 66. sections of a site have own base template templates/ base.html section1/ base.html some_page.html
  67. 67. types of templates top-level extensions includes
  68. 68. lists that may be empty {% if results %} <ul> {% for result in results %} <li>...</li> {% endfor %} </ul> {% else %} <p>No results.</p> {% endif %} # in 1.1 {% empty %} can be used for # non-wrapped cases
  69. 69. provide an override AND extension point {% block extra_body_base %} {% urchin %} <script src=quot;{{ MEDIA_URL }}base.jsquot; type=quot;text/javascriptquot;></script> {% block extra_body %}{% endblock %} {% endblock %} # bottom-level template doesn't have to # remember block.super
  70. 70. Template Tag Patterns
  71. 71. types of template tags generate content modify context
  72. 72. basic inclusion tag @register.inclusion_tag(quot;includes/media_item.htmlquot;) def media_item(media_item): return { quot;media_itemquot;: media_item, } # could also just use {% include ... %} and rely on # context (possibly with 'with')
  73. 73. slip in MEDIA_URL @register.inclusion_tag(quot;community/ inclusion_tags/member_item.htmlquot;) def member_item(member): return { quot;memberquot;: member, quot;MEDIA_URLquot;: settings.MEDIA_URL, }
  74. 74. out-of-band calculations on objects @register.simple_tag def default_avatar(member): if member.gender == quot;guyquot;: filename = quot;avatar/guy_default.jpgquot; else: filename = quot;avatar/girl_default.jpgquot; return filename
  75. 75. the context, the whole context and nothing but the context @register.inclusion_tag(quot;includes/ nav.htmlquot;, takes_context=True) def nav(context): return context # is this any different than {% include ... %} ?
  76. 76. context processors inbox counts other values calculated off current user that are in header or footer especially if request object is needed don’t have to load and call template tag
  77. 77. Settings Patterns
  78. 78. make relative file paths absolute from os.path import join, dirname MEDIA_ROOT = join(dirname(__file__), quot;site_mediaquot;) TEMPLATE_DIRS = ( join(dirname(__file__), quot;templatesquot;), )
  79. 79. environment-specific settings (1) try: from local_settings import * except ImportError: pass
  80. 80. environment-specific settings (2) have all per-environment settings files checked-in to version control with symlink from settings.py
  81. 81. Meta Patterns
  82. 82. Endo Approach (framework-like)
  83. 83. Exo Approach (library-like)
  84. 84. Parting Thoughts keep the conversation going during conference going to put these (and more) up on eldarion.com pattern library will grow over time
  85. 85. James Tauber jtauber@jtauber.com http://jtauber.com/ twitter: @jtauber Brian Rosner brosner@gmail.com http://oebfare.com/ twitter: @brosner

×