What's new in Django community blogs?

Two-Faced Django Part 2: Models and Django Testing

Dec 04 2007 [Archived Version] □ Published at Irrational Exuberance

In the [first part][partone] of this tutorial we put together our [files and folder configuration][polling1]. Now we are going to start putting together the core of functionality and data that represents the core of our project. One of the keys for our project being quick to build, and easy to test, is to implement all of our functionality only once, even though it will be used differently by both the Facebook and web apps. In the best case, which happens to be the one we're aiming for, all the code we'll write in the fb and web apps will just be a simple interface layer for our core functionality in the core app.

Okay. So a lot of silly words got thrown out in the last paragraph, now lets see what I am talking about.

Mapping functionality onto models

So, we want to start writing our models, but first we need to narrow down what our program will actually do. I've said that it will be a polling program, what does a simple polling application do?

  1. Make polls.
  2. Vote on polls.
  3. Make users who vote on polls.
  4. Delete polls if their creator wants to.

Sounds simple enough. Lets put together some models that can handle that:

from django.db import models
from django.contrib.auth.models import User as DjangoUser

class Poll(models.Model):
    question = models.CharField(unique=True, max_length=200)
    creator = models.ForeignKey('User')
    up_votes = models.IntegerField()
    down_votes = models.IntegerField()

class User(models.Model):
    user = models.ForeignKey(DjangoUser, blank=True, null=True)
    facebook_id = models.IntegerField(blank=True, null=True)
    name = models.CharField(max_length=200)

Looking at our Poll class, we can see that we are going to keep track of a question, the User who created the Poll, and the number of up and down votes it has received.

The User class is made slightly complex because we want to make the app oblivious of what interface (either the web or the fb apps in our project) is being used. Thus we have two potential ways of identifying a person with a User, either by their Facebook User ID, or by a django.contrib.auth.models.User instance.

Question Time: Do we really need to differentiate the Facebook users from webapp users?

Yes, unfortunately, we do. When we talk with a Facebook user, we are actually talking by means of an intermediary (Facebook), and have no direct connection, and thus we don't have the ability to recognize users by using cookies, and in general can't take advantage of the Django sessions or auth framework while dealing with Facebook users.

End of Question Time

