Powerful Generic Patterns With Django

74

description

A look into django's powerful content types framework and way to use in to reduce the complexity of large applications

Transcript of Powerful Generic Patterns With Django

Page 1: Powerful Generic Patterns With Django
Page 2: Powerful Generic Patterns With Django

POWERFUL GENERIC PATTERNSDjango's Content Types Framework

Page 3: Powerful Generic Patterns With Django

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

Page 4: Powerful Generic Patterns With Django

SAY WHAT ?

Page 5: Powerful Generic Patterns With Django

CONTENTTYPES----------------------------------------| name | app_label | model || post | blogger | post || blog | blogger | Blog || like | likeable | Like |----------------------------------------

Page 6: Powerful Generic Patterns With Django

PROBLEMblog = Blog.objects.get( pk = 1 )posts = Post.objects.filter( blog = blog )features = Post.objects.filter( feature = True )

3+ URLs3+ view function3+ templates

Page 7: Powerful Generic Patterns With Django

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 %}

Page 8: Powerful Generic Patterns With Django

GENERIC VIEWS?

Page 9: Powerful Generic Patterns With Django

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

Page 10: Powerful Generic Patterns With Django

GENERIC VIEWS?from django.views.generic import list_detail

def my_view( request ): return list_detail.object_list( queryset=Post.objects.all() )

Page 11: Powerful Generic Patterns With Django

NOT SO GENERIC VIEWSfrom django.views.generic import list_detail

def my_view( request ): return list_detail.object_list( queryset=Post.objects.all() )

Page 12: Powerful Generic Patterns With Django

NOT SO GENERIC VIEWS

Page 13: Powerful Generic Patterns With Django

NOT SO GENERIC VIEWS

• A Queryset• A Model + ID• A Model + Slug• A Model + Slug Field

To Use A Generic View You Need...

Page 14: Powerful Generic Patterns With Django

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

Page 15: Powerful Generic Patterns With Django

MORE PROBLEMSclass Feature(models.Model): title = models.CharField() post = models.ForeignKey( Post )

likers = models.ForeignKey( User ) likes = models.IntegerField()

Page 16: Powerful Generic Patterns With Django

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

Page 17: Powerful Generic Patterns With Django

CONTENTTYPESUse the ORM without knowing what kind of objects you might be working with.

Page 18: Powerful Generic Patterns With Django

CONTENTTYPESUse the ORM without knowing what kind of objects you might be working with.

EVER.

Page 19: Powerful Generic Patterns With Django

CONTENTTYPESYour Model

ContentType

Model A Model B Model C

Page 20: Powerful Generic Patterns With Django

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' )

Page 21: Powerful Generic Patterns With Django

MAGIC BITS

class Feature(models.Model):

content_type = models.ForeignKey (ContentType)

object_id = models.PositiveIntegerField()

content_object = generic.GenericForeignKey( 'content_type' ,'object_id' )

Page 22: Powerful Generic Patterns With Django

MAGIC BITS

class Feature(models.Model):

content_type = models.ForeignKey (ContentType)

object_id = models.PositiveIntegerField()

content_object = generic.GenericForeignKey( 'content_type' ,'object_id' )

Page 23: Powerful Generic Patterns With Django

MAGIC BITS

class Feature(models.Model):

content_type = models.ForeignKey (ContentType)

object_id = models.PositiveIntegerField()

content_object = generic.GenericForeignKey( 'content_type' ,'object_id' )

Page 24: Powerful Generic Patterns With Django

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>

Page 25: Powerful Generic Patterns With Django

GENERIC FOREIGNKEY

Page 26: Powerful Generic Patterns With Django

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 )

Page 27: Powerful Generic Patterns With Django

GOTCHA!

Models are NOT aware of their Content Type

Page 28: Powerful Generic Patterns With Django

MORE PROBLEMSclass Like( models.Model ): content_type = models.ForeignKey( ContentType ) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey( ... ) likers = models.ManyToMany( User )

