What's new in Django community blogs?

Python's getattr

Dec 10 2007 [Archived Version] □ Published at orestis.gr

An interesting question

So, yesterday I was asked an interesting question:

Int: Are you familiar with Python's getattr?
Me: Um, yes?
Interviewer clarifies what it's all about
Int: So I now want you to implement __getattr__ in such a way that when a method is called with the prefix print it'll print it's name before calling it.

Before that, we had a discussion about decorators, where I created a decorator called printme:

def printme(func):
    def decorated(*args, **kwargs):
        print func.func_name #well, it was "print func", but now I know better :)
        return func(*args,**kwargs)
    return decorated

So after a bit of confusion (writing code like that in a piece of paper with two pairs of eyes looking at you can be tricky), I ended up with something that may have looked like this:

class Foo(object):
    def __getattr__(self, name):
        if name.startswith('print_'):
            f = getattr(self, name[6:]) #actually, I did super(Foo, self) but that won't work.
            return printme(f)
        return getattr(self, name)

    def hi(self):
        print "Hello!"

Can you spot the error?

.
.
.

Well, we didn't have access to a console, so we didn't. If you're lazy and didn't pasted the above into a console, here are the results:

>>> f = Foo()
>>> f.hi()
Hello!
>>> 
>>> f.print_hi()
hi
Hello!

So far, so good. But:

>>> f.blah()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 6, in __getattr__
  File "<stdin>", line 6, in __getattr__
  .
  .
  .
  File "<stdin>", line 6, in __getattr__
  File "<stdin>", line 6, in __getattr__
  File "<stdin>", line 6, in __getattr__
RuntimeError: maximum recursion depth exceeded

Seeing that this was a topic I wasn't sure about, I came home and searched about it, which resulted in this blog post. So, after this quite big introduction, a small tutorial on getattr

getattr, __getattr__ and __getattribute__

So, what is this all about? Python, as you may know is a Strong & Dynamic typed language. We don't care about Strong here, but the Dynamic part is very interesting. Dynamic languages don't care at the compile time the nature of the constructs that are used - they do all the work at run time. So, for example, when you call method "bar" on instance "foo" (foo.bar()), the compiler won't check if there is a void method named bar in the class tree of foo.

So, how does it work? There has to be support for looking up methods at runtime. This is what Python uses getattr, __getattr__ and __getattribute__ for. (Objective-C has similar support, though I haven't got that far yet. I don't know about other languages.)

In essence, consider the following snippet:

>>> class Foo(object):
...     def hi(self):
...             print 'Hello there!'
... 
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'Foo': <class __main__.Foo at 0x54c30>, '__doc__': None}

What is interesting here is the entry Foo at the globals dictionary. Let's keep that and move on.

>>> f = Foo()
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'Foo': <class __main__.Foo at 0x54c30>, '__doc__': None, 'f': <__main__.Foo instance at 0x60ad0>}

Again, f is mapped to a Foo instance, in the globals dictionary.

>>> dir(f)
['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'hi']

What's this? Our instance has an attribute named hi. How can we access this?

>>>f.hi
<bound method Foo.hi of <__main__.Foo instance at 0x60ad0>>
>>> f.hi()
Hello there!

Nice, but boring. This isn't using any features of dynamic typing. Let's see what getattr can do.

getattr

>>> getattr(f, 'hi')
<bound method Foo.hi of <__main__.Foo instance at 0x60ad0>>

That looks familiar, but now we're using a string to specify what method we want.

>>> getattr(f, 'hi')()
Hello there!

Nice, right? But this isn't getting us any closer to the answer of the above question, so let's proceed. What we want to do is somehow intercept the built-in atrribute lookup (methods are, after all, attributes) and do some work of our own.

__getattribute__

This is where it gets a bit complicated. You may have noticed a method called __getattribute__ in the results of dir, above. So at first glance you'd want to implement this, but wait! Let's check what the reference has to say:

__getattribute__( self, name)
Called unconditionally to implement attribute accesses for instances of the class. If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError. This method should return the (computed) attribute value or raise an AttributeError exception. In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, "object.__getattribute__(self, name)".

That is, if you implement this method, you are overriding the built-in attribute access mechanism! So tread carefully, and notice that you can easily trigger infinite recursion. Also, this method is only available with new-style classes.

__getattr__

Back to where we started. Let's see what the reference is saying about __getattr__:

__getattr__( self, name)
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.

Note that if the attribute is found through the normal mechanism, __getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__().) This is done both for efficiency reasons and because otherwise __setattr__() would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__() method below for a way to actually get total control in new-style classes.

