What's new in Django community blogs?

Best of PyCon 2014

Apr 18 2014 [Archived Version] □ Published at Alex Gaynor

This year was my 7th PyCon, I've been to every one since 2008. The most consistent trend in my attendance has been that over the years, I've gone to fewer and fewer talks, and spent more and more time volunteering. As a result, I can't tell you what the best talks to watch are (though I recommend watching absolutely anything that sounds interesting online). Nonetheless, I wanted to write down the two defining events at PyCon for me.

The first is the swag bag stuffing. This event occurs every year on the Thursday before the conference. Dozens of companies provide swag for PyCon to distribute to our attendees, and we need to get it into over 2,000 bags. This is one of the things that defines the Python community for me. By all rights, this should be terribly boring and monotonous work, but PyCon has turned it into an incredibly fun, and social event. Starting at 11AM, half a dozen of us unpacked box after box from our sponsors, and set the area up. At 3PM, over one hundred volunteers showed up to help us operate the human assembly line, and in less than two and a half hours, we'd filled the bags.

The second event I wanted to highlight was an open space session, on Composition. For over two hours, a few dozen people discussed the problems with inheritance, the need for explicit interface definition, what the most idiomatic ways to use decorators are, and other big picture software engineering topics. We talked about design mistakes we'd all made in our past, and discussed refactoring strategies to improve code.

These events are what make PyCon special for me: community, and technical excellence, in one place.

PS: You should totally watch my two talks. One is about pickle and the other is about performance.


Migrating from django-registration to django-allauth

Apr 16 2014 [Archived Version] □ Published at Rachel's Knowledge Base under tags  django

I’ve been wanting to migrate away from django-registration for a while. Yes, I know it’s been the default registration package since forever and I’ve used it happily for many years. But : it’s no longer actively maintained I want to do away with the email confirmation I want to allow login using either email or […]

The post Migrating from django-registration to django-allauth appeared first on Rachel's Knowledge Base.


Sync Personal Docs on Kindle for Mac

Apr 16 2014 [Archived Version] □ Published at Nerdy Dork under tags  kindle technology

Recently Luke lamented that his favorite ebook reader was being sunset. I suggested Kindle. I was showing off the features I liked about Kindle when I realized that while I could send personal documents (PDF, MOBI) to my kindle devices (Kindle, iPad, iPhone), these personal documents were not showing up in Kindle for Mac or […]

The post Sync Personal Docs on Kindle for Mac appeared first on Dustin Davis.


Beware of Django default Model Field Option When Using datetime.now()

Apr 15 2014 [Archived Version] □ Published at atodorov.org

Beware if you are using code like this:

models.DateTimeField(default=datetime.now())

i.e. passing a function return value as the default option for a model field in Django. In some cases the value will be calculated once when the application starts or the module is imported and will not be updated later. The most common scenario is DateTime fields which default to now(). The correct way is to use a callable:

models.DateTimeField(default=datetime.now)

I've hit this issue on a low volume application which uses cron to collect its own metrics by calling an internal URL. The app was running as WSGI app and I wondered why I got records with duplicate dates in the DB. A more detailed (correct) example follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def _strftime():</p>

<pre><code>return datetime.now().strftime('%Y-%m-%d')
</code></pre>

<p>class Metrics(models.Model):</p>

<pre><code>key = models.IntegerField(db_index=True)
value = models.FloatField()
added_on = models.DateTimeField(db_index=True, default=datetime.now)
added_on_as_text = models.CharField(max_length=16, default=_strftime)
</code></pre>

<p>

Difio also had the same bug but didn't exhibit the problem because all objects with default date-time values were created on the backend nodes which get updated and restarted very often.

For more info read this blog.


Combine Dajax/Dajaxice with Django templating

Apr 14 2014 [Archived Version] □ Published at michalcodes4life under tags  ajax dajax dajaxice dajngo templating django

