specify queryset when instantiating an inlineformset
I have the following in views.py to generate a page that shows an inline set of forms for a user's "qualifications"
from django.db.models import User
from models import UserQualification
def edit_quals(request):
QualsFormSet = inlineformset_factory(User, UserQualification, fields=('qualification', 'qualification_year', 'qualification_source'))
if request.method == 'POST':
formset = QualsFormSet(request.POST, request.FILES, instance = request.user)
if formset.is_valid():
formset.save()
#send user somewhere
return HttpResponseRedirect(request.user.get_profile_url())
else:
formset = QualsFormSet(instance = request.user)
return render_to_response("edit_quals.html", {
"formset": formset,
}, context_instance=RequestContext(request)开发者_如何学Go)
This works fine, however, I would like to create a formset
that only includes certain 'UserQualification' objects (ie, only ones that are marked as a certain type) so when a user gets to this page they are only editing a subset of their qualifications. This reference seems to be what I want: http://docs.djangoproject.com/en/1.1/topics/forms/modelforms/#changing-the-queryset. It deals with modelformset_factory, however inlineformset_factory is based on the prior, so I figure the same thing should work:
formset = QualsFormSet(instance = request.user, queryset=UserQualification.objects.filter(type__startswith='xyz'))
But going to this page just gives a TypeError: init() got an unexpected keyword argument 'queryset'. There are two methods listed on that reference and I'm having trouble with both.
I provided an answer to a slightly simpler version of this problem here: Limit Django's inlineformset_factory to only create new objects
For what you were trying to do, the InlineFormSet class would look like this instead:
class BaseInlineFilteredFormSet(BaseInlineFormSet):
def get_queryset(self):
## See get_queryset method of django.forms.models.BaseModelFormSet
if not hasattr(self, '_queryset'):
self._queryset = self.queryset.filter(type__startswith='xyz'))
if not self._queryset.ordered:
self._queryset = self._queryset.order_by(self.model._meta.pk.name)
return self._queryset
I believe in this case you need to use the form parameter when calling inlineformset_factory and to pass it a custom form you made with an ad-hoc __init__
method..
If you need it to be parametric, I found around here some time ago the life-saving line
FormSetName.form = staticmethod(curry(FormName, customparam=chosenparam))
where, as explained above, FormSetName was defined with form parameter pointing to FormName.
Btw, would love to give credit to the person suggesting the above solution, would have loved to leave a heart-warming reply, but too little reputation.
edit to explain better my solution:
This would be the custom form class (in this case, to chose a person as a relative, but only among a subset of people, those returned by the relatives() method):
class RelativeSelectForm(forms.ModelForm):
class Meta:
model = Person
fields = ('relative',)
def __init__(self, *args, **kwargs):
try:
profile = kwargs.pop('profile')
except KeyError:
super(RelativeSelectForm, self).__init__(*args, **kwargs)
return
super(RelativeSelectForm, self).__init__(*args, **kwargs)
self.fields['relative'].queryset = profile.relatives().order_by('name')
and it would be used like so, in your vies.py:
RelativesFormSet = inlineformset_factory(Person, Person, form=RelativeSelectForm, can_delete=True, extra=1)
RelativesFormSet.form = staticmethod(curry(RelativeSelectForm, profile=request.user.profile))
so that every time you instantiate a formset it automatically gets the profile parameter filled with the right object and the queryset for the relative select box shows only people he has access to. Hope I made myself a bit clearer :)
精彩评论