Xam

We have a new puppy. A male dog, this time, called Xam. My dad is still thinking about another name, because he doesn't like Xam. They found the dog yesterday in Germany and directly bought it and took it home. He's 14 weeks old and already very large. Although very disproportioned, at the moment, because he was fed puppy-pallets (which usually contain way too much stuff). We hope he'll look better in a few weeks.

I'm not happy with the whole thing. I mean, it's a really cute dog, all puppies are cute. But I think my dad and Hanneke don't spend enough time on the dogs we already have. They even say so themselves. But still, they wanted a male dog (the first one here, although there are already 6 dogs), so they bought the first they came across. Not amused. But he's cute, though.

14 Years for murder, mixed feelings

Last year the son of friends of my father shot someone. The guy he shot died. Yesterday, the Justice Department (OM in The Netherlands) demanded 14 years imprisonment for the crime. Reasonable, I think, if this is all the information you have. It becomes more difficult if you know some details.

The shooter rented a house to the guy, in which the guy demolished almost all the walls for growing weed. You know, the drug kind. Instead of popular believe, growing weed is illegal in The Netherlands. The shooter, however, knew they were doing this and he was even payed extra for it. But when they started to demolish his house, he wanted them out. They wouldn't and started threatening him. He hid for several weeks, but then they found him and beat him up with honkbal bats. No joke. Honkbal bats. He went back to his place, got a gun and shot the guy.

Even though he's far from innocent, I do feel kind of sorry for him. His life was ruined because he thought he could make a little bit more money. He couldn't live a normal life anymore. His only way back to normality, he thought, was shooting the guy.

What else to do? And since the dead guy was a foreigner, he can expect retaliation when he leaves prison again. I think it's sad. And because of these things, people tend to dislike foreigners. I myself don't care about foreigners one way or the other, as long as they're honest. But I can understand the sentiment.

14 Years in prison for trying to bring you life back to normal. Maybe he deserved it. Maybe not. I'm having mixed feelings about it.

Criminalization of paying customers

Last week I traveled with the Dutch Railways (NS). I have a discount card, so can get 40% discount when I travel after peak hours. For simplicity, NS says the card is valid from 9am each day. Quite preoccupied, I bought my ticket with a discount and got in the train. While I was searching for a seat, the conductor used the intercom to tell everybody that it was still 8.45am and discount cards weren't valid. Ah well, I went in search of the conductor, to give myself in. I didn't watch the time, so I was in the wrong. No trouble with that, I should've payed attention.

The conductor took some of my personalia and told me I could expect a ticket the next week. And a €35,- fine. I was a bit surprised by this, because I always thought fines were for criminals, who explicitly went about doing wrong things. I actually thought that, because I contacted the conducter myself, they would waive the fine. No such thing.

Today I got the payment slip and I went in search for some more info. Apparantly, they waive the first fine you ever get. So I called them on a 0900 number for €0,10 and after some stalling (hey, they have to make money from the call, don't they?) I got to talk to a girl who told me that indeed she could waive the fine if it was my first 'offence'. So she proceeded asking me some personalia again and she could indeed see that it was my first offence. So now I could consider the slip I had before me as 'not-sent' and she would sent me a new one.

I have to gripes with this. The first one is, if she could look up that this was my first offence, why didn't they do that before sending the check? Why did I have to call them myself to get the fine waived? Is that NS's way of giving service to their customers? I'm appalled that I had to call an 0900 number just to get the fine waived.

The second thing is, since when is it proper to treat you paying customers (I had bought a ticket!) like they were petty crooks? If I go up to the conductor myself to tell him I don't have a ticket because I didn't look at the time properly, why am I all of a sudden fined? This is rediculous. I'm no criminal and I sure as hell don't like being fined for being honest. The next time I won't go to the conductor.

It's a trend. More and more companies are criminilazing the normal, human behaviour of their customers. The record companies started the trend, by going after the 'pirates'. Even though their sales are up, they don't care, they just have to make criminals of their customers. The NS is going on the same path, by criminalizing the normal, human behaviour of forgetfulness. Or being preoccupied.

As a normal consumer, depending on the NS for my transportation, I have no choice but to allow them to do that. It way too expensive to drive the distances I need to travel. And they're the only transportation that uses trains here to get me there. Nothing we can do about their tendency to criminalize their customers.

But I still think it's just plain wrong. I hope someone will figure out a way to stop them. If they need help, let me know.

Authentication in Django

Ok, as I told yesterday, I'm working on a forum, to get to know Django a bit better (when darcs from DarwinPorts actually builds on my Mac OS X.4, I'll make a repository available). Today, I set myself two goals:

  1. Be able to start new threads and post to ongoing threads
  2. Authenticate users and only allow authenticated user to do the previous.

Of course I could much more, but I want to take it slow (so I can play WoW again with my girlfriend tonight :P). In this post I try to explain how I'm building it.

First of all, I'll be using the built-in authentication mechanism from Django. That's just convenient. I check in the view (forum/views/forum.py) if the user is authenticated or not. If not, I present the user with a login functionality. If so, the user can post a message to the thread.

At the present, the view is defined like so:

from django.models.forum import forums, threads

from django.core.extensions import render_to_response, get_object_or_404



[... other methods ...]



def show(request, thread_id):
        t = get_object_or_404(threads, pk=thread_id)
        return render_to_response('forum/forums_thread', {'thread': t})

The template is a simple thing too, like so:

<h1>{{ thread.subject }}</h1>



{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}



<table>

{% for post in thread.get_post_list %}

<tr><td>Author:</td><td>{{ post.get_user.username }}</td>
    <td>Date posted:</td><td>{{ post.date }}</td></tr>
<tr><td colspan="4">{{ post.content }}</td></tr>