I have recently noticed that most of the Dajaxice examples on the web follow the same pattern: Setup Dajaxice Create ajax.py with a function returning JSON. Use JSON data in your Javascript function. While this is a perfectly good example, there is a better (less or no Javascript) way to tackle most of the common […]


Parsing tags with django-content-bbcode in examples

Apr 13 2014 [Archived Version] □ Published at RkBlog - Python, Linux, Astronomy under tags  django

Some time ago I've released django-content-bbcode - a BBCode alike tag parser. In this article I'll show some example of usage for such parser - from simple search and replace to more complex using database to get the response.


RuntimeError: App registry isn't ready yet

Apr 13 2014 [Archived Version] □ Published at Nicolas Kuttler

The upcoming Django 1.7 has many interesting and welcomed changes. One of them is an update to how applications are loaded.

The relase notes also mention potential problems with these changes. I ran into RuntimeError: App registry isn't ready yet while using the self-contained tests approach, but the fix is as simple as mentioned in the relase notes.

import django
django.setup()

If your code needs to run on Django < 1.7 as well you could use

# Django 1.7
try:
    import django
    django.setup()
except AttributeError:
    pass

Update: Dieter's comment actually made me think about my code for a second and I'll be using something different:

import django
if hasattr(django, 'setup'):
    django.setup()


Server/Client With React, Part 3: Frontend Server

Apr 11 2014 [Archived Version] □ Published at Eric Florenzano's Blog

Server/Client With React, Part 3: Frontend Server

Apr 11, 2014

In the past two posts, we started writing the client code, and then made it build. What's stopping us from loading it in our browser? It's not being served yet! Let's fix that, starting with installing Connect -- the middleware we'll use to build our http server.

npm install --save connect

Why not some other thing like Koa, hapi, or mach? No real reason. Our needs are simple and any one of those would work well. I chose Connect because it's popular, it'd been around a while, and it seemed to work well enough.

Now let's create a file server.js right at the root of our project, starting with the basics, and we'll fill in more as we go:

var connect = require('connect');

// Set up the application and run it
var server = connect();
  .use(connect.static(__dirname + '/build'))
  .use(connect.logger())
  .use(connect.csrf())
  .use(connect.urlencoded())
  .use(connect.query())
  .use(connect.json())
  .listen(5000);

Running this file will start up a server listening on port 5000, serving any static files that are found in /build, which knows how to parse querystrings, form submissions, and json, and is protected against CSRF attacks. So far, so easy. Now let's add cookie sessions, where I've found that maxAge needs to be set per-request in Connect for some reason:

var connect = require('connect');

var IRLMOJI_COOKIE_SECRET = process.env['IRLMOJI_COOKIE_SECRET'];

function fixConnectCookieSessionHandler(req, res, next) {
  req.session.cookie.maxAge = 365 * 24 * 60 * 60 * 1000;
  return next();
}

// Set up the application and run it
var server = connect();
  .use(connect.static(__dirname + '/build'))
  .use(connect.logger())
  .use(connect.cookieParser())
  .use(connect.cookieSession({
    secret: IRLMOJI_COOKIE_SECRET,
    cookie: {maxAge: 365 * 24 * 60 * 60 * 1000, proxy: true}
  }))
  .use(fixConnectCookieSessionHandler)
  .use(connect.csrf())
  .use(connect.urlencoded())
  .use(connect.query())
  .use(connect.json())
  .listen(5000);

Now we're up and running with a cookie-based session system, but we're not yet using it. In fact, we're not using any of this yet, because we're not rendering or serving the main site yet. We can write that now:

// Note that we're importing from the build directory
var makeRouter = require('./build/javascript/router').makeRouter;
var routes = require('./build/javascript/routes');

