Skip to content

Instantly share code, notes, and snippets.

@1player
Last active April 17, 2023 20:05
Show Gist options
  • Save 1player/0cab02b415cfe3b56f1f to your computer and use it in GitHub Desktop.
Save 1player/0cab02b415cfe3b56f1f to your computer and use it in GitHub Desktop.
Django natural sorted ModelChoiceField
# Tested with Django 1.7.1
# and natsort 3.5.1
# Very hackish, but that's the easiest and fastest solution I've found.
import natsort
from django import forms
# Adapted from private forms.ModelChoiceIterator
class NatSortedModelChoiceIterator(object):
def __init__(self, field):
self.field = field
self.queryset = field.queryset
def _get_choices(self):
if self.field.empty_label is not None:
yield ("", self.field.empty_label)
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 __iter__(self):
choices = list(self._get_choices())
# choices is a list of choice tuples, index 1 is the label
for choice in natsort.natsorted(choices, key=lambda model: model[1]):
yield choice
def __len__(self):
return (len(self.queryset) +
(1 if self.field.empty_label is not None else 0))
def choice(self, obj):
return (self.field.prepare_value(obj), self.field.label_from_instance(obj))
class NatSortedModelChoiceField(forms.ModelChoiceField):
def _get_choices(self):
if hasattr(self, '_choices'):
return self._choices
return NatSortedModelChoiceIterator(self)
choices = property(_get_choices, forms.ChoiceField._set_choices)
class NatSortedModelMultipleChoiceField(forms.ModelMultipleChoiceField, NatSortedModelChoiceField):
def __init__(self, *args, **kwargs):
NatSortedModelChoiceField.__init__(self, *args, **kwargs)
# Example
class MyForm(forms.Form):
field = NatSortedModelChoiceField(queryset=MyModel.objects.all())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment