Google Search

Google
 

Thursday, July 26, 2007

Django Newforms and Django Check Constraints

Last week, my friend had an interesting comment to make on my project. He felt it was academic and hardly had any utility. This got me thinking and I wondered if I could use some of Django's cool features and get my project to work with them effortlessly. Then I thought of writing a small test project to show how Newforms (one of the coolest features of Django) and Django Check Constraints worked together.
So here is a small tutorial to use Django Newforms and Django Check Constraints together.
I will run through most of the mundane part (that is often on the Django Tutorial but usually neglected by many people). After downloading and patching Django with Django Check Constraints, start a django project. Then create a django app and edit the models.py file in the app. Here is my model:
from django.db import models
from check_constraints import Check
from datetime import date

# Create your models here.

class Manufacturer(models.Model):
mfg_name = models.CharField(maxlength=50)
car_sale_start = models.DateField()
car_sale_end = models.DateField()
quantity_sold = models.IntegerField()
car_price = models.IntegerField()

class Meta:
constraints = (
("check_name",Check(mfg_name__like = 'Merc%')),
("check_date",Check(car_sale_start__between = [date(2007,1,1),date(2008,1,1)])),
("check_end_date",Check(car_sale_end__gte = 'car_sale_start')),
("check_quantity",Check(quantity_sold__gte = 0)),
("check_price",Check(car_price__between = [1000,10000])),
)

Now a brief explanation of the Check fields:
"check_name" expects that the mfg_name field follow the Merc* regexp ('*' matches 0 or more characters while '+' matches only one character).
"check_date" expects a value between the dates 1st January 2007 and 1st January 2008.
"check_end_date" expects that the end_date is always greater than the start_date (Wouldn't it be weird if the sale end date was less than the sale start date).
"check_quantity" expects that the quantity be a PositiveInteger (you might use the Positive Integer field if you are using Postgresql. Django provides the PositiveIntegerField by default).
"check_price" expects that the price of the car be between 1000 and 10000.

Now to check the SQL output if everything is fine....and then syncdb.

(Observe how the '*' in the regexp got converted into a Postgresql regexp.)
After syncing the database, edit the views.py in the apps folder.
Create two functions (say index and data). My view below:

def index(request):
ManufacturerForm = form_for_model(Manufacturer)
f = ManufacturerForm()
f.as_table()
return HttpResponse("<form action=\"/data/\" method=\"POST\" ><table>"+str(f)+"</table><br><input type=\"submit\" value=\"Submit\"/>
</form>")

from django.http import HttpResponse
from letsee.models import Manufacturer
from django.newforms import form_for_model
from datetime import datetime,date

def index(request):
ManufacturerForm = form_for_model(Manufacturer)
f = ManufacturerForm()
f.as_table()
return HttpResponse("<form action=\"/data/\" method=\"POST\" ><table>"+str(f)+"</table><br><input type=\"submit\" value=\"Submit\"/$

def data(request):
if request.POST:
ManufacturerForm = form_for_model(Manufacturer)
f = ManufacturerForm(request.POST)
f.full_clean()
if f.is_valid():
start_date_type = datetime.strptime(request.POST['car_sale_start'],"%Y-%m-%d")
start_date = date(start_date_type.year,start_date_type.month,start_date_type.day)
end_date_type = datetime.strptime(request.POST['car_sale_end'],"%Y-%m-%d")
end_date = date(end_date_type.year,end_date_type.month,end_date_type.day)
f.save()
return HttpResponse("Success")
else:
return HttpResponse(f.errors)
else:
return HttpResponse("Was expecting POST.")

(I would like to thank Eduardo de Oliveira Padoan for helping me correcting my code.)
Most of the code is similar to the normal newforms code. The addition would be in the Date parsing...which is required if you want to save the data into a db.
Then update the urls.py file in the project folder and fire up the server to test the app.
Here are the screenshots:










For once I wouldn't agree with the phrase "No pain, No gain". Just add some exception handling and you can create a cool Newforms base Django app that validates easily and effectively.

Hope you like the django-check-constraints project and use it.

(PS: Currently does not support Datetime and Time...will add it soon.) Supported from 2nd August 2007.

6 comments:

Eduardo de Oliveira Padoan said...

Man, I dont understand how would somebody think that your work 'hardly had any utility'. It seems the most logical way to do common server-side validation, leaving the work to the db without having to write the SQL.

AFAIK, you dont need the some_data dict on the data() functions. Just do "f = ManufacturerForm(request.POST)". Also, as your form is created with form_for_model, why you not just "if f.is_valid(): f.save()"?

Thejaswi Puthraya said...

Thanks for enlightening me about the changes in the code. I've updated the blog with the changes you mentioned....it looks lot better now.

google said...

Just a quick comment on your blog's css... your code areas could use an overflow:auto statement

Eratothene said...

This is an example how django-check-constraints does not work with new forms. Because in the end you have got IntegrityError not a form with correctly built errorlist round each field.
So in order to use your code in real application I will have to right my own implementation of form_for_model and form_for_instance, which will add clean_(field name) functions for each field ir order to get coorect errorlist in the form

Eratothene said...

By the way, it is very difficult to get from constraint what field it is associated with, hence it is very difficult to create form_for_model and form_for_instance, which will work with this contraints.
Please improve your constraint implementation to make it compatible with newforms.

tim said...

form validation is great - but sometimes you want to save objects without using a form - and in these situations it would be nice to know the proper validation is taking place. There's nothing that says you can't implement form validation and check constraints together - a little more processing, but a lot more peace of mind - i'm very happy with your project and wish django would consider merging it into core.