function reactHandler(req, res, next) {
  // Render implemented here so it can capture req/res in its closure
  function render(reactComp, opts) {
    // We'll implement this next
  }

  var app = {
    render: render,
    isServer: function() {
      return true;
    },
    getUrl: function() {
      var proto = req.headers['x-forwarded-proto'] || 'http';
      return proto + '://' + req.headers.host + req.url;
    },
    getPath: function() {
      return req.url;
    }
  };

  var router = makeRouter(
    routes.getRoutes(app),
    routes.getNotFound(app)
  );

  router.go(app.getPath());
}

// ...

// Set up the application and run it
var server = connect();
  .use(connect.static(__dirname + '/build'))
  .use(connect.logger())
  // ...
  .use(reactHandler)
  .listen(5000);

The basic idea here is to build an app object that exactly mimics the functionality available on the app object in frontend/javascript/client.js that we built in part 1. To do so, we create this object on-the-fly using the information available to us from the request. Then we import that same simple router we used before, and tell the router to route and render its contents by calling the go function with the current path as a parameter.

How do we actually render it though? We left that function blank. Before we work on that, we need some sort of HTML template to work from. Let's build our basic HTML page template in frontend/page.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
  <meta name="description" content="Take a pic that looks like an emoji!">
  <meta name="author" content="IRLMoji">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <meta content="yes" name="apple-mobile-web-app-capable">
  <title>{{ PAGE_TITLE }}</title>
  <script src="//cdnjs.cloudflare.com/ajax/libs/es5-shim/2.2.0/es5-shim.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/es5-shim/2.2.0/es5-sham.min.js"></script>
  <link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
  <link href="{{ STYLE_PATH }}" rel="stylesheet" media="screen">
</head>
<body class="{{ BODY_CLASS }}">
  <div id="react-root">{{ BODY_CONTENT }}</div>
  <input style="display: none" type="hidden" id="csrftoken" name="csrf" value="{{ CSRF_TOKEN }}" />
  <script src="{{ SCRIPT_PATH }}"></script>
</body>
</html>

This "template" has no real logic (it's just a frame) so we can use basic variable substitution -- in this case, in the style of Django templates. So that's what our render function will have to do: determine what should be inserted for e.g. BODY_CONTENT and PAGE_TITLE, render the template with that content, and serve it up to the user. Here's a first stab at it:

var fs = require('fs');
var _ = require('lodash/dist/lodash.underscore');

var NODE_ENV = process.env['NODE_ENV'];
var PROD = NODE_ENV === 'production';

// Read the whole template into memory, no need to re-read it every request
var PAGE_TEMPLATE = fs.readFileSync('frontend/page.html');

function render(reactComp, opts) {
    opts = opts || {};

    // Render the React component to a string
    var bodyContent = React.renderComponentToString(reactComp);

    // Build up the list of variable substitutions
    var sub = {
      BODY_CLASS: opts.bodyClass || '',
      BODY_CONTENT: bodyContent,
      CSRF_TOKEN: req.csrfToken(),
      SCRIPT_PATH: '/javascript/compiled' + (PROD ? '.min' : '') + '.js',
      STYLE_PATH: '/styles/main' + (PROD ? '.min' : '') + '.css',
      PAGE_TITLE: opts.title || 'IRLMoji'
    };

    // Create a regex out of the variable substituion object
    var re = new RegExp('{{ (' + _.keys(sub).join('|') + ') }}', 'g');

    // Start the response
    res.writeHead(opts.statusCode || 200, {'Content-Type': 'text/html'});

    // Substitute all the variables and write it to the response to finish
    res.end(('' + PAGE_TEMPLATE).replace(re, function(m) {
      return sub[m.substring(3, m.length - 3)];
    }));
}

We could have used any templating language, but as you can see, most of what's going on happens inside of react-root, so this is all we'll need.

Hey, we're live! If we start up our server by running:

gulp watch

We'll see build directory cleaned up, then the files generated, then the server will start up. Cool! Let's open the browser to http://127.0.0.1:5000, and it should say "Hello, World!", as that's what we have in our handleIndex() function in frontend/javascript/routes.js.

You can check out what the fully-built demo site is doing in server.js by visiting the code on github.

What is happening here?

Now that we've got the basic structure of our site set up, what all is happening?

  • The server looks at the URL, routes to the right React component, and renders our hello world component to a string.
  • Then it interpolates that string into the html page template and servers it up to the user.
  • In that template, we've told the browser to load a script which is the browserified (and potentially minified) version of client.js, which is an implementation of the app that was used to render the page. (Whoa.)
  • The browser downloads and executes that script, which in turn runs its router on the client side and routes to the same component.
  • React does a fast checksum and notices that, hey, the markup we just generated on the client matches what was just served from the server, so it doesn't change the DOM.

So now we've loaded a javascript implementation of the website frontend, and attached it to the existing markup that was served down the wire. Pretty cool, but right now we're not taking advantage of that. Soon we will :)