Page 29: Powerful Generic Patterns With Django

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 )

Page 30: Powerful Generic Patterns With Django

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'>

Page 31: Powerful Generic Patterns With Django

GENERIC GEMS

Page 32: Powerful Generic Patterns With Django

PATTERN #1

Page 33: Powerful Generic Patterns With Django

PATTERN #1Self-Aware Model

Page 34: Powerful Generic Patterns With 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

Page 35: Powerful Generic Patterns With 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

Page 36: Powerful Generic Patterns With 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]

Page 37: Powerful Generic Patterns With Django

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()

Page 38: Powerful Generic Patterns With Django

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

Page 39: Powerful Generic Patterns With Django

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>

Page 40: Powerful Generic Patterns With Django

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

Page 41: Powerful Generic Patterns With Django

HOORAY!

Page 42: Powerful Generic Patterns With Django

PATTERN #2REAL Generic Views

Page 43: Powerful Generic Patterns With Django

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( ... )

Page 44: Powerful Generic Patterns With Django

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 )

Page 45: Powerful Generic Patterns With Django

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 )

Page 46: Powerful Generic Patterns With Django

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

Page 47: Powerful Generic Patterns With Django

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 )

Page 48: Powerful Generic Patterns With Django

REAL GENERIC VIEWproject|| - likeables/|| - blog/ | |- templates/ | |- blog/ | -post_list.html | -post_detail.html | -urls.py|| - templates/| - object_list.html| - object_detail.html| - urls.py

Page 49: Powerful Generic Patterns With Django

MORE PROBLEMS

Blog

Post

Post LikesPost

Likers

Page 50: Powerful Generic Patterns With Django

MORE PROBLEMS

Blog

Post

Post LikersPost Likes

Page 51: Powerful Generic Patterns With Django

LESS PROBLEMSLIKE

ContentType

POST FEATURE ANYTHING

Page 52: Powerful Generic Patterns With Django

LIKE ANYTHING YOU WANT

Page 53: Powerful Generic Patterns With Django

NOT BAD, KID!

Page 54: Powerful Generic Patterns With Django

PATTERN #3Universal URLs

Page 55: Powerful Generic Patterns With Django

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" ) ...)

Page 56: Powerful Generic Patterns With Django

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/

Page 57: Powerful Generic Patterns With Django

PATTERN #4Magic Forms

Page 58: Powerful Generic Patterns With Django

MAGIC FORMS

def edit_object( request, ct_id, obj_id ): obj = utils.get_object( ct_id, obj_id )

Page 59: Powerful Generic Patterns With Django

MAGIC FORMS

def edit_object( request, ct_id, obj_id ): obj = utils.get_object( ct_id, obj_id )

form = ???

Page 60: Powerful Generic Patterns With Django

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

Page 61: Powerful Generic Patterns With Django

MAGIC FORMS

Page 62: Powerful Generic Patterns With Django

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

Page 63: Powerful Generic Patterns With Django

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

Page 64: Powerful Generic Patterns With Django

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

Page 65: Powerful Generic Patterns With Django

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

Page 66: Powerful Generic Patterns With Django

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} )

Page 67: Powerful Generic Patterns With Django

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

Page 68: Powerful Generic Patterns With Django

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

Page 69: Powerful Generic Patterns With Django

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 %}

Page 70: Powerful Generic Patterns With Django

FOR THE WIN

Page 71: Powerful Generic Patterns With Django

SCALE IT OUT

1.Define A Model2.Sync DB3.Make A Template ( Maybe ? )4.Rinse 5.Repeate

Page 72: Powerful Generic Patterns With Django

PATTERNS RECAP

• Self Aware Models• Better Generic Views & utilities• Universial URLs• Magic Forms

Page 73: Powerful Generic Patterns With Django

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.

Page 74: Powerful Generic Patterns With Django

FIN