{% endfor %}

</table>

To make sure the view has access to the methods defined in Django for authentication, I import users. And I also need to make sure I have access to the user in the template. First, I change the imports into:

from django.models.forum import forums, threads

from django.core.extensions import render_to_response, get_object_or_404, DjangoContext

from django.models.auth import users

DjangoContext is an easy way to transmit user info to the template. I need to change a little code, just one line:

def show(request, thread_id):
        t = get_object_or_404(threads, pk=thread_id)
        return render_to_response('forum/forums_thread', {
                'thread': t,
         }, context_instance=DjangoContext(request))

Now I have access to the user information that's stored in the cookie, if the user is logged in. Yes, Django stores it's login information in a cookie, via a session. But more on that later. First, we need to change the template, so it displays a login form instead of the username, if the user isn't logged in. This is quite simpl, really. Look at this:

<p>Post a new message here:</p>



{% if user.is_anonymous %}

<form action="/mylogin/" method="post">

<input type="hidden" name="redirect" value="/post/{{ thread.id }}">

<table><tr><td>Username:</td><td><input type="text" name="name"></td></tr>

<tr><td>Password:</td><td><input type="password" name="password"></td></tr>

<tr><td colspan="2"><textarea name="content" cols="10" rows="5"></textarea></td></tr>

</table>

</form>

{% else %}

<form action="/post/{{ thread.id }}" method="post">

<table><tr><td>Username:</td><td>{{ user.username }}</td></tr>

<tr><td colspan="2"><textarea name="content" cols="50" rows="10"></textarea></td></tr>

</table>

</form>

{% endif %}

Quite simple, huh? I thought so, too. As you can see, the form for the anonymous user is POSTed to /mylogin/ and the form for the authenticated user is POSTed to /post/thread_id/. But I tell the script at /mylogin/ that it needs to redirect to /post/thread_id/ with a hidden input field.

As I said before, authentication is stored in a session. Although I would have prefered http authentication, this isn't really a bad thing. Maybe I'll hack http authentication in it, once I'm better versed at Python ;-) For now, what we need to do at /mylogin/ is simple, check if the information entered is valid and set the cookie, if it is valid.

For that, we make a new view. I've called it simply login. It looks like this, but it's far from finished.

def login(request):
        username = request.POST['name']
        password = request.POST['password']
        user_cache = users.get_object(username__exact=username)
        if user_cache is not None and not user_cache.check_password(password):
                user_cache = None
        request.session[users.SESSION_KEY] = user_cache.id
        return HttpResponseRedirect(request.POST['redirect'])

As I said, it's far from finished, because it doesn't do any error-handling yet. I'll built that later, when I'm done sleeping or something ;-) This simply creates a User object, based on the username that was entered, checks the password that was entered, and if that wasn't correct, destroy the object. If it was correct, we set the session cookie with the session id. All done. We redirect to the URL that was given in the hidden form element.

There are a few problems still with the code above, but this will get you along, I hope. Error-handling is one thing that needs to be added. But cookie-testing needs to be done, too. Another problem is that the POSTed data isn't forwarded with the HttpResponseRedirect (it isn't supposed to be, if you want to follow rfc's), so we need to store the "content" textarea in the session and read it in the posting code. Take a look at the source references below for a better idea on how to write proper verifications. I might fix it all tomorrow and post it here :)

How I got so smart

Well, actually, I'm not. I had a lot of help from #django on irc.freenode.org and especially the following documents:

Fixed the comments!

Well now, it seemed the Permalinks just don't work well. Don't know why, but I've disabled them and now the URLs may look a bit crappy, but comments work again :) So leave me a comment, just to test ;-)

Django is really nice

Ok, I'm diving into Django. I'm gonna start by making a (simple) forum for our World of Warcraft guild, The Undutchables (nothing much to see there at this moment). Since we got about 50+ members in the guild, I think it would be interesting to see how Django holds up on such a low traffic site.

After starting simply (just following the same tutorial as I did the day before yesterday, only adapting it to the new programme), I've seen the first real problem. The tutorial has only one level nesting (a Poll-object which contains Choices), but my application has one more level (a Forum contains a Thread which contains Posts) and the admin interface (which Django creates almost automagically) doesn't seem to handle this correctly. My objects are defined as follows:

class Forum(meta.Model):
        title = meta.CharField(maxlength=200)
        moderators = meta.ManyToManyField(auth.User)
    def __repr__(self):
            return self.title

    class META:
            admin = meta.Admin()

class Thread(meta.Model): forum = meta.ForeignKey(Forum, edit_inline=meta.STACKED) subject = meta.CharField(maxlength=200)

    def __repr__(self):
            return self.subject

class Post(meta.Model): thread = meta.ForeignKey(Thread, edit_inline=meta.STACKED) user = meta.ForeignKey(auth.User) date = meta.DateTimeField(auto_now_add=True) content = meta.TextField()

    def __repr__(self):
            return self.content</pre>

Threads are displayed correctly, but the Posts are nowhere to be found. I posted a message describing my problem to the mailinglist. I hope they'll respond quickly :)

Fallen in love again...

... with programming (if you expected "my girlfriend" here, I've never stopped loving her). I've been going through the Django tutorial and I'm really liking doing webdevelopment again. Yes, I know, I was working with Ruby on Rails earlier, but Django is much more to my liking. I have to quit now with the tutorial because I'm sleepy, but I will continue with it tomorrow. Take a look at it... It's really, really cool.

On a sidenote, maybe if Ruby on Rails had such a cool tutorial, I would fall for that language, but I'm struggling much more with Ruby on Rails. And I already know (some) Python, which makes Django much easier for me. What are you waiting for?? Go take a look!