What's Next?

  • Build the communications layer between the frontend and the API
  • Ensure that the client re-uses the same data the server used when it rendered
  • Build a basic IRLMoji timeline
  • Implement camera upload by interfacing with non-React JavaScript Dropzone.js
  • Finish building the app and deploy it


Numerical Python

Apr 11 2014 [Archived Version] □ Published at python under tags  programming python science

For the past few months, I've been covering different software packages for scientific computations. For my next several articles, I'm going to be focusing on using Python to come up with your own algorithms for your scientific problems. more>>


Server/Client With React, Part 2: The Build System

Apr 10 2014 [Archived Version] □ Published at Eric Florenzano's Blog

Server/Client With React, Part 2: The Build System

Apr 10, 2014

In the last post, we started writing the client glue code and our first React component. But we're using things like require() for something that should be run on the client, how will that work? Browserify can take a look at our client.js, walk the dependencies, and spit out something that browsers can actually execute. Let's set that up!

But first a bit about we'll structure the app. This is how things look currently:

frontend/
    javascript/
        client.js
        routes.js
        components/
            common.js

But these JavaScript files need processing. Some of them have JSX in them, which needs to be converted to pure JavaScript. It all needs to be transformed to work in the browser. Additionally, we may have other non-JS files that we want to preprocess. To address this, we'll do all this processing and save it in a directory adjacent to frontend named build.

Here's the basic idea of how things will look after this blog post:

build/
    images/
    javascript/
        compiled.js
        compiled.min.js
        client.js
        client.min.js
        routes.js
        routes.min.js
        components/
            common.js
            common.min.js
    styles/
        main.js
        main.min.js
frontend/
    images/
    javascript/
        client.js
        routes.js
        components/
            common.js
    styles/
        main.less
    tests/
gulpfile.js
package.json

First make sure you have a package.json file. If you're not familiar with this file, here's some light reading about it. We're going to use Gulp to do our processing, and we need a number of plugins to make that work. These commands should get you started:

npm install --save react react-tools lodash
npm install --save-dev browserify envify gulp gulp-util gulp-uglify \
    gulp-clean gulp-rename gulp-browserify gulp-less gulp-react \
    gulp-minify-css gulp-nodemon

Now that we have the dependencies installed, let's create a file named gulpfile.js which will describe to Gulp how to build the app:

var gulp = require('gulp');
var clean = require('gulp-clean');

gulp.task('clean', function() {
  return gulp.src(['build/*'], {read: false}).pipe(clean());
});

So far it's not that impressive, all we're doing is deleting anything inside the build/, directory, which doesn't even exist. We noted earlier that some of the JavaScript had JSX in it, and would need to be processed, so let's set that up by adding this to gulpfile.js:

var react = require('gulp-react');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');

// Parse and compress JS and JSX files

gulp.task('javascript', function() {
  // Listen to every JS file in ./frontend/javascript
  return gulp.src('frontend/javascript/**/*.js')
    // Turn React JSX syntax into regular javascript
    .pipe(react())
    // Output each file into the ./build/javascript/ directory
    .pipe(gulp.dest('build/javascript/'))
    // Optimize each JavaScript file
    .pipe(uglify())
    // Add .min.js to the end of each optimized file
    .pipe(rename({suffix: '.min'}))
    // Output each optimized .min.js file into the ./build/javascript/ dir
    .pipe(gulp.dest('build/javascript/'));
});

