Form, fields and widgets

Note

This chapter assumes that you have been through Quick start: adding simple autocompletes and Autocomplete classes.

Design documentation

This app provides optionnal helpers to make forms:

  • autocomplete_light.modelform_factory which wraps around django’s modelform_factory but uses the heroic autocomplete_light.ModelForm.

  • autocomplete_light.ModelForm: the heroic ModelForm which ties all our loosely coupled tools together:

    • SelectMultipleHelpTextRemovalMixin, which removes the “Hold down control or command to select more than one” help text on autocomplete widgets (fixing Django ticket #9321),
    • VirtualFieldHandlingMixin which enables support for generic foreign keys,
    • GenericM2MRelatedObjectDescriptorHandlingMixin which enables support for generic many to many, if django-genericm2m is installed,
    • ModelFormMetaclass which enables FormfieldCallback to replace the default form field creator replacing <select> with autocompletes for relations and creates generic foreign key and generic many to many fields.

You probably already know that Django has form-fields for validation and each form-field has a widget for rendering logic.

autocomplete_light.FieldBase makes a form field field rely on an Autocomplete class for initial choices and validation (hail DRY configuration !), it is used as a mixin to make some simple field classes:

In the very same fashion, autcomplete_light.WidgetBase renders a template which should contain:

  • a hidden <select> field containing real field values,
  • a visible <input> field which has the autocomplete,
  • a deck which contains the list of selected values,
  • add-another optionnal link, because add-another works outside the admin,
  • a hidden choice template, which is copied when a choice is created on the fly (ie. with add-another).

It is used as a mixin to make some simple widget classes:

Examples

This basic example demonstrates how to use an autocomplete form field in a form:

class YourForm(forms.Form):
    os = autocomplete_light.ChoiceField('OsAutocomplete')

Using autocomplete_light.ModelForm

Consider such a model which have every kind of relations that are supported out of the box:

class FullModel(models.Model):
    name = models.CharField(max_length=200)

    oto = models.OneToOneField('self', related_name='reverse_oto')
    fk = models.ForeignKey('self', related_name='reverse_fk')
    mtm = models.ManyToManyField('self', related_name='reverse_mtm')

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    gfk = generic.GenericForeignKey("content_type", "object_id")

    # that's generic many to many as per django-generic-m2m
    gmtm = RelatedObjectsDescriptor()

Assuming that you have registered an Autocomplete for FullModel and a generic Autocomplete, then autocomplete_light.ModelForm will contain 5 autocompletion fields by default: oto, fk, mtm, gfk and gmtm.

class FullModelModelForm(autocomplete_light.ModelForm):
    class Meta:
        model = FullModel
        # add for django 1.6:
        fields = '__all__'

autocomplete_light.ModelForm gives autocompletion super powers to django.forms.ModelForm. To disable the fk input for example:

class FullModelModelForm(autocomplete_light.ModelForm):
    class Meta:
        model = FullModel
        exclude = ['fk']

Or, to just get the default <select> widget for the fk field:

class FullModelModelForm(autocomplete_light.ModelForm):
    class Meta:
        model = FullModel
        autocomplete_exclude = ['fk']

In the same fashion, you can use Meta.fields and Meta.autocomplete_fields. To the difference that they all understand generic foreign key names and generic relation names in addition to regular model fields.

Not using autocomplete_light.ModelForm

Instead of using our autocomplete_light.ModelForm, you could create such a ModelForm using our mixins:

class YourModelForm(autocomplete_light.SelectMultipleHelpTextRemovalMixin,
        autocomplete_light.VirtualFieldHandlingMixin,
        autocomplete_light.GenericM2MRelatedObjectDescriptorHandlingMixin,
        forms.ModelForm):
    pass

This way, you get a fully working ModelForm which does not handle any field generation. You can use form fields directly though, which is demonstrated in the next example.

Using form fields directly

You might want to use form fields directly for any reason:

  • you don’t want to or can’t extend autocomplete_light.ModelForm,
  • you want to override a field, ie. if you have several Autocomplete classes registered for a model or for generic relations and you want to specify it,
  • you want to override any option like placeholder, help_text and so on.

Considering the model of the above example, this is how you could do it:

class FullModelModelForm(autocomplete_light.ModelForm):
    # Demonstrate how to use a form field directly
    oto = autocomplete_light.ModelChoiceField('FullModelAutocomplete')
    fk = autocomplete_light.ModelChoiceField('FullModelAutocomplete')
    m2m = autocomplete_light.ModelMultipleChoiceField('FullModelAutocomplete')
    # It will use the default generic Autocomplete class by default
    gfk = autocomplete_light.GenericModelChoiceField()
    gmtm = autocomplete_light.GenericModelMultipleChoiceField()

    class Meta:
        model = FullModel
        # django 1.6:
        fields = '__all__'

As you see, it’s as easy as 1-2-3, but keep in mind that this can break DRY: Model field’s help_text and verbose_name are lost when overriding the widget.

Using your own form in a ModelAdmin

You can use this form in the admin too, it can look like this:

from django.contrib import admin

from forms import OrderForm
from models import Order

class OrderAdmin(admin.ModelAdmin):
    form = OrderForm
admin.site.register(Order, OrderAdmin)

Note

Ok, this has nothing to do with django-autocomplete-light because it is plain Django, but still it might be useful to someone.