So __getattr__ will be called only when an attribute is not found. This gives us a hint to why we got infinite recursion in the first example. Let's revisit that:

def __getattr__(self, name):
    if name.startswith('print_'):
        f = getattr(self, name[6:]) #actually, I did super(Foo, self) but that won't work.
        return printme(f)
    return getattr(self, name)

This will be called if there is an attribute lookup that fails. In the given specifications, we suppose that someone will try to call print_whatever. However, if something doesn't start with print_, I tried to get the normal behaviour, which is to raise an AttributeError calling it again. I should've either raised AttributeError manually, or used the default implementation, like this:

return object.__getattribute__(self, name)

Of course, this works only for new-style classes, but if you're implementing this for something new, why go old-style?

Final Thoughts

This probably came out more boring than I thought, though it was a good clarification for me. The uses for a technique like this are endless. The first that comes to mind is a Rails-like Active Record class that is empty but responds to messages that correspond to table columns. Another is a proxy object, that routes certain methods over to the real thing, perhaps with cacheing of results and so on.

Comments


Deutschsprachige Django-Community

Dec 09 2007 [Archived Version] □ Published at zerokspot.com

Seit gestern gibt es auf django-de.org eine neue deutschsprachige Community-Seite für Django. Hauptziel ist derzeit die Übersetzung der Dokumentation ähnlich den Initiativen, die es bereits für die spanische und französische Community gibt. Wer mithelfen möchte, findet unter http://www.django-de.org/participate/ genauere Information. Ein herzliches Dankeschön an Jannis Leidel und alle anderen, die mitgeholfen haben, django-de ins Leben zu rufen :D


Getting Involved in the Django Project

Dec 07 2007 [Archived Version] □ Published at Eric Florenzano's Blog

Getting Involved in the Django Project

Dec 07, 2007

Getting involved in an open source project is hard. I've been working with Django since early 2006 and doing so makes me want to contribute back to the project. Several times now I've made attempts to do so, but all of them have failed. Looking back it's easy to see where I, as a newcomer to the open source world, went wrong. So let's start out with what not to do, by example. After that I'll give some suggestions on what I think is the right way to go about it.

Don't do these things:

  1. Don't bite off more than you can chew

    As a newcomer to both Python and Django, and to web development in general, one thing that I constantly needed to do was redefine my models slightly differently as I discovered flaws in my previous designs. Django does not make this easy, as you either have to manually migrate your SQL or you have to delete all of your data. Can you see where this is going? I thought, "Perfect, there's a way that I can contribute! I'll add schema evolution to Django!" This was a huge undertaking, it took a long time, and it eventually flopped because I simply didn't have the experience at that point necessary to accomplish this task.

  2. Don't work privately

    The other problem with tackling schema evolution was that I had contacted off-list another person who was enthusiastic about the idea, Victor NG. Now he's a great coder and did a lot of the work on that project, but he wasn't an established member of the community. We had set up a completely separate SVN (actually SVK) server and everything. The result was that we were completely shut off from the rest of the development community, and didn't get any input or insight from the people who were really running the project.

  3. As a newcomer, don't work on a community project

    The next thing that I tried working on was a "DjangoForge" project. I worked very hard for almost a month--the month leading up to PyCon--and got a complete prototype up and running. When I got to PyCon, I expected to make a big debut and everyone would instantly love it and jump on board. Wow, was that naive! Nobody knew who I was, and everyone had different ideas about what a community portal, a "DjangoForge", would need to be.