Now let's set up browserify to transform the require() calls into JavaScript the browser understands. You'll notice this matches the structure of the previous code block almost exactly, except we're swapping out a call to browserify() instead of a call to react():

var browserify = require('gulp-browserify');

gulp.task('browserify', ['javascript'], function() {
  return gulp.src('build/javascript/client.js')
    .pipe(browserify({transform: ['envify']}))
    .pipe(rename('compiled.js'))
    .pipe(gulp.dest('build/javascript/'))
    .pipe(uglify())
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest('build/javascript/'));
});

And like I mentioned, there are things other than JavaScript which need to be processed too!

var less = require('gulp-less');
var minifycss = require('gulp-minify-css');

gulp.task('styles', function() {
  return gulp.src('frontend/**/*.less')
    .pipe(less())
    .pipe(gulp.dest('build/'))
    .pipe(minifycss())
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest('build/'));
});

But no gulpfile.js is complete without a default task that will run when the user types just gulp in their console:

gulp.task('default', ['clean'], function() {
  return gulp.start('browserify', 'styles');
});

This says to run clean as a dependency, then to run the browserify and styles tasks in parallel, and finish when everything returns. So just to hammer this point home, all you need to do to build everything is to open a terminal, navigate to your project directory and type:

gulp

Local Development

Doing local development is slightly more tricky, but not so bad to set up. What we'd like is to be able to run a command, and then have it watch for changes and automatically rebuild the parts that have changed. Let's add a watch task which does this:

var nodemon = require('gulp-nodemon');

gulp.task('watch', ['clean'], function() {
  var watching = false;
  gulp.start('browserify', 'styles', function() {
    // Protect against this function being called twice. (Bug?)
    if (!watching) {
      watching = true;

      // Watch for changes in frontend js and run the 'javascript' task
      gulp.watch('frontend/**/*.js', ['javascript']);

      // Run the 'browserify_nodep' task when client.js changes
      gulp.watch('build/javascript/client.js', ['browserify_nodep']);

      // Watch for .less file changes and re-run the 'styles' task
      gulp.watch('frontend/**/*.less', ['styles']);

      // Start up the server and have it reload when anything in the
      // ./build/ directory changes
      nodemon({script: 'server.js', watch: 'build'});
    }
  });
});

Most of this is fairly self-explanatory except two things:

  • What is 'browserify_nodep'?
  • We haven't built server.js yet.

The latter we'll tackle in the next post, but the reason for 'browserify_nodep' is that we don't need/want all the javascript to be rebuilt every time just client.js changes. We have something already watching for that. So let's modify our browserify task and split it into browserify and browserify_nodep:

function browserifyTask() {
  return gulp.src('build/javascript/client.js')
    .pipe(browserify({
      transform: ['envify']
    }))
    .pipe(rename('compiled.js'))
    .pipe(gulp.dest('build/javascript/'))
    .pipe(uglify())
    .pipe(rename({suffix: '.min'}))
    .pipe(gulp.dest('build/javascript/'));
}

gulp.task('browserify', ['javascript'], browserifyTask);
gulp.task('browserify_nodep', browserifyTask);

This is why it's awesome that Gulpfiles are just javascript files: we can break out common functionality into a function, and apply it to different tasks. If you ever want to see the fully finished gulpfile.js for the final project, you can check it out here.

What's Next?

  • Write the server.js that mimics the client.js we've been building and acts as http server.
  • Build the communications layer between the frontend and the API
  • Ensure that the client re-uses the same data the server used when it rendered
  • Oh yeah, write our app :)


Getting ready for Django 1.7

Apr 10 2014 [Archived Version] □ Published at The Django weblog

