powerful generic patterns with django
DESCRIPTION
A look into django's powerful content types framework and way to use in to reduce the complexity of large applicationsTRANSCRIPT
POWERFUL GENERIC PATTERNSDjango's Content Types Framework
CONTENTTYPESAn application that can track all of the models installed in your Django-powered project, providing a high-level, generic interface for working with your models
SAY WHAT ?
CONTENTTYPES----------------------------------------| name | app_label | model || post | blogger | post || blog | blogger | Blog || like | likeable | Like |----------------------------------------
PROBLEMblog = Blog.objects.get( pk = 1 )posts = Post.objects.filter( blog = blog )features = Post.objects.filter( feature = True )
3+ URLs3+ view function3+ templates
PROBLEMblog = Blog.objects.get( pk = 1 )posts = Post.objects.filter( blog = blog )features = Post.objects.filter( feature = True )
{% for post in posts %}
{{ post.title }} {{ post.like_set.count }} likes
{% endfor %}
GENERIC VIEWS?
GENERIC VIEWS?
Take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to write too much code
GENERIC VIEWS?from django.views.generic import list_detail
def my_view( request ): return list_detail.object_list( queryset=Post.objects.all() )
NOT SO GENERIC VIEWSfrom django.views.generic import list_detail
def my_view( request ): return list_detail.object_list( queryset=Post.objects.all() )
NOT SO GENERIC VIEWS
NOT SO GENERIC VIEWS
• A Queryset• A Model + ID• A Model + Slug• A Model + Slug Field
To Use A Generic View You Need...
MORE PROBLEMSclass Post(models.Model): title = models.CharField() blog = models.ForeignKey( Blog ) body = models.TextField() slug = models.SlugField() likers = models.ForeignKey( User ) likes = models.IntegerField()
Blog
Post
Post LikesPost
Likers
MORE PROBLEMSclass Feature(models.Model): title = models.CharField() post = models.ForeignKey( Post )
likers = models.ForeignKey( User ) likes = models.IntegerField()
CONTENTTYPESAn application that can track all of the models installed in your Django-powered project, providing a high-level, generic interface for working with your models
CONTENTTYPESUse the ORM without knowing what kind of objects you might be working with.
CONTENTTYPESUse the ORM without knowing what kind of objects you might be working with.
EVER.
CONTENTTYPESYour Model
ContentType
Model A Model B Model C
GENERIC FOREIGNKEYfrom django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic
class Feature(models.Model): content_type = models.ForeignKey (ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey( 'content_type' ,'object_id' )
MAGIC BITS
class Feature(models.Model):
content_type = models.ForeignKey (ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey( 'content_type' ,'object_id' )
MAGIC BITS
class Feature(models.Model):
content_type = models.ForeignKey (ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey( 'content_type' ,'object_id' )
MAGIC BITS
class Feature(models.Model):
content_type = models.ForeignKey (ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey( 'content_type' ,'object_id' )
GENERIC FOREIGNKEY>>> obj = Feature.objects.get( pk = 1 )>>> obj.content_object>>> <Post: Hello World>
>>> obj = Feature.objects.get( pk = 2 )>>> obj.content_object>>> <Blog: The Best Blog>
>>> obj = Feature.objects.get( pk = 3 )>>> obj.content_object>>> <Anything: Whatever You Want>
GENERIC FOREIGNKEY
GENERIC FOREIGNKEYclass Post(models.Model): title = models.CharField() blog = models.ForeignKey( Blog ) body = models.TextField() slug = models.SlugField()
class Like( models.Model ): content_type = models.ForeignKey( ContentType ) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey( ... ) likers = models.ManyToMany( User )
GOTCHA!
Models are NOT aware of their Content Type
MORE PROBLEMSclass Like( models.Model ): content_type = models.ForeignKey( ContentType ) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey( ... ) likers = models.ManyToMany( User )
MORE PROBLEMS
def like_view( request, object_id ): post = Post.objects.get( pk = object_id ) like = Like( object_id = post, ??? )
class Like( models.Model ): content_type = models.ForeignKey( ContentType ) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey( ... ) likers = models.ManyToMany( User )
GENERIC GEMSfrom django.contrib.contenttypes.models import ContentTypefrom django.contrib.auth.models import User
>>> type = ContentType.objects.get_for_model( User )>>> type>>> <ContentType: user >
>>> model = type.model_class()>>> model>>> <class: 'django.contrib.auth.models.User'>
GENERIC GEMS
PATTERN #1
PATTERN #1Self-Aware Model
SELF AWARE MODELclass SelfAwareModel(models.Model): def get_ct( self ): ''' Returns the Content Type for this instance''' return ContentType.objects.get_for_model(self)
def get_ct_id( self ): ''' Returns the id of the content type for this instance''' return self.get_ct().pk
def get_app_label( self ): return self.get_ct().app_label
def get_model_name( self ): return self.get_ct().model
class Meta: abstract = True
SELF AWARE MODELclass SelfAwareModel(models.Model): def get_ct( self ): ''' Returns the Content Type for this instance''' return ContentType.objects.get_for_model(self)
def get_ct_id( self ): ''' Returns the id of the content type for this instance''' return self.get_ct().pk
def get_app_label( self ): return self.get_ct().app_label
def get_model_name( self ): return self.get_ct().model
class Meta: abstract = True
CACHED BY DJANGO
SELF AWARE MODELclass SelfAwareModel(models.Model): def get_ct( self ): ''' Returns the Content Type for this instance''' return ContentType.objects.get_for_model(self)
def get_ct_id( self ): ''' Returns the id of the content type for this instance''' return self.get_ct().pk
def get_app_label( self ): return self.get_ct().app_label
def get_model_name( self ): return self.get_ct().model
class Meta: abstract = True
CACHED BY DJANGO
self.__class__._cache[self.db][key]
SELF AWARE EVERYTHINGclass Post( SelfAwareModel ): title = models.CharField() blog = models.ForeignKey( Blog ) body = models.TextField() slug = models.SlugField()
likers = models.ForeignKey( User ) likes = models.IntegerField()
SELF AWARE EVERYTHINGclass Post( SelfAwareModel ): title = models.CharField() blog = models.ForeignKey( Blog ) body = models.TextField() slug = models.SlugField()
likers = models.ForeignKey( User ) likes = models.IntegerField()
ALL MODELSSUBCLASSE
SELFAWAREMODEL
SELF AWARE EVERYTHINGclass Post( SelfAwareModel ): title = models.CharField() blog = models.ForeignKey( Blog ) body = models.TextField() slug = models.SlugField()
likers = models.ForeignKey( User ) likes = models.IntegerField()
@permalink def get_absolute_url( self ): ...
>>> post = Post.objects.latest()>>> obj.get_ct()>>> <ContentType: post>
SELF AWARE EVERYTHINGclass Post( SelfAwareModel ): title = models.CharField() blog = models.ForeignKey( Blog ) body = models.TextField() slug = models.SlugField()
likers = models.ForeignKey( User ) likes = models.IntegerField()
@permalink def get_absolute_url( self ): ...
>>> post = Post.objects.latest()>>> obj.get_ct()>>> <ContentType: post>
I KNOW MYCONTENT TYPE
HOORAY!
PATTERN #2REAL Generic Views
REAL GENERIC VIEW
def object_list( request, ct_id ... ): type = ContentType.objects.get( pk = ct_id ) model = type.model_class() obj_list = model._default_manager.all()
return render_to_response( ... )
REAL GENERIC VIEW
def object_detail( request, ct_id, obj_id, template=None ): type = ContentType.objects.get( pk = ct_id ) model = type.model_class() obj = model._default_manager.get( pk = ct_id )
if template is None: template = '%s_detail.html'%(type) return render_to_response( template )
REAL GENERIC VIEW
def object_detail( request, ct_id, obj_id, template=None ): type = ContentType.objects.get( pk = ct_id ) model = type.model_class() obj = model._default_manager.get( pk = ct_id )
if template is None: template = '%s_detail.html'%(type) return render_to_response( template )
def object_detail( request, ct_id, obj_id, template=None ): type = ContentType.objects.get( pk = ct_id ) model = type.model_class() obj = model._default_manager.get( pk = ct_id )
if template is None: template = '%s_detail.html'%(type) return render_to_response( template )
REAL GENERIC VIEW
Might Want To Cache That
def object_detail( request, ct_id, obj_id, template=None ): type = ContentType.objects.get( pk = ct_id ) model = type.model_class() obj = model._default_manager.get( pk = ct_id )
if template is None: template = '%s_detail.html'%(type) return render_to_response( template )
REAL GENERIC VIEW
Might Want To Cache That
• self.__class__._cache[self.db][key]• cPickle & noSQL DB ( Redis )
REAL GENERIC VIEWproject|| - likeables/|| - blog/ | |- templates/ | |- blog/ | -post_list.html | -post_detail.html | -urls.py|| - templates/| - object_list.html| - object_detail.html| - urls.py
MORE PROBLEMS
Blog
Post
Post LikesPost
Likers
MORE PROBLEMS
Blog
Post
Post LikersPost Likes
LESS PROBLEMSLIKE
ContentType
POST FEATURE ANYTHING
LIKE ANYTHING YOU WANT
NOT BAD, KID!
PATTERN #3Universal URLs
UNIVERSAL URLsurlpatterns = patterns( 'myproj.myapp', url( r'^(?P<slug>[-\w]+)/(?P<ct_id>\d+)/list/$', 'object_list', name='my_proj_content_list' ), url( r'^(?P<slug>[-\w]+)/(?P<ct_id>\d+)-(?P<obj_id>\d+)/$', 'object_detail', name="my_proj_content_detail" ), url( r'^(?P<slug>[-\w]+)/(?P<ct_id>\d+)-(?P<obj_id>\d+)/edit/$', 'object_edit', name="my_proj_content_edit" ) ...)
UNIVERSAL URLs/something-here/23/list/
/some-title/23-21/
/some-title/23-21/edit/
/some-title/23-21/delete/
/some-title/23-21/blah/
PATTERN #4Magic Forms
MAGIC FORMS
def edit_object( request, ct_id, obj_id ): obj = utils.get_object( ct_id, obj_id )
MAGIC FORMS
def edit_object( request, ct_id, obj_id ): obj = utils.get_object( ct_id, obj_id )
form = ???
MAGIC FORMS
def edit_object( request, ct_id, obj_id ): obj = utils.get_object( ct_id, obj_id )
form = ???
Can't predefine ModelForm when you don't know what model you're working with
MAGIC FORMS
MAGIC FORMSdef form_class_for( obj, includes=[] excludes=[] ): modelclass = obj.get_ct().model_class()
class _MagicForm(forms.ModelForm): ... class Meta: model= modelclass if includes: fields = includes if excludes: exclude = excludes
return _MagicForm
MAGIC FORMSdef form_class_for( obj, includes=[] excludes=[] ): modelclass = obj.get_ct().model_class()
class _MagicForm(forms.ModelForm): ... class Meta: model= modelclass if includes: fields = includes if excludes: exclude = excludes
return _MagicForm
DON'T KNOW
MAGIC FORMSdef form_class_for( obj, includes=[] excludes=[] ): modelclass = obj.get_ct().model_class()
class _MagicForm(forms.ModelForm): ... class Meta: model= modelclass if includes: fields = includes if excludes: exclude = excludes
return _MagicForm
DON'TCARE
MAGIC FORMSdef form_class_for( obj, includes=[] excludes=[] ): modelclass = obj.get_ct().model_class()
class _MagicForm(forms.ModelForm): ... class Meta: model= modelclass
if includes: fields = includes if excludes: exclude = excludes
return _MagicForm PERFECTLYLEGAL
FULL CIRCLE
def edit_object( request, ct_id, obj_id ): obj = utils.get_object( ct_id, obj_id )
formclass = utils.get_form_for( obj )
form = formclass()
return render_to_response( ... {'form':form} )
FULL CIRCLE
def edit_object( request, ct_id, obj_id ): obj = utils.get_object( ct_id, obj_id )
formclass = utils.get_form_for( obj )
form = formclass()
return render_to_response( ... {'form':form} )
DON'T KNOW
FULL CIRCLE
def edit_object( request, ct_id, obj_id ): obj = utils.get_object( ct_id, obj_id )
formclass = utils.get_form_for( obj )
form = formclass()
return render_to_response( ... {'form':form} )
DON'TCARE
DEAD SIMPLE{% url my_proj_content_list ct_id=obj.get_ct_id %}
{% url my_proj_content_detail slug=obj.slug, ct_id=obj.get_ct_id, obj_id=obj.pk %}
{% url my_proj_content_edit slug=obj.slug, ct_id=obj.get_ct_id, obj_id=obj.pk %}
FOR THE WIN
SCALE IT OUT
1.Define A Model2.Sync DB3.Make A Template ( Maybe ? )4.Rinse 5.Repeate
PATTERNS RECAP
• Self Aware Models• Better Generic Views & utilities• Universial URLs• Magic Forms
PATTERNS RECAP
• Self Aware Models• Better Generic Views & utilities• Universial URLs• Magic Forms
You can perform CRUD ops and create any kind of relationship on any kind of object at anytime without programming for every situation.
FIN