Sleeping with the enemy

by George Brocklehurst (@georgebrock)

I work for thoughtbot

Presented at Ruby Manor 4, and RailsConf 2013,

This talk is a comparison of Rails and Django. All of the code examples come from a Django application which can be found here: github.com/georgebrock/swte-demo

They’re not really the enemy!

You say ‘tomato’, I say ‘tomato’

Anatomy of a Django project

Project layout

        ├── demo
        │   ├── __init__.py
        │   ├── settings.py
        │   ├── urls.py
        │   └── wsgi.py
        ├── apps
        │   ├── __init__.py
        │   ├── blog
        │   │   ├── __init__.py
        │   │   ├── models.py
        │   │   ├── views.py
        │   │   └── ...
        │   └── ...
        ├── manage.py
        ├── requirements.txt
        └── templates
            └── ...
      

Models

from django.db import models

class BlogPost(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
    published = models.BooleanField()
    publish_date = models.DateTimeField()

See apps/blog/models.py @ 54321ab

Displaying blog posts

Models on their own aren't very interesting: we need to display them somehow. In Django, we do this using views

Views are like actions

  1. Request
  2. View
  3. Response

Same old, same old

View code, like Rails action code, gets very repetitive. How Django removes the repetition by providing generic class-based views; classes we can use as the basis of our views.

from django.views.generic import DetailView
from apps.blog.models import BlogPost

class BlogPostDetailView(DetailView):
    model = BlogPost
 
 
 
 
 
 
 
 

See apps/blog/views.py @ 54321ab

from django.views.generic import DetailView
from apps.blog.models import BlogPost

class BlogPostDetailView(DetailView):
    model = BlogPost
    queryset = BlogPost.objects.filter(
        published=True,
    )
 
 
 
 
 

See apps/blog/views.py @ 0e54a93

from django.views.generic import DetailView
from apps.blog.models import BlogPost

class BlogPostDetailView(DetailView):
    model = BlogPost

    def get_queryset(self):
        if self.request.GET.get("preview"):
            return BlogPost.objects.all()
        else:
            return BlogPost.objects.filter(
                published=True,
            )

See apps/blog/views.py @ 31374df

Behind the scenes

The template method pattern

Creating blog posts

So far we've only looked at displaying data. Another very common pattern is getting data from the user and adding it to a database.

  1. HTML form
  2. POST
  3. ?: Something happens in our application
  4. SQL
  5. Database
from django.views.generic import CreateView
from apps.blog.models import BlogPost

class BlogPostCreateView(CreateView):
    model = BlogPost

See apps/blog/views.py @ 235d27d

Forms

  1. Form
  2. Field
  3. Widget

A concrete example

  1. Form
  2. DateField
  3. DateSelectWidget
  4. Date select widget
from django.views.generic import CreateView
from apps.blog.models import BlogPost
from apps.blog.forms import BlogPostForm

class BlogPostCreateView(CreateView):
    model = BlogPost
    form_class = BlogPostForm

See apps/blog/views.py @ e95b5c4

from django import forms
from django.forms.extras.widgets import \
    SelectDateWidget
from apps.blog.models import BlogPost

class BlogPostForm(forms.ModelForm):
    class Meta:
        model = BlogPost
        exclude = ("published", )
        widgets = {
            "publish_date": SelectDateWidget(),
        }

See apps/blog/forms.py @ e95b5c4

Displaying the form

Screenshot of the form
The user interface produced by this Form.

Submitting the form

  1. HTML form
  2. "publish_date_year=2013&publish_date_month=4&publish_date_day=30"
  3. Plumbing
  4. {"publish_date_year": "2013", "publish_date_month": "4", "publish_date_day": "30"}
  5. BlogPostCreateView
  6. BlogPostForm
  7. {"publish_date_year": "2013", "publish_date_month": "4", "publish_date_day": "30"}
  8. SelectDateWidget
  9. "2013-04-30"
  10. DateField
  11. datetime.date(2013, 4, 30)
  12. BlogPostForm
  13. {"publish_date": datetime.date(2013, 4, 30)}
  14. BlogPost

What happens in Rails?

  1. HTML form
  2. "blog_post[publish_date(1i)]=2013&blog_post[publish_date(2i)]=4&blog_post[publish_date(3i)]=30"
  3. Plumbing
  4. {:blog_post => {:'publish_date(1i)' => '2013', :'publish_date(2i)' => '4', :'publish_date(3i)' => '30'}}
  5. BlogPostsController
  6. {:'publish_date(1i)' => '2013', :'publish_date(2i)' => '4', :'publish_date(3i)' => '30'}
  7. BlogPost
  8. ActiveRecord::AttributeAssignment
  9. [2013, 4, 30]
  10. Date.new(2013, 4, 30)

The single responsibility principle

Awesome!

Any questions?

Ask now, or later: @georgebrock on Twitter or email george@thoughtbot.com

These slides: http://georgebrock.com/railsconf2013

I work for thoughtbot