Django 1.7 is shaping up to be the biggest Django release since 1.0. It has a new app loading framework, a new checks framework, many improvements to query construction, and most importantly - Migrations.

Since it's going to be such a big release, we need your help! If we're going to publish a high quality, bug free release, and do it on schedule, we need everyone in the community to pitch in. Django releases are only as good as the testing they receive, and we can only fix the bugs that people report. So - if you're wondering how you can get involved with Django development, now is the perfect time. Download the Django 1.7 beta, and run your existing projects and test suites against it. If you spot any problems, let us know by opening a ticket.

But it's not all hard work. Since this is such a big release, we thought it might be nice to celebrate - with a shirt!

Starting today, we're running a TeeSpring campaign to sell a 1.7 release shirt. We've commissioned some artwork especially for this campaign; this artwork is available on:

  • a Men's American Apparel "New silver" T-shirt ($20),
  • a Women's Bella "Athletic heather" T-shirt ($19.50), or
  • a Gildan 8oz "Ash Grey" hoodie ($30.50).

$10 from every item sold goes to the Django Software Foundation, to help fund activities like sprints, and travel grants to DjangoCon.

TeeSpring is a crowd funding site - so we need to hit a minimum of 200 sales to confirm the print run. If we don't hit that target, there won't be any shirts! It also means that there's a time limit - you need to place your order by April 24. If you don't place an order before then, you'll miss out!

So - get testing, place your orders, and spread the word!


Django debugtoolbar for site running inside virtualbox/vagrant

Apr 10 2014 [Archived Version] □ Published at Reinout van Rees' weblog under tags  apple django

I had a hard time getting the Django debug toolbar to work this morning. Complicating factor: the site runs inside a vagrant virtualbox and my browser is simply running inside OSX.

(I use vagrant and virtualbox, see why I use vagrant and my vagrant setup on OSX)

Ok, I thought I had installed everything correctly. What can be the problem? I checked and double checked. The best way to verify your settings is to use django's own "diffsettings" management command, that way you're sure you're looking at the right settings:

$ bin/django diffsettings
...
DEBUG = True
INSTALLED_APPS = ['debug_toolbar', 'lizard5_site', ... ]
INTERNAL_IPS = ('33.33.33.20', '127.0.0.1', '0.0.0.0')
...
MIDDLEWARE_CLASSES = ('debug_toolbar.middleware.DebugToolbarMiddleware', ...)
...

Debug mode is on, it is in the INSTALLED_APPS and I've correctly enabled the middleware. Oh, and I've adjusted the INTERNAL_IPS setting.

  • 127.0.0.1 even though I don't really talk to the site from within my vagrant box.
  • 0.0.0.0 as that's what I start my runserver with inside vagrant (bin/django runserver 0.0.0.0:8000): listen on everything on port 8000.
  • 33.33.33.20 as that's the default IP address that vagrant/virtualbox gives me to talk to the virtualbox from within OSX. So on my laptop you'll normally see http://33.33.33.20:8000/ instead of http://localhost:8000/ :-)

The 0.0.0.0 shouldn't be needed, but at that time I was just trying things out to no avail.

... Time to call in the artillery. We have the source code, so I looked up the debug toolbar version I was using and put an import pdb;pdb.set_trace() into the def show_toolbar(request) method inside debug_toolbar/middleware.py.

The first lines of that short function are:

if request.META.get('REMOTE_ADDR', None) not in settings.INTERNAL_IPS:
    return False

In went the pdb and I reloaded the homepage:

> /.../django_debug_toolbar-1.0.1-py2.7.egg/debug_toolbar/middleware.py(26)show_toolbar()
-> if request.META.get('REMOTE_ADDR', None) not in settings.INTERNAL_IPS:
(Pdb) request.META['REMOTE_ADDR']
'33.33.33.1'

Sure enough, the error was in my INTERNAL_IPS setting after all. REMOTE_ADDR on the request turned out to be 33.33.33.1! Even though I talk to it from OSX with 33.33.33.20. So there must be some internal virtualbox/vagrant trickery that does some mapping here.

