Login

FeaturedModelChoiceField

Author:
willhardy
Posted:
August 1, 2009
Language:
Python
Version:
1.1
Score:
5 (after 5 ratings)

Here is a way to get a drop down list from a queryset, with a list of "featured" items appearing at the top (from another queryset). This can be used for long select boxes which have a subset of commonly used values. The empty label is used as a separator and values can appear in both the featured set and the full set (it's more usable if they are in both).

For example a country drop down list with 5 featured countries might look like this:

Andorra
Australia
Kazakhstan
Singapore
Turkey
------------
Afghanistan
Albania
Algeria
American Samoa
Andorra
Angola
(hundreds more)

To use this, define your form field like this:

country = FeaturedModelChoiceField(queryset=Country.objects.all(),
                featured_queryset=Country.objects.featured())
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from django import forms

class FeaturedModelChoiceIterator(object):
    # Adapted from django.forms.models.ModelChoiceIterator
    def __init__(self, field):
        self.field = field
        self.queryset = field.queryset
        self.featured_queryset = field.featured_queryset

    def __iter__(self):
        # Just show all the featured content
        for obj in self.featured_queryset.all():
            yield self.choice(obj)
        # Add the empty label between featured and non-featured content
        if self.field.empty_label is not None:
            yield (u"", self.field.empty_label)
        # And here is the non-featured content
        if self.field.cache_choices:
            if self.field.choice_cache is None:
                self.field.choice_cache = [
                    self.choice(obj) for obj in self.queryset.all()
                ]
            for choice in self.field.choice_cache:
                yield choice
        else:
            for obj in self.queryset.all():
                yield self.choice(obj)

    def choice(self, obj):
        if self.field.to_field_name:
            key = obj.serializable_value(self.field.to_field_name)
        else:
            key = obj.pk
        return (key, self.field.label_from_instance(obj))


class FeaturedModelChoiceField(forms.ModelChoiceField):
    """ This is just like a model choice field, but will add a set of "featured" 
        choices to the top of the list. These choices are provided by the 
        featured_queryset parameter.
    """
    def __init__(self, featured_queryset, *args, **kwargs):
        self.featured_queryset = featured_queryset
        super(FeaturedModelChoiceField, self).__init__(*args, **kwargs)

    def _get_choices(self):
        # NB this method is not in the public Django API.

        # If self._choices is set, then somebody must have manually set
        # the property self.choices. In this case, just return self._choices.
        if hasattr(self, '_choices'):
            return self._choices

        # Otherwise, execute the QuerySet in self.queryset to determine the
        # choices dynamically. Return a fresh QuerySetIterator that has not been
        # consumed. Note that we're instantiating a new QuerySetIterator *each*
        # time _get_choices() is called (and, thus, each time self.choices is
        # accessed) so that we can ensure the QuerySet has not been consumed. This
        # construct might look complicated but it allows for lazy evaluation of
        # the queryset.
        return FeaturedModelChoiceIterator(self)
    choices = property(_get_choices, forms.ChoiceField._set_choices)

    def _get_featured_queryset(self):
        return self._featured_queryset

    def _set_featured_queryset(self, queryset):
        self._featured_queryset = queryset
        self.widget.choices = self.choices

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 3 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 3 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 10 months, 1 week ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 11 months ago
  5. Help text hyperlinks by sa2812 11 months, 3 weeks ago

Comments

Please login first before commenting.