1. Skip to navigation
  2. Skip to content

Entries tagged “newforms”

This Week in Django 30 - 2008-07-20

written by Michael Trier, on Jul 21, 2008 11:54:00 PM.

This Week in Django is a weekly podcast about all things Django.

This week we discuss the NewForms-Admin merge into Trunk, DjangoCon, a few source commits, some cool projects from the community, and the Tip of the Week.

Please see the Show Notes below for all the pertinent information and links

Downloads

AAC Enhanced Podcast (41.8 MB, 49:31, AAC)

MP3 Edition (34.1 MB, 49:31, MP3)

OGG Edition (27.9 MB, 49:31, Vorbis)

The Enhanced Podcast version contains screenshots and easy access links to all of the items we discuss throughout the podcast.

Feeds Available

iTunes Feeds are available. By subscribing using the iTunes feeds the podcasts will automatically be downloaded for you when we release them.

iTunes Feeds

This Week in Django – AAC Edition

This Week in Django – MP3 Edition

Regular RSS Feeds

This Week in Django – AAC Edition

This Week in Django – MP3 Edition

This Week in Django – OGG Edition

Give Us Feedback

Want to give us some feedback on the show? We’re always looking for ideas or suggestions that will help improve each episode. Please contact us at feedback __at__ thisweekindjango.com.

Show Notes

Big News (0:47)

Tracking Trunk (15:04)

Community Catchup (26:16)

  • Kevin Fricovsky – joins the This Week in Django team as Community Evangelist. Kevin will be working to produce the show, contacting guests, gathering news items, coming up with ideas. Kevin has been doing this work regularly anyway so it’s great of him to team up with us to help out the program:
  • PyOhio – Reminder that this free one day conference is in Columbus, OH on July 26, 2008.
  • – via Kevin Fricovsky using the new Twitter Search capability to track all tweets about django.
  • OSCON Python BoF – Tuesday, July 22nd 7pm – 10pm from Jax Bar and Restaurant. Via Jason Kirtland’s excellent blog discorporate.

Tip of the Week (38:32)

This tip comes to us via Ben Jao Ming in his post Django auto-translation of field values.

If you need to translate content in a field then gettext is not going to help you out. Since you can create your own custom fields it’s easy to wrap a CharField with the translation behavior:


from django.db import models
from django.utils.translation import gettext_lazy as _

class AutoTranslateField(models.CharField):
    __metaclass__ = models.SubfieldBase
    def to_python(self, value):
        return str(_(value))

Just add whatever translations you know of to the locale file and run compilemessages.

Thank You! (42:45)

NewForms-Admin Migration Screencast!

written by Michael Trier, on Jul 21, 2008 8:40:00 PM.

Brian Rosner just released a new screencast and blog post, newforms-admin Migration and Screencast, that walks you through how to get started with migrating to NewForms-Admin from your existing trunk based code. The screencast and accompanying blog post are very well done. It’s great to get this kind of helpful information, especially from someone that knows so much about the internals of the code.

I highly encourage everyone to check it out, especially if you’re on the fence on whether or not the conversion is worth it. Brian shows how easy it is to get started.

Django Screencasts 7 - NewForms Admin I

written by Michael Trier, on Feb 9, 2008 9:29:00 AM.

In this screencast I cover converting an existing application to the NewForms-Admin Branch of Django. We will cover hooking the NewForms-Admin into our urls.py module, extracting out the inner Admin class information into a NewForms-Admin class, handling the prepopulate_from, and organizing our admin class information into a separate module.

Downloads

Now available in an Ogg/Theora format!

MP4 Edition (33.0 MB, 21:09, H.264, MP4)

IPod Edition (16.6 MB, 21:09, H.264, MP4)

OGG Edition (34.9 MB, 21:09, Theora, OGG)

Flash Edition – All screencasts are available at ShowMeDo, although not immediately upon publish.

Source Code (44 KB)

Requires the latest version of QuickTime, VLC Media Player, or comparable software capable of playing MP4 container format and the H.264 codec.

Feeds Available

iTunes Feeds are available, So please choose the iTunes feed if you want automatic downloads in iTunes. Please show me some iTunes love.