So: if you use the django debug toolbar inside vagrant: make sure you've got the correct port in the INTERNAL_IPS setting!


Django Update - 2014-03-23 to 2014-04-05

Apr 09 2014 [Archived Version] □ Published at The Django weblog

Overview

Sorry it's late this time, folks... a lack of user submissions, and an excess of work :/

Notices

As you've all seen, OpenSSL was broken. Update your libs. Update your keys, Update your certs.

Ticket Movement

Short lived tickets: 36

Tickets Created: 33

Open tickets: 1398 (+9)

Projects

Matthew Schinkel's proposal for allowing defining more complex constraints has resurfaced. This could be very useful, but a syntax is not forthcoming...

Ticket 22298 has been talked about, which is removing the Media class from widgets, has also joined a number of related tickets on ensuring we don't say "media" when we mean "static".

Summary

-- Have a better one!


Server/Client With React, Part 1: Getting Started

Apr 09 2014 [Archived Version] □ Published at Eric Florenzano's Blog

Server/Client With React, Part 1: Getting Started

Apr 09, 2014

A few months ago I wrote about how React.js has made it possible (and relatively easy) to write code that renders markup quickly on the server, and also can be executed on the client to perform all page updates. Now that we know what the goal is, we can explore how to put all these pieces together, or how I've managed to do it anyway.

Let's Build Something!

It's easiest to talk about concepts if we can also work on something concrete, so in the next few posts we'll build a little example site called IRLMoji, which asks users to post pictures that look like emoji. All credit goes to @dwiskus for originally starting this meme on Twitter. But we're going to turn it into its own app for demo purposes, which will look something like this:

A screenshot of what we are building

It's definitely not pretty (sorry), but it's got all the stuff we need to learn about React including auth, content creation, server communication, integration with third-party JS libraries, etc. If you'd like to follow along and see the code for the finished site, it's all up on GitHub: https://github.com/ericflo/irlmoji.

Let's Begin

There will be two entry points to our app: server.js, which will sit at the project's top level, and frontend/javascript/client.js. Let's start with the client, because it's a bit simpler. First let's start small and build a render() function, which will take a React component and some options, and render it to the page:

function render(reactComp, opts) {
  opts = opts || {};
  var elt = document.getElementById('react-root');
  React.renderComponent(reactComp, elt);
}

All we're doing here is calling React.renderComponent on the component that was passed in, rendering the component into that #react-root DOM element. We'll end up making this function do a bit more work in a future post, but for now let's move on to writing some handlers, and hooking them up to URL routes.

In the past I've used director to handle URL routing, but eventually I wrote a small router which escews hashbang-style URLs in favor of HTML5 pushState. If needed, we can always fall back to full page reloads, rather than resort to hashbangs. Now we've got this router to work with, let's put it to use, starting with a new file at frontend/javascript/routes.js:

/** @jsx React.DOM */

// Lodash is a fast/light underscore replacement that works better for me
var _ = require('lodash/dist/lodash.underscore');
var common = require('./components/common');

// Renders the index page, for now just "Hello, World!"
function handleIndex(app) {
  app.render(<p>Hello, World!</p>, {
    title: 'Welcome',
  });
}

// Prepares a handler for use. For now all we're doing is making sure
// that 'app' is passed as the first argument to the handler function.
function prepareHandler(app, handler) {
  return _.partial(handler, app);
}

// Gets a list of routes to be passed to the router
function getRoutes(app) {
  return [
    ['/', prepareHandler(app, handleIndex)]
  ];
}

// Gets a function that should be called when no routes match
function getNotFound(app) {
  return function() {
    // At the time this post was written, JSX does not allow namespacing,
    // so we need to grab a reference to the NotFound component class.
    var NotFound = common.NotFound;
    app.render(<NotFound />, {statusCode: 404});
  };
}

module.exports = {
  getRoutes: getRoutes,
  getNotFound: getNotFound
};

