Я вернулся к этой проблеме после повторного поиска в Интернете. Мое представление о том, что нужен специальный менеджер, было неверным. Мне нужен был собственный встроенный набор форм, который берет два набора запросов: один для поиска, а другой с упорядоченным списком элементов для отображения.
Проблема с этой техникой состоит в том, что model_formsets действительно хотели бы, чтобы за существующими экземплярами следовали дополнительные экземпляры. Решение состоит в том, чтобы создать два списка экземпляров для отображения: существующие записи и дополнительные записи. Затем, после того как model_formsets создаст формы, отсортируйте их обратно в порядке отображения.
Чтобы отсортировать формы набора форм, вам нужно применить мой патч django [14655] , чтобы сделать наборы форм итеративными, а затем создать итератор сортировки.
Полученный вид показан ниже:
from django.contrib.auth.models import User
from django.forms.models import inlineformset_factory, BaseInlineFormSet, \
BaseModelFormSet, _get_foreign_key
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from models import Band, Rating
class list_qs(list):
"""a list pretending to be a queryset"""
def __init__(self, queryset):
self.qs = queryset
def __getattr__(self, attr):
return getattr(self.qs, attr)
class BaseSparseInlineFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
self.display_set = kwargs.pop('display_set')
self.instance_class = kwargs.pop('instance_class', self.model)
# extra is limited by max_num in baseformset
self.max_num = self.extra = len(self.display_set)
super(BaseSparseInlineFormSet, self).__init__(*args, **kwargs)
def __iter__(self):
if not hasattr(self, '_display_order'):
order = [(i, obj._display_order)
for i, obj in enumerate(self._instances)]
order.sort(cmp=lambda x,y: x[1]-y[1])
self._display_order = [i[0] for i in order]
for i in self._display_order:
yield self.forms[i]
def get_queryset(self):
if not hasattr(self, '_queryset'):
# generate a list of instances to display & note order
existing = list_qs(self.queryset)
extra = []
dk = _get_foreign_key(self.display_set.model, self.model)
for i, item in enumerate(self.display_set):
params = {dk.name: item, self.fk.name: self.instance}
try:
obj = self.queryset.get(**params)
existing.append(obj)
except self.model.DoesNotExist:
obj = self.instance_class(**params)
extra.append(obj)
obj._display_order = i
self._instances = existing + extra
self._queryset = existing
return self._queryset
def _construct_form(self, i, **kwargs):
# make sure "extra" forms have an instance
if not hasattr(self, '_instances'):
self.get_queryset()
kwargs['instance'] = self._instances[i]
return super(BaseSparseInlineFormSet, self)._construct_form(i, **kwargs)
RatingFormSet = inlineformset_factory(User, Rating, formset=BaseSparseInlineFormSet)
def update(request):
band = Band.objects.all()[0]
formset = RatingFormSet(request.POST or None,
display_set=band.album_set.all(),
instance=request.user)
if formset.is_valid():
objects = formset.save(commit=False)
print "saving %d objects" % len(objects)
for obj in objects:
obj.save()
return HttpResponseRedirect("/update/")
return render_to_response("rating/update.html",
{'formset': formset, 'band':band},
context_instance=RequestContext(request))