iTunes Feeds

Django Screencasts – MP4 Edition

Django Screencasts – iPod Edition – Still waiting for it to show up

Regular RSS Feeds

Django Screencasts – MP4 Edition

Django Screencasts – iPod Edition

Show Notes

NewForms-Admin Branch Wiki Page – The NewForms-Admin documentation on the Django Wiki site.

Django Installation Documentation – Covers how to install Django. You will need to go through similar steps when installing the NewForms-Admin branch.

Using Git with Django Screencast – Excellent screencast that gives a nice introduction to working with Git and Django together.

Source code is available in the Source Code download link above. Apparently a lot of people miss that. The source for the application can be downloaded and used under the MIT License.

Django Screencasts 4 - NewForms Personalization

written by Michael Trier, on Dec 13, 2007 8:10:00 AM.

In this screencast I cover NewForms Personalization. Often you have a drop down or some other field in your NewForm that you need to have personalized, or changed based on the logged in user or other instance criteria. This screencast will show you how to accomplish that in your applications.

Source code for the screencast is provided below. Please be sure to read the README file included in the download.

Downloads

Regular (20.8 MB, 13:31, H.264, MP4)

IPod (9.8 MB, 13:31, H.264, MP4)

Source Code (124 KB)

Requires the latest version of QuickTime, VLC Media Player, or comparable software capable of playing MP4 container format and the H.264 codec.

Show Notes

  • Django Documentation on NewForms
  • Marty Alchin’s Post that Discusses Keyword Arguments

Simpler NewForms Save

written by Michael Trier, on Nov 25, 2007 11:53:00 PM.

In my last post on Customizing NewForms Nick pointed out that I had forgot my save() method in the form class definition. The problem was a simple copy and paste error but as I began to copy in the save() method it occurred to me that there might be a simpler way to handle saving in NewForms.

The Normal Save

Normally your form class save() method will look something like this:


def save(self, id=None, commit=True):
    if id is None:
        instance = Link()
    else:
        instance = Link.objects.get(pk=id)

    instance.title = self.cleaned_data['title']
    instance.url   = self.cleaned_data['url']
    instance.notes = self.cleaned_data['notes']

    if commit:
        instance.save()

    return instance

In the above situation I’m using a combined add/edit save method. If the id is passed in then I’m using it to look up the object and update the attributes accordingly. If there is no id then I’m just creating a new instance and saving it to the database, provided the commit option is set.

You may prefer to do the lookup in the view and pass in the instance, but either way you’re going to end up with something that resembles the above code in some form or fashion.

At this point in the process the validation has already occurred. We could call the is_valid() method prior to doing the save if we wanted to, or we can rely on the view to handle that for us.

The important thing to notice about the above code is that it is very generic in nature. If we were working with a Foo model then the code would look like the following:


def save(self, id=None, commit=True):
    if id is None:
        instance = Foo()
    else:
        instance = Foo.objects.get(pk=id)

    instance.attribute = self.cleaned_data['attribute']
    ...

    if commit:
        instance.save()

    return instance

Simplifying the Save

It occured to me that I might be able to simplify this process a bit. It then occured to me that the NewForms source code must be doing this already in order to set up the save() method code for the form_for_model and form_for_instance convienence methods.

After digging in a bit I figured out that there is a save_instance method that essentially uses some meta programming to simplify the above code. save_instance is part of the django.newforms.models module. By importing the method and passing in the appropriate values we get a lot of functionality for free. Now my save() method looks like the following:


def save(self, id=None, commit=True):
    if id is None:
        instance = Link()
    else:
        instance = Link.objects.get(pk=id)
    return forms.models.save_instance(self, instance, commit=commit)

This same structure will work for any form we would be saving.

Notice that we’re just passing in an instance of our model and whatever we have specified for the commit option. the save_instance method handles the rest for us. The save_instance method actually accepts a couple other arguments, so if you’re inclined you can accept and pass those along as well. Here’s a list of the arguments:

  • form – an instance of the form that contains the cleaned_data values.
  • instance – The instance model that will be updated with the new values. Note in the case of a new object, we’re just instantiating the model and passing it in.
  • fields (None) – A restricted list of fields that should be updated. This would be in the case where the fields we want to update are not all the fields in the form instance.
  • fail_message (‘saved’) – If there are errors on the form instance a ValueError exception is raised with the following formatted message:

