Custom form field

35 views
Skip to first unread message

Daniel Gagnon

unread,
Apr 20, 2011, 10:44:33 AM4/20/11
to django-users
I'm trying to create a custom form field and I'm not succeeding at it so far.

I want to create an autocomplete field with jquery. It would be used on models that refer another model via a foreign key where the linked model have an id and a name (unique). I would like to render the name of the linked model in html. I would like the user to be able to select a value via autocompletion (possibly thousands of values). And I would like the to_python to query the db to get the actual object associated with the name the user picked via autocompletion.

The javascript part of the equation works very well. However, I'm having trouble with the creating a custom field part. I wasted a few hours on this so far and I'm just not getting how to do it.

Could someone give me some pointers on this?

Thanks

Shawn Milochik

unread,
Apr 20, 2011, 10:48:00 AM4/20/11
to django...@googlegroups.com
Maybe just save your time and re-use some tasty open-source.

Examples:

http://code.google.com/p/django-ajax-selects/

http://code.google.com/p/django-autocomplete/

Daniel Gagnon

unread,
Apr 20, 2011, 11:06:49 AM4/20/11
to django-users
The first one uses a jquery plugin that's discontinued and the second one works only in the admin.

Also, I feel I'm missing something that's not so complex...


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.


Shawn Milochik

unread,
Apr 20, 2011, 11:11:00 AM4/20/11
to django...@googlegroups.com
http://docs.djangoproject.com/en/1.3/howto/custom-model-fields/

What specific problems/errors are you experiencing?

Daniel Gagnon

unread,
Apr 20, 2011, 11:20:04 AM4/20/11
to django-users
I'm trying to create a Form Field, not a Model Field.

The doc about creating a custom one is a tiny paragraph at the end of this page: http://docs.djangoproject.com/en/1.3/ref/forms/fields/

I read the source code of django but I'm having trouble even finding where exactly is the value stored in the field.

The problems I have are that I don't know how to do the following:

  • The field is going to receive an object and should render the name property of that object in html (so far, I can only render the id).
  • The page is going to POST a string containing the name of the object (unique), it should be converted back to an object.

On Wed, Apr 20, 2011 at 11:11 AM, Shawn Milochik <sh...@milochik.com> wrote:
http://docs.djangoproject.com/en/1.3/howto/custom-model-fields/

What specific problems/errors are you experiencing?

--

Daniel Roseman

unread,
Apr 20, 2011, 11:27:50 AM4/20/11
to django...@googlegroups.com
On Wednesday, April 20, 2011 4:20:04 PM UTC+1, Dan wrote:
I'm trying to create a Form Field, not a Model Field.

The doc about creating a custom one is a tiny paragraph at the end of this page: http://docs.djangoproject.com/en/1.3/ref/forms/fields/

I read the source code of django but I'm having trouble even finding where exactly is the value stored in the field.

The problems I have are that I don't know how to do the following:

  • The field is going to receive an object and should render the name property of that object in html (so far, I can only render the id).
  • The page is going to POST a string containing the name of the object (unique), it should be converted back to an object.

Rendering is the job of the widget, not the field. You will need to define a custom widget and make that the default for your field.

Converting a POSTed value is the job of the field's `clean()` method. 
--
DR.

Daniel Gagnon

unread,
Apr 20, 2011, 11:48:14 AM4/20/11
to django-users
So far, I have the following code:

from django.forms.widgets import TextInput
from django.forms.fields import Field

class AutoCompleteWidget(TextInput):

    def render(self, name, value, attrs=None):
        if hasattr(value, 'name'):
            v = value.name

        else:
            v = None

        super(AutoCompleteWidget, self).render(self, name, v, attrs)

class AutoCompleteField(Field):
    widget = AutoCompleteWidget

    def clean(self):
        pass    # Not implemented yet

class TicketForm(ModelForm):

    target = AutoCompleteField()

    class Meta:
        model = Ticket
        exclude = ('slug')


When I use this code the whole form stops rendering! What's wrong with my widget?


--

DrBloodmoney

unread,
Apr 20, 2011, 12:17:47 PM4/20/11
to django...@googlegroups.com
On Wed, Apr 20, 2011 at 11:48 AM, Daniel Gagnon <redal...@gmail.com> wrote:
> So far, I have the following code:
> from django.forms.widgets import TextInput
> from django.forms.fields import Field
> class AutoCompleteWidget(TextInput):
>     def render(self, name, value, attrs=None):
>         if hasattr(value, 'name'):
>             v = value.name
>         else:
>             v = None
>         super(AutoCompleteWidget, self).render(self, name, v, attrs)
> class AutoCompleteField(Field):
>     widget = AutoCompleteWidget
>     def clean(self):
>         pass    # Not implemented yet
> class TicketForm(ModelForm):
>     target = AutoCompleteField()
>     class Meta:
>         model = Ticket
>         exclude = ('slug')
>
> When I use this code the whole form stops rendering! What's wrong with my
> widget?

Take out the self in the super call
(ie) Change this:

super(AutoCompleteWidget, self).render(self, name, v, attrs)

