tkbe

October 30, 2006

Django :: Custom Field

Filed under: django — tb @ 4:00 am

If you don't do so allready, you should really get into the habit of looking at the validation errors your users are seeing. If you're using the idiomatic Django view code this is very easy to do. After you get a POST request, you'll verify the data:

PYTHON:
  1. # validate the form
  2. errors = sform.get_validation_errors(new_data)

then all you have to do is write the errors dict to a log (a database table with a big message field works as well as most other approaches). When I did this with a project where I'd used the date widget from a couple of posts ago, users were having big problems entering dates correctly (it was a quick project for users that shouldn't have any problems entering dates in new formats, but still there were problems).

The first solution I wanted to try was to allow users to enter dates in their normal format. Normal for these users meant that October 30th, 2006 would either be entered as 30.10.2006 or 30/10/2006. You can do this in Django by simply writing a validator for this pattern, but since I knew I needed to write custom field classes for other things I wanted to do, this is the solution I ended up with:

PYTHON:
  1. import re, datetime
  2. from django import forms
  3. from django.core import validators
  4.  
  5. class BirthDay(forms.FormField):
  6.     @staticmethod
  7.     def validator(field_data, all_data):
  8.         # split on . or / and expect to get three items
  9.         fields = re.split(r'[\./]', field_data)
  10.         if len(fields) != 3:
  11.             errtxt = "Date must look like: 30.10.2006."
  12.             raise validators.ValidationError(errtxt)
  13.         try:
  14.             fields = map(int, fields)
  15.         except ValueError:
  16.             raise validators.ValidationError("Only use numerical date parts.")
  17.         try:
  18.             d = datetime.date(fields[2], fields[1], fields[0])
  19.         except ValueError:
  20.             raise validators.ValidationError("Invalid date.")
  21.         today = datetime.date.today()
  22.         if d> today:
  23.             raise validators.ValidationError("You're not born yet.")
  24.         if today.year - d.year> 120:
  25.             errtxt = "Invalid date: did you enter a 4 digit year?"
  26.             raise validators.ValidationError(errtxt)
  27.         return True
  28.    
  29.     @staticmethod
  30.     def html2python(data):
  31.         # data is allready validated
  32.         fields = map(int, re.split(r'[\./]', data))
  33.         return datetime.date(fields[2], fields[1], fields[0])       
  34.    
  35.     def __init__(self, field_name='', is_required=False):
  36.         self.field_name = field_name
  37.         self.validator_list = [BirthDay.validator]
  38.         self.is_required = is_required
  39.        
  40.     def render(self, data):
  41.         widget = '<input id="%s" name="%s" type=text maxlength=10 size=10 value="%s" />'
  42.         return widget % (self.get_id(), self.field_name, forms.escape(data))

If you look closely it's all almost trivial code. I try to be as thorough as possible in the validator so that I can issue relevant error messages. The html2python method relies on validation allready being done, which it would be when following the standard Django idiom (you cannot in general call the validator from here since the validator might need other fields in order to validate...) The magic happens in the render method, by making sure you provide the correct id and name for the input tag, and feed it data that the user has allready provided.

After this is done, you can use the new class in the custom manipulator we wrote last time:

PYTHON:
  1. class SuggestionForm(forms.Manipulator):
  2.     def __init__(self):
  3.         self.fields = [
  4.             forms.TextField('name', is_required=True),
  5.             BirthDay('dob', is_required=False),
  6.             forms.LargeTextField('comment', is_required=True),
  7.             ]

This was perhaps not the best documented corner of Django, but the code in the forms module was exceedingly easy to follow.

October 25, 2006

My first IE7 bug!

Filed under: it — tb @ 11:55 pm

Well I didn't think I'd find it this fast, but here it is... The following code does not work with IE7:

HTML:
  1. <button onclick="window.open('http://www.google.com', 'Google Homepage')">Goto Google</button>

While the following code does work:

HTML:
  1. <button onclick="window.open('http://www.google.com', 'GoogleHomepage')">Goto Google</button>

The difference: there's a space between Google and Homepage in the first version.

Besides that, it sure is pretty to look at (and it opens links in a background tab when you click with the middle mouse button, just like Easy Gestures for FF!)

October 23, 2006

Django :: setup for the form examples

Filed under: django — tb @ 3:10 am

I normally develop on Windows and deploy on Linux which has so far worked without a single problem. On my development system I followed the effbot receipe to get Django up and running in five minutes. That receipe was written before Django 0.95 was released, and we're not going to be using the database, so you can simplify the process by downloading and installing version 0.95 from the Django download page. I'm using Python 2.4.3, but this should work with Python 2.5 also. The Django 0.95 release doesn't support the sqlite3 that comes bundled with Python 2.5, so you'd have to use the current development version until a new official release is cut if this is going to be important to you (as I was saying, for these examples it won't be).

I did use the effbot's exemakerutility to turn django-admin.py into an executable. Exemaker is very cute, I highly recommend it. If you elect to not use exemaker, you'll have to change the first line below to something that will find django-admin.py (it will normally be in c:\python24\scripts).

c:> django-admin startproject mysite
c:> cd mysite
c:mysite> mkdir templates
c:mysite> python manage.py startapp myapp

Change settings.py, adding "/mysite/templates" to TEMPLATE_DIRS, and adding "mysite.myapp" to INSTALLED_APPS. Then start the server:

c:mysite> python manage.py runserver

That's it, click here to check that it worked -- you should be seeing a page that says "It worked!" ;-)

Next Page »

Powered by WordPress