"The %s could not be %s because the data didn't validate." % (opts.object_name, fail_message)) 

The fail_message is used to format the message. (In case you’re curious opts.object_name is the models meta object_name, which is generally just the model class name.) Usually the default will suffice.

  • commit (True) – If this is true then the changes to the model instance will be saved to the database, otherwise the instance model is updated but the changes are not saved.

Note, the save_instance method always returns back the model instance with the updated values.

Possible Further Enhancements

The above is nice, but it would be great to take it one step further. I’d like to see NewForms automatically add a default save() method to the Form class. Although truthfully it is not something likely to happen since the process of loading the instance could change widely. The above solution is a good compromise in keeping our code DRY, yet still providing us with the flexibility we need.

Update – Some of the folks in the Django IRC Channel pointed me to a discussion on on the discussion group. This looks like it has the potential for addressing this issue by holding an instance of the model in the Form, along with doing away with form_for_model and form_for_instance methods. We’ll have to watch this one closely.

Other Approaches

Another approach to solving this problem, by some, has been to coerce form_for_instance, and using it’s save() method. If you’ve looked into the options for form_for_instance you will have noticed that it accepts a form option. Using the form option you can call form_for_instance and pass in your custom form class. For instance:


form = forms.form_for_instance(Entry, form=EntryForm)

The problem with this approach is that the form_for_instance method will not load up the custom form class with the instance initial data. (See Changeset 3632 for more information on this). So the solution is to do something sort of funky, such as:


instance = Entry.objects.get(pk=id)
Form = forms.form_for_instance(instance, form=EntryForm)

form_instance = Form(request.POST) 
if form_instance.is_valid():
  form_instance.save()

Warning I didn’t test the above but pulled it out of some code I’ve seen flying around. It’s just too ugly for my tastes, and so the solution I’ve proposed above will suffice for now.

I’ve only been playing around with this approach for a couple of hours so I haven’t had a chance to really bang on it. If I notice some problems I’ll post more about it here. Until then, enjoy.

Customizing NewForms

written by Michael Trier, on Nov 23, 2007 4:48:00 PM.

In my last post I covered a very basic introduction to getting NewForms working in your application. The purpose of that post was just to make sure we were all on the same page. For a much more detailed explanation, I highly recommend James Bennett’s post on NewForms, part 1.

If you’ve followed along then at this point you should have a functioning add/edit form that posts back your information and saves it in the database. This works great as long as you want every field in your model to appear on the form. Generally that’s not the case. A lot of times there are fields, like user, that you want to set in the view before saving the form data. You don’t want the user to be able to specify the user, you want to get this information from the currently logged in user information.

To customize your NewForms output there are several different approaches. I’m going to touch on a couple of them, but keep in mind there are many ways to achieve the same results. We’re going to look at the following five ways to modify the fields that appear in the NewForms output:

  • Make the field not editable
  • Modify it Inline
  • Using the Fields Parameter
  • Modifying with a Callback
  • Using a NewForm Class

Make the Field not Editable

The quickest and easiest solution to remove a field from your NewForms output is to just set the field option editable=False in your model class. This will cause the form field to not be displayed in the output. For instance:


class Entry(models.Model):
    pub_date = models.DateTimeField()
    title = models.CharField(maxlength=100)
    description = models.TextField()
    user = models.ForeignKey(User)
    last_updated = models.DateTimeField(editable=False)

In the Entry model above we have set the last_updated field to be not editable, by setting the editable field option to False. Although this will cause the NewForm output to not contain the last_updated field, usually this is not an acceptable solution because it has the added side effect of also removing the field from the Administration pages.

A case where this might make sense is one similar to the above where you have a field like last_updated, which holds the date/time the record was last updated. In that case, you just need to override the model’s save() method and update the field accordingly.

Modifying it Inline