to this:

super(AutoCompleteWidget, self).render(name, v, attrs)

Daniel Gagnon

unread,
Apr 20, 2011, 12:30:12 PM4/20/11
to django-users
Thanks

It still doesn't work though. I found out through logging that value that is receive is an int (the id) instead of being the object itself. How can I make my widget pass the object itself to the widget instead of passing object.id ?

DrBloodmoney

unread,
Apr 20, 2011, 12:35:28 PM4/20/11
to django...@googlegroups.com

Could you also include the model?

Daniel Gagnon

unread,
Apr 20, 2011, 12:45:34 PM4/20/11
to django-users
Sure, here's a minimal version of them (I skipped fields that aren't too relevant to the problem at hand because there's many, including tons of foreign keys):

class Target(Model):
    name = CharField("Target Name", max_length=255, unique=True)
    created = DateTimeField(auto_now_add=True, editable=False)
    modified = DateTimeField(auto_now=True, editable=False)

class Ticket(Model):
    target = ForeignKey(Target, related_name="tickets")
    reference = CharField(max_length=255)
    created = DateTimeField(auto_now_add=True, editable=False)
    modified = DateTimeField(auto_now=True, editable=False)

DrBloodmoney

unread,
Apr 20, 2011, 1:56:42 PM4/20/11
to django...@googlegroups.com
On Wed, Apr 20, 2011 at 12:45 PM, Daniel Gagnon <redal...@gmail.com> wrote:
> Sure, here's a minimal version of them (I skipped fields that aren't too
> relevant to the problem at hand because there's many, including tons of
> foreign keys):
> class Target(Model):
>     name = CharField("Target Name", max_length=255, unique=True)
>     created = DateTimeField(auto_now_add=True, editable=False)
>     modified = DateTimeField(auto_now=True, editable=False)
> class Ticket(Model):
>     target = ForeignKey(Target, related_name="tickets")
>     reference = CharField(max_length=255)
>     created = DateTimeField(auto_now_add=True, editable=False)
>     modified = DateTimeField(auto_now=True, editable=False)

It probably is related to the this being a foreign key field and in
order for it to produce a valid object instance from the Target model,
it needs a queryset. Try to subclass from ModelChoiceField[1] and see
if that works.

[1] http://code.djangoproject.com/browser/django/trunk/django/forms/models.py#L891

Daniel Gagnon

unread,
Apr 20, 2011, 2:27:08 PM4/20/11
to django-users
New code:
 
from django.forms.widgets import Select
from django.forms.models import ModelChoiceField

from Server_Automation.target_mgmt.models import Target

class AutoCompleteWidget(Select):

    def render(self, name, value, attrs=None, choices=()):
        logger.debug(value)     # Logs 1 (the id)
        logger.debug(choices)   # Logs and empty tuple

        super(AutoCompleteWidget, self).render(name, value, choices)

class AutoCompleteField(ModelChoiceField):
    widget = AutoCompleteWidget

class TicketForm(ModelForm):

    target = AutoCompleteField(queryset=Target.objects.all())

    class Meta:
        model = Ticket
        exclude = ('slug',)

For a reason I ignore, my model just doesn't want to pass the choices to the widget and it takes the default value (an empty tuple). I verified this by changing the default value and checking the log.

Daniel Gagnon

unread,
Apr 20, 2011, 2:27:32 PM4/20/11
to django-users

It probably is related to the this being a foreign key field and in
order for it to produce a valid object instance from the Target model,
it needs a queryset. Try to subclass from ModelChoiceField[1] and see
if that works.

[1] http://code.djangoproject.com/browser/django/trunk/django/forms/models.py#L891


Code now:

from django.forms.widgets import Select
from django.forms.models import ModelChoiceField

from Server_Automation.target_mgmt.models import Target

class AutoCompleteWidget(Select):

    def render(self, name, value, attrs=None, choices=()):
        logger.debug(value)     # Logs 1 (the id)
        logger.debug(choices)   # Logs and empty tuple

        super(AutoCompleteWidget, self).render(name, value, choices)

class AutoCompleteField(ModelChoiceField):
    widget = AutoCompleteWidget

class TicketForm(ModelForm):

    target = AutoCompleteField(queryset=Target.objects.all())

    class Meta:
        model = Ticket

Derek

unread,
Apr 22, 2011, 9:31:25 AM4/22/11
to Django users
On Apr 20, 5:06 pm, Daniel Gagnon <redalas...@gmail.com> wrote:
"The first one uses a jquery plugin that's discontinued and the second
one
works only in the admin."

But that first project is live; did you contact the author and ask him/
her about plans to update it (especially as the jquery plugin in
question has a clear upgrade path)?

Karen Tracey

unread,
Apr 23, 2011, 11:51:25 PM4/23/11
to django...@googlegroups.com
On Wed, Apr 20, 2011 at 11:06 AM, Daniel Gagnon <redal...@gmail.com> wrote:
The first one uses a jquery plugin that's discontinued and the second one works only in the admin.

--
http://tracey.org/kmt/

Reply all
Reply to author
Forward
0 new messages