"Wait a minute," you may be thinking. What is this app thing that every function seems to refer to? The app is what I've chosen to call the object that is used to tie everything together. It's because of this object that we're able to use one codebase for both server and client. Right now all we're using is the render function that we created earlier, but through the interface of this app object. Let's go back to client.js and create a basic app object.

var app = {
  render: render,
  isServer: function() {
    return false;
  },
  getUrl: function() {
    return '' + window.location;
  },
  getPath: function() {
    return window.location.pathname + window.location.search;
  }
};

Here we've got a basic app object, with access to a render function and a few helpers like the ability to get the current path or get the full URL or to detect whether we're on the server or the client. We're building this in client.js, so we know we're not on the server, and can just return false.

Now that we have our app, and our routes, let's tie them together:

// Import the routes we created earlier
var routes = require('./routes');
// ...and the simple router we're using
var makeRouter = require('./router').makeRouter;

app.router = makeRouter(routes.getRoutes(app), routes.getNotFound(app));
app.router.start();

To finish up this part, we still have to create that NotFound React component, so let's create a new file in frontend/javascript/components/common.js with this as its content:

/** @jsx React.DOM */

var React = require('react/addons');

var NotFound = React.createClass({
  render: function() {
    return <p>That page could not be found.</p>;
  }
});

module.exports = {NotFound: NotFound};

What's Next?

It would be great if we could fire up our browsers now and see in action what we've built so far. Unfortunately, however, we haven't built the server yet. Here are some of the high level things that we're going to cover next:

  • Set up Gulp and Browserify to compile our node JavaScript into Browser JS
  • Write the server.js that mimics the client.js we've been building and acts as http server.
  • Build the communications layer between the frontend and the API
  • Ensure that the client re-uses the same data the server used when it rendered
  • Oh yeah, write our app :)


Caktus has a new website!

Apr 09 2014 [Archived Version] □ Published at Caktus Blog

It’s been a few years since we last updated our website, and we gave it a whole new look! With the new site, it’s easy to see just what services we offer, and our processes for bringing our client’s ideas to life. The new layout allows for more in-depth reviews of our projects, and also...


django-planet aggregates posts from Django-related blogs. It is not affiliated with or endorsed by the Django Project.

Social Sharing

Feeds

Tag cloud

admin administration advanced ajax apache api app app engine apple aprendiendo python articles asides audrey authentication automation backup bash basics bitbucket blog blog action day book books buildout business cache celery celerycrawler challenges cherokee choices class-based-views cliff cloud code coding couchdb css data database databases debian deploy deployment developers development digitalocean django djangocon django-rest-framework django templates documentation dojango dojo dreamhost dughh eclipse education email error events extensions fabric facebook family fashiolista fedora filter fix flash form forms friends gae games geek general gentoo gis git github gnome google google app engine gunicorn hackathon hacking hamburg heroku holidays hosting howto how-tos html http i18n image install intermediate internet ios iphone java javascript jobs journalism jquery json justmigrated linear regression linkedin linode linux mac machine learning math memcached mercurial meta migration mirror misc model models mod_wsgi mongodb mozilla mysql nelenschuurmans newforms nginx nosql ogólne open source open-source orm osx os x ottawa paas performance philosophy php pi pinax pip piston planet plone plugin pony postgres postgresql ppoftw private programmieren programming programming &amp; internet project projects pycon pyladies pypi pypy python quick tips quora rabbitmq rails rant ratnadeep debnath redis release request resolutions rest review rtnpro ruby science script security server setup simple smiley snaking software software development south sql ssh ssl static supervisor sysadmin talk nerdy to me tastypie tdd techblog technology template templates test testing tests tip tools tornado transifex travel tutorial twitter twoscoops typo3 ubuntu uncategorized unicode unix usergroup uwsgi uxebu virtualenv virtualenvwrapper web web 2.0 web design &amp; development webdev web development webfaction whoosh windows work