Back to our original hypothetical. Let us assume that we want to store the logged in user information when the user signs our guessbook, but obviously we don’t want to allow the user to type in their User ID. So instead, let’s modify the form so that it doesn’t display the user field, but instead have it get auto-populated in the view.

There are three changes we need to make to accomplish this:

1. Modify the base_fields dict to hide the user field

2. If required, modify the user field to not be required

3. Modify the save routine to set the user information before saving the record.

The following modified view accomplishes all of the above:


from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django import newforms as forms
from django.newforms import widgets
from guestbook.models import Entry

def add_edit_entry(request, id=None):
    if id is None:
        EntryForm = forms.form_for_model(Entry)
    else:
        entry = get_object_or_404(Entry, pk=id)
        EntryForm = forms.form_for_instance(entry)

    EntryForm.base_fields['user'].widget = widgets.HiddenInput()
    EntryForm.base_fields['user'].required = False

    if request.method == 'POST':
        form = EntryForm(request.POST)
        if form.is_valid():
            entry = form.save(commit=False)
            entry.user = request.user
            entry.save()

            return HttpResponseRedirect("/guestbook")
    else:
        form = EntryForm()

    return render_to_response('guestbook/add_edit_entry.html', {'form': form})

Let’s decipher what’s going on in the above code. We start off by modifying the base_fields dict to hide the user field. To accomplish this we need to set the form field’s widget to the HiddenInput widget. This causes the form to be rendered in the NewForm output but it’s type is hidden, so it’s invisible to the end user. We’ve also modified its required attribute to False. Note that we need to make the field not required otherwise the form.is_valid() method will return False, not allowing us to set the user information ourself.

The next change we needed to make is to modify the form.save() method by setting the parameter commit to False. This causes the form.save() method to not save the form data directly to the database, but instead return a new model instance with the form data. By doing this we now have the opportunity to modify the model before saving it.

At this point we just need to set the entry.user to the current request user information and then finally call the entry.save() method which will save our form information to the database.

This method of modifying the form information is one approach to the problem of customizing your form. It can be used to further modify other fields, their properties, or set customized widgets, etc…

The problem with this approach is three-fold. First it quickly becomes unwieldy. Secondly it’s not very reusable. We would need to do the same thing in another view if we wanted similar behavior. Finally and most importantly you make some compromises in security by doing so.

For instance, note that we have set the user widget to a HiddenInput() widget. This still renders the user field to the web page, but renders it as a hidden input field. Any fields that appear on a form are available to be compromised by the client. You should never trust information that comes from a user form, because it can easily be manipulated. If the user field is not something we want on the form, then the best approach is to not have it appear at all, hidden or not. The next approach will do that for us.

Using the Fields Parameter

Both the form_for_model and form_for_instance methods accept a fields parameter, which takes a list of field names to display on the form. By specifying just the fields we want in that list the form will be rendered accordingly. This has the added benefit of not displaying the fields we don’t want as hidden field elements, leading to better security. For instance:


from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django import newforms as forms
from guestbook.models import Entry

def add_edit_entry(request, id=None):
    fields = ('pub_date', 'title', 'description')

    if id is None:
        EntryForm = forms.form_for_model(Entry, fields=fields)
    else:
        entry = get_object_or_404(Entry, pk=id)
        EntryForm = forms.form_for_instance(entry, fields=fields)

    if request.method == 'POST':
        form = EntryForm(request.POST)
        if form.is_valid():
            entry = form.save(commit=False)
            entry.user = request.user
            entry.save()

            return HttpResponseRedirect("/guestbook")
    else:
        form = EntryForm()

    return render_to_response('guestbook/add_edit_entry.html', {'form': form})

Notice that all we added was a fields list containing the fields we want, and passed that into the form_for_model and form_for_instance methods. The result is much cleaner now and the excluded fields will not appear in the NewForm output.

There’s been talk about adding an exclude parameter (or something similar), since the usual case is that you want most of the form but just want to exclude one or two fields. It would be a nice addition, but at the same time it’s important that form_for_model and form_for_instance don’t get unwieldy, since they are just “quick and easy” shortcuts and not meant to be the be all end all for NewForms work.

Another approach to this is to use the formfield callback.

