Newforms admin and custom widgets

45 views
Skip to first unread message

Rob Hudson

unread,
Jul 18, 2008, 6:36:00 PM7/18/08
to Django users
Hello,

This is question my get long so I'll post the short version then the
longer more detailed version:

The short:
My goal is provide the user with an autocomplete AJAX widget to
quickly look up ISBNs. The ISBNs will end up in a text field in HTML
to be sent back to the backend where I'd like it split and save in the
ManyToMany field.

As a first deep foray into newforms I'm getting stuck at multiple
levels.

The long:

I have a Newsletter model with some basic fields. I have a Special
model with FK to Newsletter and ManyToMany to books for book specials
to be included with the newsletter. Since the Book model contains 16k
+ records the default admin for this takes forever to load and render
which makes it unusable.

I found a django snippet that I tried to adapt to create a
CommaSeparatedBooks(Field|Input) here:
http://www.djangosnippets.org/snippets/595/

From there I'm trying to create a ModelForm from my Special model and
override the book field to use my custom Field.

Then I'm providing the FormSet to the StackedInline class to use.

At this point I'm getting an error when I load the admin:
Error while importing URLconf 'anglers.urls': 'ModelFormOptions'
object has no attribute 'many_to_many'.

So I've decided to come here for help and try to see where I've gone
astray. See code below.

## admin.py

from django.contrib import admin
from django.db.models.fields.related import ManyToManyField
from django.newforms import ModelForm
from django.newforms.fields import Field
from django.newforms.models import modelformset_factory
from django.newforms.util import ValidationError
from django.newforms.widgets import Input
from anglers.book.models import Book
from anglers.newsletter.models import Newsletter, Special

# Widgets and Fields

class CommaSeparatedBooksInput(Input):
input_type = 'text'
def render(self, name, value, attrs=None):
if value is None:
value = ''
elif isinstance(value, (list, tuple)):
value = ', '.join([book.isbn for book in value])
return super(CommaSeparatedBooksInput, self).render(name,
value, attrs)

class CommaSeparatedBooksField(Field):
widget = CommaSeparatedBooksInput
def clean(self, value):
super(CommaSeparatedBooksField, self).clean(value)
if not value:
return ''
if isinstance(value, (list, tuple)):
return value
isbns = set(value.split(','))
isbns = set([isbn.strip() for isbn in isbns])
books = list(Book.objects.filter(isbn__in=isbns))
unknown_books = isbns ^ set([book.isbn_clean for book in
books])
if unknown_books:
raise ValidationError('Book ISBN values not found: %s' %
str(unknown_books))
return books

# ModelForms and FormSets

class SpecialBookForm(ModelForm):
books = CommaSeparatedBooksField()
class Meta:
model = Special

SpecialBookFormSet = modelformset_factory(SpecialBookForm)

# ModelAdmins with Inlines

class SpecialAdmin(admin.ModelAdmin):
list_display = ()
search_fields = ()
ordering = ()

class SpecialBookInline(admin.StackedInline):
model = Special
formset = SpecialBookFormSet

class NewsletterAdmin(admin.ModelAdmin):
list_display = ('start_date', 'end_date')
ordering = ('-start_date',)
inlines = (SpecialBookInline,)

admin.site.register(Newsletter, NewsletterAdmin)
admin.site.register(Special, SpecialAdmin)



## models.py

class Newsletter(models.Model):
message = models.TextField()
start_date = models.DateField(default=datetime.today)
end_date = models.DateField()

def __unicode__(self):
return u"Newsletter from %s to %s"

class Special(models.Model):
newsletter = models.ForeignKey(Newsletter)
header = models.CharField(max_length=200, core=True)
message = models.TextField()
books = models.ManyToManyField(Book)

Thanks for any guidance.

-Rob

Brian Rosner

unread,
Jul 22, 2008, 9:26:18 PM7/22/08
to django...@googlegroups.com
On Fri, Jul 18, 2008 at 4:36 PM, Rob Hudson <trebor...@gmail.com> wrote:

> # ModelForms and FormSets
>
> class SpecialBookForm(ModelForm):
> books = CommaSeparatedBooksField()
> class Meta:
> model = Special
>
> SpecialBookFormSet = modelformset_factory(SpecialBookForm)

This is wrong. The modelformset_factory takes a model as the first
positional argument. The documentation [1], should address this
better. However to take this a step further since you are later using
this as the formset in a StackedInline it really should be written:

SpecialBookFormSet = inlineformset_factory(Newsletter, Special,
form=SpecialBookForm)

[1]: http://www.djangoproject.com/documentation/modelforms/#model-formsets

Brian Rosner
http://oebfare.com


--
Brian Rosner
http://oebfare.com

Rob Hudson

unread,
Jul 24, 2008, 8:23:07 PM7/24/08
to django...@googlegroups.com
> SpecialBookFormSet = inlineformset_factory(Newsletter, Special,
> form=SpecialBookForm)
>
> [1]: http://www.djangoproject.com/documentation/modelforms/#model-formsets

Hmm... I'm still not able to get it to work. I think I'll try to
whittle my code down to an example that I can play with and post a
without all the clutter of my real code.

I suppose I should first create a custom model field without it being
an inline and make sure I understand that, and then bring it (and my
new found understanding) over to extending that towards an inline
custom model field and custom widget.

-Rob

Rob Hudson

unread,
Sep 17, 2008, 11:28:58 PM9/17/08
to Django users
I was playing with this again tonight and it's just not working for me
no matter what I try. I'm wondering if there are issues with
ManyToMany and Inlines and trying to override them?
Reply all
Reply to author
Forward
0 new messages