Okay, so we have our models, but lets flesh them out a bit by making them accessible to the Django admin, and add a unicode method so that Django knows how to represent them. (And add some comments, just because we're professionals here, right?) This leaves us with:

from django.db import models
from django.contrib.auth.models import User as DjangoUser

class Poll(models.Model):
    """
    Model used for tracking polls and responses to the poll.
    """
    question = models.CharField(unique=True, max_length=200)
    creator = models.ForeignKey('User')
    up_votes = models.IntegerField()
    down_votes = models.IntegerField()
    class Admin:
        pass
    def __unicode__(self):
        return "%s asked by %s" % (self.question, self.creator)

class User(models.Model):
    """
    Model used for associating Facebook User IDs or
    django.contrib.auth.models.User instances with
    actions performed on our system.

    Only one of 'user' and 'facebook_id' should have a
    non-null value. Facebook users will not have an
    associated User, and regular web users will not
    have a 'facebook_id'.
    """
    user = models.ForeignKey(DjangoUser, blank=True, null=True)
    facebook_id = models.IntegerField(blank=True, null=True)
    name = models.CharField(max_length=200)
    class Admin:
        pass
    def __unicode__(self):
        return self.name

And those are our models. Good work everyone. (Note that a [zip file with all the work done in this tutorial][polling2] is available.)

Making sure it all works

Now, this is where a lesser tutorial or a lesser tutorial reader might dance into the increasingly complicated horizon. But not us, fair reader, not us. We are going to stop and take a gander at the Django testing framework and 'leverage' it to...

What the hell am I saying. We're going to write some tests.

In the Django testing framework, tests related to a specific app go in the app's folder itself, in a file named tests.py. Lets go ahead and make that (starting from our polling project folder):

emacs core/tests.py

Now lets put in some scaffolding for our tests.py file:

from django.utils import simplejson
from polling.core.models import Poll, User

MODELS = [Poll, User]

class CoreModelTest(unittest.TestCase):
    "Test the models contained in the 'core' app."
    def setUp(self):
        'Populate test database with model instances.'
        self.client = Client()
    def tearDown(self):
        'Depopulate created model instances from test database.'
        for model in MODELS:
            for obj in model.objects.all():
                obj.delete()
    def test_something(self):
        "This is a test for something."
        self.assertEquals(True, 1)

The setUp method is called before each test in a TestCase is called. Thus before every unittest (like test_function in the scaffolding above), the setUp function will be called. In the same way, after each unittest, tearDown will be called.

The tearDown method may look a little bit uneccessary. Why do we need to be so damn insistent about deleting all the models from the database after each test? Well, although there is a test database created for the Django tests to run in, it is created for each running of the test framework, and is not recreated after each individual unittest. Thus we can have situations where we are uncertain about the number and type of models in the test database at a given point. That is not particularly condusive to testing, and so I prefer to delete all previous models from the testing database after each unittest. Since we are using SQLite, this turns out to be a very quick operation, because the testing database is created in memory and thus has very quick access time (accompanied by very little permanence).

Now lets run our scaffolding.

python manage.py test

And it'll run our tests. Thats all there is to it. The bottom of the output text should look something like this:

...
----------------------------------------------------------------------
Ran 3 tests in 0.027s

OK
Destroying test database...

The three periods above the line of "------" each represent a passing test. When a test fails, the testrunner isn't afraid of letting you know. But lets verify that anyway. In the core/tests.py file, switch

        self.assertEquals(True, 1)

to

        self.assertEquals(True, 0)

Now run the tests again

python manage.py test

And this time our output looks like this:

..F
======================================================================
FAIL: This is a test for something.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/will/django/polling/../polling/core/tests.py", line 28, in test_something
    self.assertEquals(True, 0)
AssertionError: True != 0

----------------------------------------------------------------------
Ran 3 tests in 0.027s

FAILED (failures=1)
Destroying test database...

Okay. Now that we've gotten our test scaffolding together, lets write a better setUp method to actually populate the test database with some data.

Replace the old setUp method with this:

def setUp(self):
    'Populate test database with model instances.'
    self.client = Client()
    wl = User(name="Will Larson")
    wl.save()
    jb = User(name="Jack Bauer")
    jb.save()
    bc = User(name="Bill Clinton")
    bc.save()
    Poll.objects.create(question="Did you vote for me?", creator=bc)
    Poll.objects.create(question="Am I human?", creator=jb)
    Poll.objects.create(question="Are you still reading?", creator=wl)

Now lets run our tests and make sure the setUp function works.

python manage.py test

Okay, so that didn't work out that well. It should have thrown something like:

IntegrityError: core_poll.up_votes may not be NULL

Oops. We need to be initializing a value for the *up_vote and down_vote* fields in our Poll model! One of the things about writing tests is that it really forces us to figure out what the hell we are doing, and earlier rather than later. That is a Good Thing.

Fixing up the Poll model right quick

To initialize the values in the two fields in our Poll we are going to override the save function. This is a handy technique because it lets you dynamically determine values at time of creation, and also to sanitize incoming values if you need to. (Its worth pointing out that in this specific situation it would be more appropriate to use the default argument key for the field, to the tune of "up_vote = IntegerField(default=0)", but that would miss out on an opportunity for looking at the always handy technique of overriding save.)

So, before the unicode method in our Poll object in polling/core/models.py insert this code:

def save(self):
    """Overriding save() to innitialize values for up_votes and down_votes."""
    if not self.up_votes:
        self.up_votes = 0
    if not self.down_votes:
        self.down_votes = 0
    return super(Poll, self).save()

Reading through the code, if we fail to specify either the *up_vote or down_vote* fields in a Poll instance, then the save method will specify them for us before it gets to the default model save method (model is Poll's super class).

Lets save our polling/core/models.py file, and rerun our tests.

python manage.py test

And, dum de dum, everything works again.

Continuing the code-test cycle

Okay, so we have two more short implement-test cycles left in part two of this tutorial. I promise you can take a break soon.

First I want to create a couple of nifty methods for Poll to take care of voting the poll up or down, and also a function for calculating the current overall score for a poll. They're going to look like this:

def score(self):
    return self.up_votes - self.down_votes

def up(self):
    self.up_votes = self.up_votes + 1
    self.save()

def down(self):
    self.down_votes = self.down_votes + 1
    self.save()

Now lets right some tests to make sure that they work correctly. Opening back up polling/core/test.py:

    def test_poll(self):
        "This is a test for something."
        poll = Poll.objects.get(pk=1)
        up = poll.up_votes
        down = poll.down_votes
        self.assertEquals(up, poll.up_votes)
        self.assertEquals(down, poll.up_votes)
        self.assertEquals(up-down, poll.score())
        poll.up()
        up = up + 1
        self.assertEquals(up, poll.up_votes)
        poll.down()
        down = down + 1
        self.assertEquals(down, poll.down_votes)
        self.assertEquals(up-down, poll.score())

That should be a method of the CoreModelTest class. We retrieve a poll we have already created (it was created in the *setUp method of CoreModelTest), and then retrieve its initial values for up_votes and down_votes*. Then we have a few sanity checks, and then go about calling the up, down and score methods and verifying they are returning the values we expect.

So lets run our tests and see what happens:

python manage.py test

And they pass. Excellent.

One more quick cycle

Okay, now we want to add a wee bit of functionality to the User class to facilitate creating Poll instances. We want to make a create_poll method that creates a new Poll instance, created by the User instance whose method was called. Its going to look like this:

def create_poll(self, question):
    p = Poll(creator=self, question=question)
    p.save()
    return p

And a test to make sure it all works:

def test_user(self):
    "This is a test for the polling.core.model.User model."
    user = User.objects.get(pk=1)
    p = user.create_poll("Is this actually working?")
    self.assertEquals(user, p.creator)

And... you know the drill... run the test:

python manage.py test

And the tests all pass. Hallelujah.

Another exciting conclusion.

Okay. So we've worked through creating our models for our application, and have also introduced tests to our app, so we have some kind of proof that what we're doing makes some sense. You can download the snapshot of this project (what it looks like having completed this tutorial) [here][polling2].

When you're ready to move on, part three is waiting for you.


Two-Faced Django Part 1: Building a project that exists simultaniously on Facebook and the web

Dec 04 2007 [Archived Version] □ Published at Irrational Exuberance

  • You can find details about seeing a live version of this project, both web and Facebook interfaces, here.

Recently I have been spending a lot of time with Django and PyFacebook. There is a decent enough tutorial for getting started with PyFacebook, but based on reading some of the comments on the mailing list, it seems like a more comprehensive walkthrough might be appreciated.

This series of articles aims, in the clear step-by-step style of the Django tutorial, to take you through the process of building a Django application that exists as both a simple web application, and also as a Facebook application. The web and Facebook applications will store information in the same database, using the same models, and thus users of one interface will be able to interact with the other.

Also, in the spirit of capitalism and entrepreneurship, we are going to invade the Django tutorial's turf and build a big and better toy polling application. At this moment venture capitalists are trembling across Silicon Valley at the hit to their pocket books due to these polling apps. Or something alone those lines.

Lets take a look at what we're going to accomplish over the next hour or three:

  • Building models that we will share between the web app and the Facebook app.
  • Using generic views to avoid writing as much code as possible.
  • Templates using both HTML and FBML (Facebook Markup Language) for rendering our content.
  • Ajax using both Facebook's Javascript, and the JQuery javascript library.
  • Using the Django testing framework to bring Test Driven Development to the web.
  • Using newforms to validate data, as well as represent it.

A few things this series is unfortunately not going to cover:

  • How to adapt command line instructions to a Windows. I don't have a Windows box to play with.
  • Setting up mod_python or any other deployment mechanism on your server.

This tutorial aims to cover a lot of ground, but don't worry, it doesn't expect much previous knowledge from you. As long as you have a terminal, Python, and Subversion, we're going to get through this together.

Now, lets get started.

Installing Django

The very first thing we need to do is to install Django. We are using the SVN version of Django, which at the time of this writing is version 6794. So lets grab a copy. First navigate to a place you want the library to live.

Personally mine is in a directory like this '/Users/will/django/'. But its entirely a personal stylistic choice. Choose a directory, and go there and then type:

svn co http://code.djangoproject.com/svn/django/trunk/ django-trunk

This will checkout the most recent version of the Django trunk into a folder named 'django-trunk'.

Now we need to tell Python where Django is. Do that... we need to know where Python is. So lets find out:

python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"
/Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages

The location of your library may be a bit different. Thats okay. Now lets teach Python how to find Django.

ln -s `pwd`/django-trunk/django /Library/Frameworks/Python.framework/Versions/2.5/lib/python2.4/site-packages/django

This creates a symbolic link in your site-packages folder, so that Python can operate as if your you can installed your package there in the first place.

Lets confirm that this just worked. Lets fire up Python...

python -v

(The -v flag makes python very verbose about what it is doing, which is a bit painful for actually working with the interpreter, but not so bad now since we're just checking that an import works.) And now lets try to import Django.

>>> import django
import django # directory django
# django/__init__.pyc matches django/__init__.py
import django # precompiled from django/__init__.pyc

If things are not set up correctly, you'll get this instead:

>>> import django
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ImportError: No module named django

If that happens, reread the instructions above and try to figure out where you went wrong, and try again.

Installing PyFacebook

Now we're going to go through the same steps to install PyFacebook.

  1. Checkout PyFacebook from SVN:

    svn checkout http://pyfacebook.googlecode.com/svn/trunk/ pyfacebook
    

  2. Figure out your site-packages folder:

    python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"
    /Library/Frameworks/Python.framework/Versions/2.4/lib/python2.4/site-packages
    

  3. Symbolically link your checked out library to your site-packages folder.

    ln -s `pwd`/django-trunk/django /Library/Frameworks/Python.framework/Versions/2.5/lib/python2.4/site-packages/django
    

  4. Make sure it worked.

    python
    

and then...

import facebook

As long as you don't get an ImportError thrown, then you have correctly configured everything.

Setting up our project folder.

First we need to start a new Django project:

django-admin.py startproject polling

(If django-admin.py isn't in your path, then you'll need to refer to it via a relative or absolute path. It will be at "django-trunk/django/bin/django-admin.py", but you'll have to remember where you decided to checkout django-trunk.)

Now lets go into the project directory

cd polling

And our project is born. Now we're going to create three applications, each of which will be responsible for a separate slice of our project.

Our three apps will be:

  1. core: will hold our models, and all code that is not specific to either the web app or the Facebook app.
  2. web: will hold all code and resources specific to our web app.
  3. fb: will hold all code and resources specific to our Facebook app.

So now lets create those apps.

python manage.py startapp core
python manage.py startapp web
python manage.py startapp fb

PyFacebook has some functionality for setting up special facebook oriented Django apps, but it isn't particularly helpful, so we'll be setting things up ourselves.

Setting up our fb app

First your want to [download this file][middleware_replacement] into the polling/fb directory. It is explained more here, but is basically an unsophisticated set of functions I put together to avoid using the PyFacebook middleware. Save it as helpers.py, so it should be sitting at

polling/fb/helpers.py

Okay. Now we want to open up and edit the views.py file in fb.

emacs polling/fb/helpers.py

(Full disclosure: its actually okay to use something other than emacs. Although, I can't officially condone it.)

And you'll want to throw all of these imports into it:

from django.http import HttpResponse, HttpResponseRedirect
from django.views.generic.simple import direct_to_template
from django.shortcuts import get_object_or_404
from django.utils import simplejson
from polling.fb.helpers import *
from polling.core.models import *

Now save it. And forget about it for the time being.

Including our new apps in the polling/urls.py file

Fortunately including our new apps in the polling/urls.py file is quite painless. First lets open it up...

emacs polling/urls.py

And edit it so that it looks like this:

from django.conf.urls.defaults import *
urlpatterns = patterns('',
    (r'^admin/', include('django.contrib.admin.urls')),
    (r'^facebook/', include('polling.fb.urls')),
    (r'^', include('polling.web.urls')),
)

Save that, and we're done playing with urls.py for the time being.

A folder for our templates...

Move into the pollings folder, and now we're going to make a place to store all of our templates we make.

mkdir templates
mkdir templates/fb
mkdir templates/web

Remember that these folders should be at polling/templates, polling/templates/fb, and polling/templates/web. We'll be telling Django where to find these folders in a moment, when we edit the settings.py file.

A brief lesson in tedium: settings.py

Okay, the heading may be overstating the case a bit, its just not my favorite part of setting up a Django project. Fortunately, it won't take very long either. I promise I'll endure as long as you do.

So we have two groups of things to deal with in settings.py, the generic Django setup, and setting up the entries we need for PyFacebook. Setting up PyFacebook is easier, so lets start with that.

You'll need to add these global variables (just paste em in anywhere, is the less snobby explanation) to your polling/settings.py file. *Please note that you'll need to change the first values to real values, supplied to you via the Developer application on Facebook.*

FACEBOOK_API_KEY = '11111111111111111111111111111111111'
FACEBOOK_SECRET_KEY = '11111111111111111111111111111111111'
FACEBOOK_APP_NAME = "application_name"
FACEBOOK_INTERNAL = True
FACEBOOK_CALLBACK_PATH = "/facebook/"

Not too much to explain, but the callback path is set to "/facebook/" because that is where we included our fb app in polling/urls.py.

Now lets get to work at configuring the rest of our settings.py file.

We're going to use SQLite for our database (we're developing, not creating a production site):

DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = '/Users/will/django/polling/poll.db'

You'll need to change the database name to a real place on your system, preferiably in the polling folder we are working in.

ADMIN_MEDIA_PREFIX = '/admin_media/'

We're going to be setting up the devel media server in a bit, and will be using the /media/ prefix for it, so we won't want any competition.

TEMPLATE_DIRS = ('/Users/will/django/polling/templates/')

Like usual, you'll need to change the path to represent where the polling folder is on your filesystem.

And finally, installing our apps.

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'polling.core',
    'polling.web',
    'polling.fb',
)

Since all our models will actually be living in polling.core, we don't actually have to add the polling.web and polling.fb apps to the list of installed apps, but its a bit more consistent to do so anyway (and if we don't, but later did add some models to one or the other, we might be very confused why they weren't being noticed).

Phew. We're done with messing with settings.py.

Using the Django devel server to serve media

Again, as I've mentioned before, we're working on putting together our development setup, not a production setup. We are going to use the devel server to serve our media, but only because its very convenient: this is not an adequate solution to real world static file serving.

Okay, with that warning out of the way, lets get to it.

First we need to make a few folders (starting from inside the polling folder):

mkdir media
mkdir media/web
mkdir media/fb

And then we need to open up polling/urls.py for just a quick moment, and edit it so it looks like this:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '/Users/will/django/polling/media/'}),
    (r'^admin/', include('django.contrib.admin.urls')),
    (r'^facebook/', include('polling.fb.urls')),
    (r'^', include('polling.web.urls')),
)