Modifying with a Callback

A formfield callback is a function that we define that gets called each time a new form field is being created. The formfield callback can also be used to do other things to the form field at the time of creation, such as overriding the default form field type.


from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django import newforms as forms
from django.newforms import widgets
from guestbook.models import Entry

def my_callback(field, **kwargs):
    if field.name == 'user':
        return None
    else:
        return field.formfield(**kwargs)

def add_edit_entry(request, id=None):
    if id is None:
        EntryForm = forms.form_for_model(Entry, formfield_callback=my_callback)
    else:
        entry = get_object_or_404(Entry, pk=id)
        EntryForm = forms.form_for_instance(entry, formfield_callback=my_callback)

    if request.method == 'POST':
        form = EntryForm(request.POST)
        if form.is_valid():
            entry = form.save(commit=False)
            entry.user = request.user
            entry.save()

            return HttpResponseRedirect("/guestbook")
    else:
        form = EntryForm()

    return render_to_response('guestbook/add_edit_entry.html', {'form': form})

Note that we simple created a formfield callback function and assigned it to the formfield_callback parameter of the form_for_model and form_for_instance methods.

To exclude the field from the form definition all we have to do is return None for the specified field. This will cause that field to not be part of the form’s field definition. Notice that we need to handle the case where the field is not the field(s) we’re trying to exclude. This is because all fields get passed into the formfield callback. You can read more about this in the Django Documentation.

All of the above might seem like a lot of work and leaves you with an implementation that is not very reusable or DRY. Well you’re right, and the quicker you learn that the better. That’s why there are form classes.

Using a NewForm Class

Customizing your form by creating a form class is the “endorsed” way to customize the look and feel of your forms—for good reason. It gives you the most flexibility and in the long run it is the cleanest way to approach the task. That said, there seems to be incredible resistance by some in creating a form class. Creating the form_for_model and form_for_instance convenience methods make using NewForms immediately accessible but on the other hand it has also put blinders on some programmers causing them to be resistant to more beneficial approaches.

To create a custom form class we need to simply inherit from the forms class and define the form in a similar manner as you define your models. I’m not going to go into the detail of creating a form class because it’s covered well in the offical documentation on newforms. Again, I also highly recommend James Bennett’s post on NewForms, part 1.

So where does this information go? Your custom form class can go wherever you want since it’s just python code. Some people prefer to put it in their view.py file directly, others prefer to put it in its own forms.py file. I prefer the latter, but it really doesn’t matter where you put it. (Believe it or not this is an often asked question.) For example here is one of my applications’ directory structure:

Here’s a simple form class for our Guestbook add / edit entry form:


from django import newforms as forms
from models import Entry
from django.newforms.extras import widgets

class EntryForm(forms.Form):
    pub_date = forms.DateField(label=u'Publish date', widget=widgets.SelectDateWidget())
    title = forms.CharField(max_length=100)
    description = forms.CharField()

    def save(self, id=None, commit=True):
        if id is None:
            instance = Entry()
        else:
            instance = Entry.objects.get(pk=id)
        return forms.models.save_instance(self, instance, commit=commit)

To exclude the fields we don’t want we just need to exclude the field from the NewForms class definition. Once we have our NewForms class defined we can use it in our view as follows:


from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django import newforms as forms
from guestbook.models import Entry
from guestbook.forms import EntryForm

def add_edit_entry(request, id=None):
    if request.method == 'POST':
        form = EntryForm(request.POST)
        if form.is_valid():
            entry = form.save(id=id, commit=False)
            entry.user = request.user
            entry.save()

            return HttpResponseRedirect("/guestbook")    
    else:
        if id is None:
            form = EntryForm()
        else:
            entry = get_object_or_404(Entry, pk=id)
            form = EntryForm(entry.__dict__)

    return render_to_response('guestbook/add_edit_entry.html', {'form': form})

As I stated above, using a form class gives you the most flexibility and provide the best option for reuse throughout your code.

We covered a lot of different approaches and I’m sure there many more creative solutions that people have come up with. Hopefully I’ve given you a couple more options for handling the customization of your NewForm output. The important thing is to be aware of the benefits and trade offs of each approach, and choose wisely.