Do these things:

  1. Start without code

    Start on documentation. There are two reasons that this is a good idea. Firstly, those people who are checking in tickets will start to recognize your name and will see that you're interested in contributing. Secondly, you will begin to understand more and more about the internals of the framework as you see how people are adding features. I think that writing documentation is boring, but what's more boring is not knowing the codebase well enough and writing bad code as a result.

  2. Then do test code

    After documenting some of the underpinnings of the framework, you now have a pretty good idea about how it should behave. Now make sure it really behaves that way. Many people don't follow the Code Unit Test First or test-driven development methodology. That is, they write the code and submit the patch without formal tests. These patches usually can't be checked in to Django without test code, as a lot of people rely on the trunk to be stable. So you can do a lot of good by simply writing test code, and it will earn you a lot of gratitude from the folks trying to get things done. On top of being helpful and earning gratitude, it helps you to learn the framework that much better.

  3. Work on already-submitted tickets.

    It can be tempting to work on a pet-feature that you would like to have done. This is not a bad temptation, but for someone who is trying to become part of the community, it may be better to look at what the core developers are anxious to have done. This is where I am currently, working on tickets that the core devs have filed. They have good reasons for filing these tickets: they have deemed it a wanted feature or bug, they have determined that it's a solvable idea or problem, and they may even have insights in how to implement it! These tickets can range from small-scale to large-scale and now is when you can really start to show your stuff as a programmer.

  4. Read django-dev mailing list

    There's nothing more helpful than tracking where Django is going and what ideas are being kicked around on that list. Many times an idea has been brought up, fleshed out, and eventually decided upon. This doesn't always happen in the ticket tracker, and it shouldn't. Whenever discussion is needed, the mailing lists are a much better forum for that discussion to take place. This is also a good place to ping the community on tickets that you're interested in or have worked on. Just please don't spam the list, as nothing will make people more upset at you than a whiny spammer on the django-dev mailing list.

...and beyond

After that, I have no more solid advice. That's where I'm sitting right now with the Django project. I'd like to become even more involved. I'll probably will go about doing so by starting to propose new features, jumping in on the conversation, attending PyCon 2008, and working on spinoff projects like the excellent django-evolution. But I'd like to know how other people have done it. How have you gotten involved in the Django project?

Update: Adrian Holovaty makes a great point in mentioning to check out the Contributing to Django section of the Django documentation, with gives a plethora of guidelines and tips for doing just that. Thanks, Adrian!

Posted in:


Fun Way of Displaying a Blogroll

Dec 06 2007 [Archived Version] □ Published at Eric Florenzano's Blog

Fun Way of Displaying a Blogroll

Dec 06, 2007

I've just relaunched the redesign of this site. Nothing major is different, but one fun thing that I'd like to highlight is the way the blogroll is displayed. I got the idea from Motel de Moka, which has more links to work with than I do.

First, I set up a very simple model:

class BlogRollLink(models.Model):
    name = models.CharField(max_length=128)
    date_added = models.DateTimeField(default=datetime.now)
    url = models.URLField()

But we've got a problem at this point: we can't order this by the number of characters in the name. So we must modify our model to have an integer called name_size, and then override BlogRollLink's save function to fill in that field any time the model is saved. Our final model is below:

class BlogRollLink(models.Model):
    name = models.CharField(max_length=128)
    name_size = models.IntegerField(editable=False)
    date_added = models.DateTimeField(default=datetime.now)
    url = models.URLField()

    def save(self):
        self.name_size = len(self.name)
        super(BlogRollLink, self).save()

    def __unicode__(self):
        return self.name

    def get_absolute_url(self):
        return self.url

    class Admin:
        pass

Now to display this on every page, I've created a context processor named blogroll_processor. It looks like this:

def blogroll_processor(request):
    blogrolls = cache.get('blogrolls', None)
    if blogrolls == None:
        blogrolls = list(BlogRollLink.objects.all().order_by('name_size'))
        cache.set('blogrolls', blogrolls)
    return {'blogrolls' : blogrolls}

And we're done! A nifty "waterfall" of blogs. Let me know what you think about this technique. Is it stupid?


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


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 aurelia australia authentication automation backup bash basics best practices big data 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 goals google google app engine guides gunicorn hack hackathon hacking hamburg haskell heroku holidays hosting howto how-to howtos how-tos html http i18n igalia 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 vagrant validation 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 year-end-review znc zope