Like usual, the reference to /Users/will/django/polling/media/ will have to be edited to reflect the location of your polling folder.

And we're done... setting up

If you didn't feel like doing all the changes yourself, [here is a zipped copy of my setup][polling1]. You will have to edit the polling/settings.py abd polling/urls.py to refer to the correct places on your system.

With that little change, we're finally finished setting everything up. Take a break. Drink some tea, and come back when you're ready to continue to part two where we start putting together our models.


Using JQuery with Django for Autocomplete Fields

Dec 01 2007 [Archived Version] □ Published at Irrational Exuberance

This article is not particularly polished, and expects some general Django knowledge. It does not require any JQuery experience.

Using Django with JQuery for Ajax is pretty easy, but figuring out how to do do a autocomplete form can be a bit more complex. So, here is a walkthrough.

I am using Frank Vega's autocomplete library. It is a bit simple, but entirely usable. Grab the javascript, and the css. You'll also need the Dimensions plugin for JQuery, and also JQuery itself. Shove all of those things into the 'js' folder in your media folder. It should look like this:

/media
    /js
        jquery-1.2.1.js
        dimensions.js
        autocomplete.js
        autocomplete.css

Our Templates

First we're going to write out templates. We'll be using two different templates, one base template to load our javascript and css files, and another smaller one we can include wherever we want to use the autocomplete form functionality.