Getting Started with NewForms

written by Michael Trier, on Nov 23, 2007 5:23:00 AM.

NewForms are a wonderful thing, and once you “get it” there is an “a ha” moment and you begin to appreciate the architecture behind the new forms implementation. Until then it is easy to find yourself struggling with getting basic functionality working. I do not want to duplicate the excellent documentation already provided, so instead I’ll assume you have familiarized yourself with what is there. In addition to the standard documentation, I highly recommend that you familiarize yourself with the Big Nerd Ranch’s post on Using the Django newforms Library.

This post will be a simple walkthrough on creating newforms. I will build on it in a followup post that will address customizing newforms.

Mapping Urls

When I get started on something I always like to start with defining my urls information. A lot of times this helps me think through my requirements for what I’ll be doing and how it will fall into the constructs of the rest of my application. In this case we are just going to create a simple add / edit form for a Guest Book page. Guest Book will consist of a standard entry form for people to “sign our guest book”. Along with a the standard generic list and object detail view urls we’re going to add in our add / edit view url. When we get done it should look something like this:


from guestbook.models import Entry

info_dict = {
    'queryset': Entry.objects.all(),
}

urlpatterns = patterns('django.views.generic.list_detail',
   (r'^(?P[\w-]+)/$', 'object_detail', dict(info_dict, slug_field='slug')),
   (r'^/?$', 'object_list', info_dict),
)

urlpatterns += patterns('guestbook.views',
    (r'^add/$', 'add_edit_entry'),
    (r'^edit/(?P\d+)/$', 'add_edit_entry'),
)

Notice that we’re just mapping /add/ and /edit/#/ to a view method named add_edit_entry. This method will combin both our add and edit functionality for the newform.

That’s all we need to do for the urls. You can choose to make them appear however fits your application the best.

Next we need to lay out our view code. This is where the meat of newforms gets implemented.

Create the View

Most people get their first experience with newforms through the use of the form_for_model and form_for_instance helper methods on the forms class.

These methods make it really easy to get started. The form_for_model and form_for_instance methods are used to create unbound and bound forms respectively. Unbound forms are what you will generally use when you are creating an “add” type of form. The form itself is not bound to any existing data. The bound form is bound to existing data and is used when creating an “edit” type of form. Consider the following views:


from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from django import newforms as forms
from models import Entry

def add_edit_entry(request, id=None):
    if id is None:
        EntryForm = forms.form_for_model(Entry)
    else:
        entry = get_object_or_404(Entry, pk=id)
        EntryForm = forms.form_for_instance(entry)

    if request.method == 'POST':
        form = EntryForm(request.POST)
        if form.is_valid():
            form.save()            
            return HttpResponseRedirect("/guestbook")
    else:
        form = EntryForm()

    return render_to_response('guestbook/add_edit_question.html', {'form': form})

This is a pretty straightforward implementation of an add / edit form. You’ll see this type of code structured many different ways, but I’ve found that this format works well for me. Notice how we’re using form_for_model when an id is not specified, whereas we are using form_for_instance when we have an object instance.

On thing that sometimes trips people up is the fact that form_for_model and form_for_instance return a Form class, not a Form instance. Its the reason we’re instantiating the EntryForm class in the else clause.

form.is_valid() is used to validate the contents of the form. If everything checks out the form is saved and the user is redirected back to our Guestbook listing page. If the form doesn’t validate then the user is returned back to the add/edit form.

When the form is returned this time the form object will be bound and have error information pertaining to the invalid information. The HTML output will include the validation errors near the field that has the problem. You can control how this information gets output by taking a more granular approach to form rendering.

And Now for the Template

A very basic template implementation typically will look like this:


{% extends "base.html" %}

{% block content %}
{{ form }}
{% endblock %}

This will display our form using the as_table specification, meaning that the form will be enclosed in an HTML table. The as_table rendering does not include the containing table tags, so be sure to include them in your template as I’ve shown above. Or better yet investigate the as_ul or as_p implementations.

