meesterguyperson meesterguyperson - 3 months ago 18x
jQuery Question

What is the replacement for modelform_factory in the newest version of Django autocomplete light?

In previous versions of autocomplete light, there was a very quick way to add new forms.

form = modelform_factory(ModelName, fields='__all__')

Provided the model had an autocomplete view registered, this would automatically build a new form based on the model given. Very quick and easy. In version 3.1.6, the latest version that's out today, this feature appears to have been removed. I'm having to go back and rework everything to get us upgraded, and I'm wondering if there is anything like the old
available in the new version that I have just missed. Or is there a quick way of setting up a generic autocomplete view / form that can be easily reused? Any thoughts are appreciated.


So after lots of hunting, I was unable to locate anything resembling the previous modelform_factory of autocomplete light, so I decided to make my own. The following is an example taken directly from what we have in our production CMS.

from dal import autocomplete

from mymodels import ThisModel, ThatModel, AnotherModel

def autocomplete_form_factory(ac_model, *args, **kwargs):
    field_url_dict = {}
    m2m = ('mypeeps', 'yourpeeps',)
    if ac_model in (ThisModel, ThatModel):
        # Connects the "stuff_field" of these
        # models to the url named "stuff-autocomplete"
        field_url_dict = {
            'stuff_field': 'stuff'
    elif ac_model == AnotherModel:
        # Connects AnotherModel's ForeignKey field "headhoncho"
        # and its ManyToManyFields "mypeeps" and "yourpeeps"
        # to the "peeps-autocomplete" url
        field_url_dict = {
            'headhoncho': 'peeps',
            'mypeeps': 'peeps',
            'yourpeeps': 'peeps',

    # Assign the appropriate widgets based on this model's autocomplete dictionary
    ac_widgets = {}
    ac_fields = kwargs.get('fields', ('__all__'))
    for field, url in field_url_dict.iteritems():
        is_m2m = field in m2m
        text = 'Type to return a list of %s...' if is_m2m else 'Type to return a %s list...'
        kwargs = {
            'url': '%s-autocomplete' % url,
            'attrs': {
                'data-placeholder': text % ac_model._meta.get_field(field).verbose_name,
                'data-minimum-input-length': 3,
        ac_widgets[field] = autocomplete.ModelSelect2Multiple(**kwargs) if is_m2m else autocomplete.ModelSelect2(**kwargs)

    # Create the form
    class DynamicAutocompleteForm(forms.ModelForm):
        class Meta:
            model = ac_model
            fields = ac_fields
            widgets = ac_widgets

    return DynamicAutocompleteForm

For the corresponding views, you might do something along the lines of the following:

class BaseAutocomplete(autocomplete.Select2QuerySetView):
    model = None
    fields = ['title']
    filters = {}

    def get_queryset(self):
        if not self.request.user.is_authenticated() or not self.q or len(self.q) < 3:
            return self.model.objects.none()

        # OR all of our field searches together
        obj = Q()
        for field in self.fields:
            kwargs = {}
            kwargs['%s__icontains' % field] = self.q
            obj = obj | Q(**kwargs)

        return self.model.objects.filter(obj).filter(**self.filters)

class StuffAutocomplete(BaseAutocomplete):
    model = Stuff
    filters = {'is_awesome': True}

class PeepsAutocomplete(BaseAutocomplete):
    model = Peeps
    fields = ['name', 'notes']

And for urls, you might use the following:

url(r'^stuff-autocomplete/$', StuffAutocomplete.as_view(), name='stuff-autocomplete'),
url(r'^peeps-autocomplete/$', PeepsAutocomplete.as_view(), name='peeps-autocomplete',),

To make use of the form factory in your code, you would use something like the following, assigning the results to a model admin's form or using it as a base for a more complex form:

autocomplete_form_factory(AnotherModel, fields=["headhoncho", "mypeeps"])

This mimics the funcitonality we had with autocomplete's earlier modelform_factory, making it very simple to create autocomplete forms and add them to the admin as needed after getting everything set up. Hoping this will help save someone some time.