<html lang=en>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <link type="text/css" rel="stylesheet" href="/media/js/autocomplete.css">
    <script type="text/javascript" src="/media/js/jquery-1.2.1.js"></script>
    <script type="text/javascript" src="/media/js/dimensions.js"></script>
    <script type="text/javascript" src="/media/js/autocomplete.js"></script>
    {% block extra_css %}{% endblock extra_css %}
    <title>{% block title %}books are social{% endblock title %}</title>
  </head>
  <body>
    {% block body %}
    {% endblock body %}
  </body>
</html>

and the smaller template:

<script type="text/javascript" >
$(function(){
  setAutoComplete("bookSearchField", "bookResults", "/lookup/?query=");
});
</script>
<label for="bookSearchField">Book: </label>
<input type="text" id="bookSearchField" name="bookSearchField">

When we want to use this, use the {% include "path/to/lookup.html" %} tag to include it (assuming you name the shorter snippet as lookup.html). Note that you'll want to include the shorter snippet into a larger template that extends the base template above, so that it has the necessary javascript libraries available.

Urls

Now we'll want to make our simple urls.py that we'll be communicating with (you'll need to update the name of the project and the application in the urls.py file):

from django.conf.urls.defaults import *
urlpatterns = patterns('project.app.views',
    (r'^/lookup/$', 'book_lookup'),
)

This may require a bit of tweaking to adapt to your specific situation, but it doesn't require any magic you haven't been exposed to in the Django tutorial.

The Model

A very very simple Book model that we'll be using to search by.

class Book(models.Model):
    name = models.CharField(max_length=200)

The View

Our view is quite simple, simple parsing the incoming GET request, and sending back some serialized data.

from django.utils import simplejson
from django.http import HttpResponse
from project.app.models import Book
def user_lookup(request):
    # Default return list
    results = []
    if request.method == "GET":
        if request.GET.has_key(u'query'):
            value = request.GET[u'query']
            # Ignore queries shorter than length 3
            if len(value) > 2:
                model_results = Book.objects.filter(name__icontains=value)
                results = [ x.name for x in model_results ]
    json = simplejson.dumps(results)
    return HttpResponse(json, mimetype='application/json')

Notice we use 'name__icontains' to search by the name of the books. This will probably translate into SQL as a LIKE query, which is historically considered to be a worse idea than delivering a nuclear warhead by a mule. It will likely be quite slow on a large database. This is an example, not production quality code.

The End

And thats it for using JQuery to do some simple autocompletion. Django really does make these things quite simple, although it can be a bit confusing when you're getting started.

Let me know if you need any help.


Using PyFacebook without the Facebook middleware

Nov 30 2007 [Archived Version] □ Published at Irrational Exuberance

Recently I have been experimenting with using PyFacebook to create a Facebook app. In the early stages of development I ran into a bit of a quandry: how to you use PyFacebook with Django, without using the PyFacebook middleware?

This is not an inrrelevant question, because the PyFacebook middleware is just bloat for any views being loaded that do not directly communicate with Facebook, and is even inappropriate for some views that do communicate directly with Facebook (requests made with the FBJSM Ajax object).

So, without further ado, lets look at how we can use PyFacebook with Django, without using the middleware.

You'll need to import the PyFacebook library into your views.py file to use the examples below. If you install the PyFacebook library into your Python path, it'll look like this:

import facebook.djangofb as facebook

Adding Facebook instances to requests

The first thing we want to do is figure out how to add Facebook instances to requests passed to our views.

def add_fb_instance(request):
    # if already has a facebook instance, immediately return
    if getattr(request, 'facebook', None) is not None:
        return request
    # auth_token is other important possible param
    api_key = settings.FACEBOOK_API_KEY
    secret_key = settings.FACEBOOK_SECRET_KEY
    app_name = getattr(settings, 'FACEBOOK_APP_NAME', None)
    callback_path = getattr(settings, 'FACEBOOK_CALLBACK_PATH', None)
    internal = getattr(settings, 'FACEBOOK_INTERNAL', True)
    request.facebook = facebook.Facebook(
        api_key=api_key,
        secret_key=secret_key,
        app_name=app_name,
        callback_path=callback_path,
        internal=internal
        )

The usage is fairly simple:

def canvas(request):
    add_fb_instance(request)
    fb = request.facebook
    # do whatever
    return direct_to_template(request, 'fb/canvas.fbml',
                              extra_context={'request':request})

The add_fb_instance adds a Facebook instance to the request at request.facebook. Since that is the only thing the PyFacebook middleware does, we've already recreated the majority of its functionality. Still we probably want a little more, like forcing users to log in before accessing a view.

Requiring login

Requiring that users log in to our app is also straightforward.

def require_fb_login(request, next=None):
    if getattr(request, 'facebook', None) is None:
        add_fb_instance(request)
    fb = request.facebook
    if not fb.check_session(request):
        return fb.redirect(fb.get_login_url(next=next))

Using the require_fb_login function works like this:

def canvas(request):
    redirect = require_fb_login(request)
    if redirect is not None: return redirect
    user = get_fb_user(request.facebook)
    return direct_to_template(request, 'fb/canvas.fbml',
                              extra_context={'request':request, 'fbuser': user})

This will redirect the user to the app login page if they have not already logged in. Notice that it will add a Facebook instance to our request if one doesn't already exist. Thus explcitily calling add_fb_instance(request) isn't necessary (but its probably advisable, since it makes it much more explicit that a Facebook instance has been added to the request).

Using the Facebook instance to do something

Okay, so now we have Facebook instances attached to our requests... but what can they do for us? Well, lets look into that. We'll make a model that stores information about our logged in user, and also use the Facebook instance to populate some data in our model.

Lets make our model:

class User(models.Model):
    facebook_id = models.IntegerField()
    name = models.CharField(blank=True, null=True, max_length=60)

Okay, a simple model. Now, lets make a function that takes a Facebook instance and retrieves or creates the User model for it, and populates the name field with data from Facebook.

from django.shortcuts import get_object_or_404

def get_fb_user(facebook):
    user, created = User.objects.get_or_create(facebook_id=int(facebook.uid))
    # if the object is newly created
    if created is True:
        # get first and last name using FQL
        query = "SELECT uid, first_name, last_name FROM user WHERE uid=%s" % facebook.uid
        # FQL results a list of dicts, retrieve the first (and only) one
        results = facebook.fql.query(query)[0]
        user.name = "%s %s" % (results[u'first_name'], results[u'last_name'])
        user.save()
    return user

Here we are using Facebook Query Language to retrieve information. The nice thing about FQL is that you can do very complex queries, and also that this one example will be enough for you to figure out retrieving other data via PyFacebook: just write a new FQL query and call it in the same way.

Calling facebook.fql.query(your_query) returns a list of dictionaries. Since we are calling a query that can only return one instance (a user's user id), we are just grabbing the first result, but in other queries you would want to be more discriminating (most likely iterating over the results).

Now, lets look at using the get_fb_user function:

def my_view(request):
    add_fb_instance(request)
    user = get_fb_user(request.facebook)
    return direct_to_template(request, 'fb/canvas.fbml',
                              extra_context={'request':request, 'fbuser': user})

Notice that we need to call the add_fb_instance on request before we can use the request's facebook instance to get our user (it doesn't exist before we call the add_fb_instance).

Tying it all together.

Okay, now for a slightly more complex example that uses all the things we have written, along with a bit of other stuff sprinkled in.

from django.shortcuts import get_object_or_404

def club(request, id):
    add_fb_instance(request)
    redirect = require_fb_login(request)
    if redirect is not None: return redirect
    user = get_fb_user(request.facebook)
    club = get_object_or_404(Club, pk=int(id))
    if user in club.admins.all():
        is_admin = True
    else:
        is_admin = False
    return direct_to_template(request, 'fb/club.fbml',
                              extra_context={'fbuser':user,
                                             'club':club,
                                             'is_admin':is_admin,
                                             })

So, the view for this view is passing us an 'id' as well. So the urls.py looks something like this:

from django.conf.urls.defaults import *

urlpatterns = patterns('bookface.fb.views',
    (r'^$', 'canvas'),
    (r'^club/(?P<id>\d+)/$', 'club'),
)

Because the regular expression in the URL only allows digits, we can call the int function directly on the id (but its still not a great idea to ignore error handling).

So we add a Facebook instance to the request, then verify that the user is logged into Facebook. If they are not logged in, we redirect them to the login page for our app. Then we retrieve the User instance associated with the logged-in Facebook user. Then we use the id we're being passed to retrieve or create a club.

Then we check if the user is one of the club's admins. We pass a bunch of extra context to the template renderer (the user, club, and if the user is an admin in the club).

And thats all there is to it.

Let me know if you have any questions.


Django Brasil no ar!

Nov 25 2007 [Archived Version] □ Published at PyMan under tags  django

Após grande expectativa, está no ar o site da comunidade Django Brasil (http://www.djangobrasil.org/)! O objetivo é ser um veículo de divulgação deste framework em terras tupiniquins, trazendo informações para auxiliar tanto os iniciantes quanto os mais experientes.

Atualmente o site fornece:

  • as principais características do Django;
  • um weblog para anúncios e avisos à comunidades brasileira;
  • uma página exclusiva para a comunidade descrevendo como um desenvolvedor pode participar;
  • um agregador de blogs (planeta) dos desevolvedores nacionais, e;
  • uma área específica para a documentação.

A documentação e tradução da documentação oficial ainda é escassa. Convidamos os interessados em traduzir ou produzir conteúdo em língua portuguesa para suprir essa necessidade.

Este é apenas o início de uma nova fase da comunidade Django Brasil. Participe!

O site foi desenvolvido pelo Guilherme Mesquita Gondim (semente) e o belo desing pelo Jader Rubini (http://jaderubini.wordpress.com). O código é livre e pode ser acessado no seguinte endereço: http://code.google.com/p/djangobrasil/.


Django Brasil no ar!

Nov 25 2007 [Archived Version] □ Published at PyMan under tags  django

Após grande expectativa, está no ar o site da comunidade Django Brasil! O objetivo é ser um veículo de divulgação deste framework em terras tupiniquins, trazendo informações para auxiliar tanto os iniciantes quanto os mais experientes.

Atualmente o site fornece:

  • as principais características do Django;
  • um weblog para anúncios e avisos à comunidades brasileira;
  • uma página exclusiva para a comunidade descrevendo como um desenvolvedor pode participar;
  • um agregador de blogs (planeta) dos desevolvedores nacionais, e;
  • uma área específica para a documentação.

A documentação e tradução da documentação oficial ainda é escassa. Convidamos os interessados em traduzir ou produzir conteúdo em língua portuguesa para suprir essa necessidade.

Este é apenas o início de uma nova fase da comunidade Django Brasil. Participe!

O site foi desenvolvido pelo Guilherme Mesquita Gondim (semente) e o belo desing pelo Jader Rubini (http://jaderubini.wordpress.com). O código é livre e pode ser acessado no seguinte endereço: http://code.google.com/p/djangobrasil/.


Usando o ZODB no Django

Nov 21 2007 [Archived Version] □ Published at PyMan under tags  django python zodb

Ter a flexibilidade da orientação a objetos do Python em um banco de dados pode ser algo muito útil e interessante que utilizar bancos relacionais. Um dos melhores bancos orientado a objetos em Python é o ZODB , é poderoso e simples de utilizar.

Mas como utilizar ele para web?

A resposta mais óbvia é Zope e Grok . São dois frameworks para web que utilizam ZODB como base de dados!

A resposta não tão óbvia é o Pylons . O Pylons é um framework que trabalha através de camadas (middlwares) onde você uma dessas camadas pode ser a implementação para suporte a ZODB.

E a resposta nada óbvia é o Django. O Django é um framework famoso pela sua coesão, mas isso não quer dizer que ele não pode ser facilmente integrado com outras ferramentas. Uma das provas disso é o tranquil, uma middlware que integra o Django ao SQLAlchemy .

Inspirado pela necessidade de usar o ZODB , por não querer usar Grok , Pylons ou outro e pela implementação do tranquil, implementei o django-zodb, uma middlware que integra o Django ao ZODB.

O endereço do projeto é: http://code.google.com/p/django-zodb/

Para instalar o projeto basta ler a página do wiki do projeto: http://code.google.com/p/django-zodb/wiki/Installation

No projeto tem um projeto em Django demonstrando como usar o django-zodb .

Ou seja usar ZODB com o Django também pode ser uma boa alternativa!


Case-insensitive ordering with Django and PostgreSQL

Nov 20 2007 [Archived Version] □ Published at Flapping Head under tags  django

When the Django Gigs site first went live we noticed the ordering of developers by name was not right. Those starting with an uppercase letter were coming before those starting with a lowercase letter. PostgreSQL and the locale PostgreSQL has a locale setting which is configured when the cluster is created. Among other things, this [...]


WebService with PHP Server and Java Client

Nov 19 2007 [Archived Version] □ Published at zoe.vc under tags  client java jax-ws netbeans php

For my current private project that answers to the nice name "Telrunya" I'm trying to access a PHP web service through a Java client. I once heard in a .NET codecamp that WebServices are super cool and that it doesn't matter in which language they are implemented. But that doesn't apply if you're communication between 2 different technologies. In the Guxx blog I found the reversed case, how to access a java server through a PHP client. There is also a Java bridge developed by Zend that manages the conversion of the objects. But that seems to be an independent server or at least a server modul but that isn't considered by me. (furthermore it costs money)

Anyway, I tried the whole thing withion NetBeans IDE with JAX-WS. My WSDL file (document) written in Eclipse was successfully parsed and the Java files for client etc. were created. The client was also able to access the service but it failed to convert the values. Monitoring the HTTP traffic I can see that the right values are returned, but the Java client returns "null". I already read a lot of documentations and tutorials but I didn't find out anything about converting the received data.
Maybe someone has an advide for me?

Well, the old school alternative works but it is lond winded. Therefore I simply create an URL in Java, set my SOAP function as "RequestProperty" and send an handmade (or even by soapUI created) SOAP envelope. The result is the corresponding envelope returned that now has to be stripped and the relevant data has to be extracted. It could be so simply with JAX-WS but isn't....


WebService with PHP Server and Java Client

Nov 19 2007 [Archived Version] □ Published at zoe.vc under tags  client java jax-ws netbeans php

For my current private project that answers to the nice name "Telrunya" I'm trying to access a PHP web service through a Java client. I once heard in a .NET codecamp that WebServices are super cool and that it doesn't matter in which language they are implemented. But that doesn't apply if you're communication between 2 different technologies. In the Guxx blog I found the reversed case, how to access a java server through a PHP client. There is also a Java bridge developed by Zend that manages the conversion of the objects. But that seems to be an independent server or at least a server modul but that isn't considered by me. (furthermore it costs money)

Anyway, I tried the whole thing withion NetBeans IDE with JAX-WS. My WSDL file (document) written in Eclipse was successfully parsed and the Java files for client etc. were created. The client was also able to access the service but it failed to convert the values. Monitoring the HTTP traffic I can see that the right values are returned, but the Java client returns "null". I already read a lot of documentations and tutorials but I didn't find out anything about converting the received data.
Maybe someone has an advide for me?

Well, the old school alternative works but it is lond winded. Therefore I simply create an URL in Java, set my SOAP function as "RequestProperty" and send an handmade (or even by soapUI created) SOAP envelope. The result is the corresponding envelope returned that now has to be stripped and the relevant data has to be extracted. It could be so simply with JAX-WS but isn't....


Leopard Calendar Store

Nov 15 2007 [Archived Version] □ Published at orestis.gr

So, over at the Achaia there was a request for a little to-do application, so I've stepped up to the challenge...

This was an excellent opportunity to create a real application that will see some use. The results so far are really encouraging:

Picture 2

Bindings are really cool, but I think they need to be a bit more extensible. I have to study the documentation a bit closer. Also custom drawing (via subclassing) in Cocoa is a bit hit-or-miss. Maybe I haven't found the documentation for that, either, but I had to get a piece of code from iLifeControls to see how it all works.

I have no idea why NSPopUpCell will not display either an attributedString or an image when in a table view. Weird.

Calendar store seems a bit beta and rough about the edges, but it does what it says...

So stay tuned for the imminent release of TodoLing beta...

Some Arsers mentioned donating, so I thought, what the heck, if anyone wants to send me money:

UPDATE: Get the beta version here!

Comments


Stockphoto 0.2.1 released

Nov 13 2007 [Archived Version] □ Published at Prosthetic Conscience

I've just released stockphoto 0.2.1. This is a bugfix release and contains no new features relative to 0.2. I would ...


Segundo Django Sprint dia 1 de Dezembro

Nov 12 2007 [Archived Version] □ Published at PyMan under tags  django

Sprint é um evento onde os desenvolvedores, colaboradores e simpatizantes com alguma ferramenta opensource se unem para implementar novidades de arrumar erros existentes nessa ferramenta. E dia 1 de Dezembro ocorrerá o segundo Django Sprint desse ano.

Para participar do Sprint, inscreva-se na página do wiki do Django e no dia do Sprint fique atento no canal #django-sprint, onde todos os envolvidos com o Sprint e os responsáveis pelo código do Django estarão para auxiliar e tirar dúvidas.

Se você ainda não conhece o processo de criação de tickets, envio de path's utilizado no desenvolvimento do Django, leia esse artigo: Contributing to Django.


Programmatic HTML generation

Oct 29 2007 [Archived Version] □ Published at Occasionally sane under tags  django

The recent release of Seaside 2.8 got me thinking about one particular feature of Seaside that is less often mentioned than its continuation-based flow: programmatic HTML generation. As far as I know, Seaside is one of the only frameworks to … Continue reading


Programmatic HTML generation

Oct 29 2007 [Archived Version] □ Published at Occasionally sane under tags  django

The recent release of Seaside 2.8 got me thinking about one particular feature of Seaside that is less often mentioned than its continuation-based flow: programmatic HTML generation. As far as I know, Seaside is one of the only frameworks to … Continue reading


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 adsense advanced ajax amazon angular angularjs apache api app appengine app engine apple application security aprendiendo python architecture argentina articles asides audrey australia authentication automation backup bash basics best practices binary bitbucket blog blog action day blogging book books buildout business c++ cache capoeira celery celerycam celerycrawler challenges chat cheatsheet cherokee choices christianity class-based-views cliff clojure cloud cms code codeship codeship news coding command community computer computers computing configuration consumernotebook consumer-notebook continuous deployment continuous integration cookiecutter couchdb coverage css custom d data database databases db debian debugging deploy deployment deployment academy design developers development devops digitalocean django django1.7 django admin django cms djangocon django framework django-nose django-readonly-site django-rest-framework django-tagging django templates django-twisted-chat django web framework tutorials documentation dojango dojo dotcloud dreamhost dughh easy_install eclipse education elasticsearch email encoding english error europe eventbrite events expressjs extensions fabric facebook family fashiolista fedora field file filter fix flash flask foreman form forms frameworks friends fun functional reactive programming gae gallery games geek general gentoo gis git github gmail gnome google google app engine guides gunicorn hack hackathon hacking hamburg haskell heroku holidays hosting howto how-to howtos how-tos html http i18n image imaging indifex install installation intermediate internet ios iphone java javascript jinja2 jobs journalism jquery json justmigrated kde la latex linear regression linkedin linode linux login mac machine learning mac os x markdown math memcached meme mercurial meta meteor migration mirror misc model models mod_wsgi mongodb months mozilla multi-language mvc mysql nasa nelenschuurmans newforms news nginx nodejs nosql oauth ogólne openshift opensource open source open-source openstack operations orm osx os x ottawa paas packages packaging patterns pedantics pelican penetration test performance personal personal and misc philippines philosophy php pi pil pinax pip piston planet plone plugin pony postgis postgres postgresql ppoftw presentation private programmieren programming programming &amp; internet project projects pycharm pycon pycon-2013-guide pydiversity pygrunn pyladies pypi pypy pyramid python python3 queryset quick tips quora rabbitmq rails rant ratnadeep debnath reactjs recipe redis refactor release request resolutions rest reusable app review rhel rtnpro ruby ruby on rails scala scaling science screencast script scripting security server setup shell simple smiley snaking software software collections software development south sphinx sprint sql ssh ssl static storage supervisor support svn sysadmin tag tag cloud talk nerdy to me tastypie tdd techblog technical technology template templates template tags test testing tests tip tips tools tornado training transifex travel travel tips for geeks tumbles tutorial tutorials twisted twitter twoscoops typo3 ubuntu uncategorized unicode unittest unix use user authentication usergroup uwsgi uxebu virtualenv virtualenvwrapper web web 2.0 web application web applications web design &amp; development webdev web development webfaction web framework websockets whoosh windows wordpress work workshop wsgi yada znc zope