That’s all it takes to get started with newforms. It’s pretty simple and we’ve only touched the surface of what’s possible. The real value in newforms is the built-in validation and clean data functionality. You can read more about each of these in the newforms documentation.

I’ve got an Introduction to NewForms screencast in the works that will give me the opportunity to dive into some of these things much deeper.

Update

James Bennett just posted an excellent post on NewForms. It’s definitely a required read.

JSON Generic Serializer

written by Michael Trier, on Jul 29, 2007 10:25:00 PM.

Recently I needed to implement some Ajax functionality into a Django app I’m building. Although Django has full support for working with Ajax and the Ajax library of your choice, out of the box it doesn’t steer you in any particular direction in this regard. Because of this I had been a bit hesitant about attacking this particular problem, fearing I would end up losing days of productivity trying to get something going. Luckily my fear was unfounded. It turns out that it’s fairly easy to implement, and quite simple to get your head around it.

One of the things I really needed was an easy way to serialize objects of data as an Ajax response back to my client. I searched around and through a combination of various forum postings and IRC, I was able to come up with the following generic json serializer. Like Generic Views, it allows me to easily make requests without a bunch of extra view code for standard cases. The implementation that I ended up with looks like this:


from django.http import HttpResponse 

def serialize(queryset, root_name=None): 
    if not root_name: 
        root_name = queryset.model._meta.verbose_name_plural 
    return '{"total": %s, "%s": %s}' % (queryset.count(), root_name, json_encode(queryset))  

def json_generic_query_serialize(request, queryset, criteria=None, parameter='query', root_name=None): 
    if criteria:
        qs = queryset.filter(**{'%s' % criteria: request[parameter]})
    else:
        qs = queryset
    return HttpResponse(serialize(qs, root_name=root_name), mimetype='text/javascript')

The only item you’re not likely to have is the method json_encode by Wolfram Kriesing. Believe it or not I found this tidbit on a dpaste post and it was exactly what I needed. Later I found out that Wolfram blogged about this item on his website. I encourage you to read his reasons for writing this nice tidbit.

The reason the default json serializer (django.core.serialize) doesn’t work well for my purposes is because the default implementation is built around the idea that the objects that are serializable need to be deserializable as full objects as well. I’m using the as my Ajax library and YUI will not work well with the default json structure provided by Django.

Just in case the json_encode method gets removed from dpaste I’m reprinting it here. I want to stress again that this is the work of Wolfram Kriesing, not mine. I included this method in the same file as my serializer methods posted above.


import types
from django.db import models
from django.utils import simplejson as json
from django.core.serializers.json import DateTimeAwareJSONEncoder
from decimal import *

def json_encode(data):
    """ 
    The main issues with django's default json serializer is that properties that
    had been added to a object dynamically are being ignored (and it also has 
    problems with some models).
    """ 

    def _any(data):
        ret = None
        if type(data) is types.ListType:
            ret = _list(data)
        elif type(data) is types.DictType:
            ret = _dict(data)
        elif isinstance(data, Decimal):
            # json.dumps() cant handle Decimal
            ret = str(data)
        elif isinstance(data, models.query.QuerySet):
            # Actually its the same as a list ...
            ret = _list(data)
        elif isinstance(data, models.Model):
            ret = _model(data)
        else:
            ret = data
        return ret

    def _model(data):
        ret = {}
        # If we only have a model, we only want to encode the fields.
        for f in data._meta.fields:
            ret[f.attname] = _any(getattr(data, f.attname))
        # And additionally encode arbitrary properties that had been added.
        fields = dir(data.__class__) + ret.keys()
        add_ons = [k for k in dir(data) if k not in fields]
        for k in add_ons:
            ret[k] = _any(getattr(data, k))
        return ret

    def _list(data):
        ret = []
        for v in data:
            ret.append(_any(v))
        return ret

    def _dict(data):
        ret = {}
        for k,v in data.items():
            ret[k] = _any(v)
        return ret

    ret = _any(data)

    return json.dumps(ret, cls=DateTimeAwareJSONEncoder)

Once the generic serializer is in place, it becomes easy to use. We need an entry in our urls.py file that routes the Ajax request to our generic serializer. A typical implementation will look like this:


