Autocompletion for GenericForeignKey

Model example

Consider such a model:

from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models


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

    content_type = models.ForeignKey(
        'contenttypes.ContentType',
        null=True,
        blank=True,
        editable=False,
    )

    object_id = models.PositiveIntegerField(
        null=True,
        blank=True,
        editable=False,
    )

    location = GenericForeignKey('content_type', 'object_id')

    def __str__(self):
        return self.name

View example for QuerySetSequence and Select2

We’ll need a view that will provide results for the select2 frontend, and that uses QuerySetSequence as the backend. Let’s try Select2QuerySetSequenceView for this:

from dal_select2_queryset_sequence.views import Select2QuerySetSequenceView

from queryset_sequence import QuerySetSequence

from your_models import Country, City


class LocationAutocompleteView(Select2QuerySetSequenceView):
    def get_queryset(self):
        countries = Country.objects.all()
        cities = City.objects.all()

        if self.q:
            countries = countries.filter(continent__incontains=self.q)
            cities = cities.filter(country__name__icontains=self.q)

        # Aggregate querysets
        qs = QuerySetSequence(guitars, trumpets)

        if self.q:
            # This would apply the filter on all the querysets
            qs = qs.filter(name__icontains=self.q)

        # This will limit each queryset so that they show an equal number
        # of results.
        qs = self.mixup_querysets(qs)

        return qs

Register the view in urlpatterns as usual, ie.:

from .views import LocationAutocompleteView

urlpatterns = [
    url(
        r'location-autocomplete/$',
        LocationAutocompleteView.as_view(),
        name='location-autocomplete'
    ),
]

Form example

As usual, we need a backend-aware widget that will make only selected choices to render initially, to avoid butchering the database. As we’re using a QuerySetSequence and Select2, we’ll try QuerySetSequenceSelect2 widget.

Also, we need a field that’s able to use a QuerySetSequence for choices to do validation on a single model choice, we’ll use QuerySetSequenceModelField.

Finnaly, we can’t use Django’s ModelForm because it doesn’t support non-editable fields, which GenericForeignKey is. Instead, we’ll use FutureModelForm.

Result:

class TestForm(autocomplete.FutureModelForm):
    location = autocomplete.QuerySetSequenceModelField(
        queryset=autocomplete.QuerySetSequence(
            Country.objects.all(),
            City.objects.all(),
        ),
        required=False,
        widget=autocomplete.QuerySetSequenceSelect2('location-autocomplete'),
    )

    class Meta:
        model = TestModel