I actually fixed that yesterday: http://code.djangoproject.com/changeset/10625
1.1 now includes a ``QuerySet.ordered`` property which FormSet uses to
artificially apply ordering, if needed. You can -- and should -- do
the same.
Jacob
There's a more subtle way to introduce the same bug. You can order on
a field that is not unique and run into the same issue: an undefined
return order due to duplicate values on the order column.
I don't see how to solve the general problem at the framework level, but there may be some additional sanity checks we could add to help out here. For instance we could make the formset validation return False if the number of items in the passed QuerySet is not what we expect based on the data dictionary, and/or check (during validation?) that the pk values in the post data match up to the instance pk values after we've matched them up, etc. To anyone with a better knowledge of model fomsets than me: does this sort of additional validation sound reasonable?
On Fri, Apr 24, 2009 at 3:24 PM, Joseph Kocherhans <jkoch...@gmail.com> wrote:On Fri, Apr 24, 2009 at 2:01 PM, Karen Tracey <kmtr...@gmail.com> wrote:I don't see how to solve the general problem at the framework level, but there may be some additional sanity checks we could add to help out here. For instance we could make the formset validation return False if the number of items in the passed QuerySet is not what we expect based on the data dictionary, and/or check (during validation?) that the pk values in the post data match up to the instance pk values after we've matched them up, etc. To anyone with a better knowledge of model fomsets than me: does this sort of additional validation sound reasonable?
I might as well put this out there in case someone crazy like Alex wants to try it. :) My thought for the hard way to fix the issue for formsets is to use the primary key as part of the form prefix and/or put the primary keys into hidden fields then use the data from the form to look up the objects. It's obviously going to be more complicated than that (was the object already deleted? is it allowed to be changed?), but it's an approach that could work I think.
We already have most of the pieces in place for this, we need to play with the architecture a bit though. Any form in a ModelFormset already stores the pk for it's object: http://code.djangoproject.com/browser/django/trunk/django/forms/models.py#L489 . However we don't use that when providing the instance variable: http://code.djangoproject.com/browser/django/trunk/django/forms/models.py#L398 . The issue is that that pk field doens't exist on the form until it's been constructed, so we need to duplicate a tiny bit of the logic to pull the pk out of the data before the form has been constructed. I can take a look at it this weekend(time permitting) unless someone else wants to do it. There shouldn't be too much work that needs doing, but writing tests for this might be a bit messy.
On Fri, Apr 24, 2009 at 4:41 PM, Alex Gaynor <alex....@gmail.com> wrote:On Fri, Apr 24, 2009 at 3:24 PM, Joseph Kocherhans <jkoch...@gmail.com> wrote:On Fri, Apr 24, 2009 at 2:01 PM, Karen Tracey <kmtr...@gmail.com> wrote:I don't see how to solve the general problem at the framework level, but there may be some additional sanity checks we could add to help out here. For instance we could make the formset validation return False if the number of items in the passed QuerySet is not what we expect based on the data dictionary, and/or check (during validation?) that the pk values in the post data match up to the instance pk values after we've matched them up, etc. To anyone with a better knowledge of model fomsets than me: does this sort of additional validation sound reasonable?
I might as well put this out there in case someone crazy like Alex wants to try it. :) My thought for the hard way to fix the issue for formsets is to use the primary key as part of the form prefix and/or put the primary keys into hidden fields then use the data from the form to look up the objects. It's obviously going to be more complicated than that (was the object already deleted? is it allowed to be changed?), but it's an approach that could work I think.We already have most of the pieces in place for this, we need to play with the architecture a bit though. Any form in a ModelFormset already stores the pk for it's object: http://code.djangoproject.com/browser/django/trunk/django/forms/models.py#L489 . However we don't use that when providing the instance variable: http://code.djangoproject.com/browser/django/trunk/django/forms/models.py#L398 . The issue is that that pk field doens't exist on the form until it's been constructed, so we need to duplicate a tiny bit of the logic to pull the pk out of the data before the form has been constructed. I can take a look at it this weekend(time permitting) unless someone else wants to do it. There shouldn't be too much work that needs doing, but writing tests for this might be a bit messy.
Note if we go with this approach (if I'm understanding it correctly) we are slightly changing the way in which we expect model formsets to be used. We currently document that the same QuerySet should be passed in during POST as GET:
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-a-custom-queryset
Switching to an approach that uses the pk ids in the POST data for lookup means we'd:
- silently ignore a queryset argument when we're given a data dictionary?
- issue some sort of deprecation warning about the now-unnecessary and unused parameter?
Also, it would be nice if we could accomplish the lookup in one DB call rather than looking up each individual item in a separate query. Having a model formset with hundreds of entries suddenly require hundreds of DB hits during POST processing instead of one isn't too nice. Not sure if you were planning that?
These two concerns pushed me more towards thinking that leaving the DB lookup as-is and adding some validation sanity checks for when the passed queryset doesn't match up with the POST data might be a "good enough" approach.
Karen
Note if we go with this approach (if I'm understanding it correctly) we are slightly changing the way in which we expect model formsets to be used. We currently document that the same QuerySet should be passed in during POST as GET:
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-a-custom-queryset
Switching to an approach that uses the pk ids in the POST data for lookup means we'd:
- silently ignore a queryset argument when we're given a data dictionary?
- issue some sort of deprecation warning about the now-unnecessary and unused parameter?
Also, it would be nice if we could accomplish the lookup in one DB call rather than looking up each individual item in a separate query. Having a model formset with hundreds of entries suddenly require hundreds of DB hits during POST processing instead of one isn't too nice. Not sure if you were planning that?
These two concerns pushed me more towards thinking that leaving the DB lookup as-is and adding some validation sanity checks for when the passed queryset doesn't match up with the POST data might be a "good enough" approach.
On Fri, Apr 24, 2009 at 5:49 PM, Karen Tracey <kmtr...@gmail.com> wrote:Also, it would be nice if we could accomplish the lookup in one DB call rather than looking up each individual item in a separate query. Having a model formset with hundreds of entries suddenly require hundreds of DB hits during POST processing instead of one isn't too nice. Not sure if you were planning that?
My appraoch was basically going to be
pk = something
kwargs['instance'] = [o for o in self.queryset if o.pk == pk][0]
Which would only be 1 query, and basically has the same behavior as we do now, but correctly :).
Yeah, that was a pretty lazy O(n^2) algorithm, the smart way to do it would be:
if not hasattr(self, '_queryset_dict'):
self._queryset_dict = dict([(o.pk, o) for o in self.queryset])
pk = something
kwargs['instance'] = self._queryset_dict[pk]