categories_dict = { 
    'queryset': Category.objects.all(), 
    'root_name': 'categories',
    'criteria': 'name__icontains',  
} 

urlpatterns += patterns('', 
    (r'json/categories/', json_generic_query_serialize, categories_dict), 
)

Notice how flexible this implementation is. We can pass any criteria we want for finding our items. If we wanted to simply return all items, we would only need the ‘queryset’ item.

So what does this look like in the Form definition in our Forms.py file? It depends on what we want to accomplish. As an example, I’m implementing an AutoComplete field from the Yahoo! User Interface Library. My implementation looks like this:


category = forms.CharField(max_length=255, 
                           label=u'Category', 
                           widget=AutoCompleteField(source='/blog/json/categories/', 
                           schema=["categories", "name", "id"], 
                           options={ 'animSpeed': 0.1, 'autoHighlight': True, 'forceSelection': True, 'typeAhead': True }))

In the above sample I’m using a custom AutoCompleteField widget that I created that gives me all of the auto complete goodness that I need. I’ll write more about the implementation of that custom widget field in another blog post.

As you can see, solving the problem of easily getting serialized data in json format makes the whole Ajax process much simpler. Although Ruby on Rails bakes this functionality into the framework at times it becomes a two edged sword. It sure makes it easy for new users to start using Ajax immediately with little or no knowledge, but it also makes it quite painful to use any Ajax library that has not been “anointed.” I have no person preference of one implementation over the other, it’s just important to recognize the advantages and disadvantages of differing approaches.

NewForms Generator

written by Michael Trier, on Jul 29, 2007 9:14:00 AM.

Django’s newforms has a couple of very useful helper methods for generating a Form class based on a model (form_for_model) and a model instance (form_for_instance). Both of these are quite useful, but quickly I find that I want to override the default widget and/or hide certain fields that I will be handling in the POST operation, etc… In no time I reach a point to where I often realize that it’s just easier to define my own Form class for my model and make all my specification in there. You can read all about creating your own Form classes in the Django Documentation on newforms.

The problem is I’m lazy. If I have a model with a fairly large number of fields, this can be a tedious task. To counteract this, I decided to create a simple generator that I use to create the Form class for me based on the model I pass in. It can also accept just certain fields if you know that you want to limit the list of fields in your Form class. The generator is not perfect and will sometimes include defaults, leading to more verbosity than necessary, but it’s a great way to get going on custom forms.


def describe_form(model, fields=None):
    """ 
    Returns a string describing a form based on the model
    """ 
    opts = model._meta
    field_list = []
    for f in opts.fields + opts.many_to_many:
        if not f.editable:
            continue
        if fields and not f.name in fields:
            continue
        formfield = f.formfield()
        if not '__dict__' in dir(formfield):
            continue
        attrs = {}
        valid_fields = ['required', 'initial', 'max_length', 'min_length', 
                        'max_value', 'min_value', 'max_digits', 'decimal_places', 
                        'choices', 'help_text', 'label']
        for k,v in formfield.__dict__.items():
            if k in valid_fields and v != None:
                # ignore defaults, to minimize verbosity
                if k == 'required' and v:
                    continue
                if k == 'help_text' and not v:
                    continue
                if k == 'widget':
                    attrs[k] = v.__class__
                else:
                    attrs[k] = v

        params = ', '.join(['%s=%r' % (k, v) for k, v in attrs.items()])
        field_list.append('    %(field_name)s = forms.%(field_type)s(%(params)s)' % { 'field_name': f.name, 
                          'field_type': formfield.__class__.__name__, 
                          'params': params })

    return '''
from django import newforms as forms
from models import %(object_name)s

class %(object_name)sForm(forms.Form):
%(field_list)s
''' % { 'object_name': opts.object_name,  'field_list': '\n'.join(field_list) }

The code is pretty straightforward. I generally just import it and call the describe_form method from a shell. Obviously there’s a a lot more I can do with this, such as more intelligent ignoring of defaults. In addition, I would like to wire it into django-admin.py so I can do something like:


./manange.py describe_form blog.Post

I could then have it output to the shell, or redirect it to a file.