#7287 closed Uncategorized (fixed)
Newforms' initial values as models, for ModelChoiceField etc
Reported by: | Owned by: | nobody | |
---|---|---|---|
Component: | Documentation | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | yes |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
ModelChoiceField cleans form data into model instances, but forms don't accept model instances as initial values. This seems a bit inconsistent to me. Patch attached.
Attachments (1)
Change History (16)
by , 17 years ago
Attachment: | newforms-initial-model.diff added |
---|
comment:1 by , 17 years ago
Another inconsistency. If my choices are of type int, clean should bring them back to int. At the moment, it seems to leave them as unicode.
comment:2 by , 16 years ago
milestone: | → 1.0 |
---|---|
Triage Stage: | Unreviewed → Design decision needed |
comment:3 by , 16 years ago
Has patch: | set |
---|---|
milestone: | 1.0 → 1.0 maybe |
Needs tests: | set |
Patch needs improvement: | set |
Triage Stage: | Design decision needed → Accepted |
That patch is not cool. Importing all of django.db.models
just for rendering a widget isn't a good idea, since you might be using forms standalone. Plus it's a leaky abstraction. We've tried very hard to keep that leakiness in django.forms.models
in that part of the code. If that import is the price to be paid for using instances as default data, then you can't use instances there. If you can do it without having to import models, the enhancement has a chance (although a hasattr
test would look a bit ugly, too, so I'm coming up a bit blank on how to do it).
comment:4 by , 16 years ago
Yep no worries wasn't aware of that. Maybe we throw it into django.forms.models instead then?
comment:5 by , 16 years ago
I've been bitten by this inconsistency myself. I think maybe an easy way to clear it up would be to edit http://www.djangoproject.com/documentation/forms/#initial and add some initial values for non-text fields so that it's obvious that you can't just pass in models.
comment:6 by , 16 years ago
Component: | Forms → Documentation |
---|---|
milestone: | 1.0 maybe → post-1.0 |
A note in the docs is probably the right approach here.
comment:7 by , 16 years ago
Also bitten by this, and took a while to track down the real cause. I'm storing cleaned data for a multi-page form in the session, and using that as initial
data when users go back to a previous page. When returning to a previous page, ModelChoiceField's were suddenly using the cleaned model instance's __unicode__
as the form widget's value, which raised ValueError: invalid literal for int() with base 10
(see #8974). It makes sense that I should be able to pass back in the cleaned_data that I'm getting out of a form.
comment:8 by , 16 years ago
I don't think that this is going to be solved by documentation. I've had the "ValueError: invalid literal for int() with base 10" error on a multi-form page (not a multi-page form, in my case :) ), also, and I managed to get rid of it by changing db/models/forms/init.py (line ~356) to:
def get_db_prep_value(self, value): if value is None: return None try: return int(value) except: return int(value.pk)
I then had a problem with the Select widget comparing each c in "choices" to the contents of the set "selected_choices." The real issue was that c could be any one of several types, but you have to compare to the db values (primary keys, in my case) for equality (list membership). I got around that by subclassing the Select widget with something like this:
class GoodSelect(Select): """ Fix the ForeignKey bug. It's a known issue: http://code.djangoproject.com/ticket/7287 """ def render_option(self, option_value, option_label, selected_choices): selected_html = (option_value in selected_choices) and u' selected="selected"' or '' return u'<option value="%s"%s>%s</option>' % ( escape(option_value), selected_html, conditional_escape(force_unicode(option_label))) def render_options(self, choices, selected_choices): # Normalize to strings. selected_choices = set([self.__pk_or_unicode(v) for v in selected_choices]) output = [] for option_value, option_label in chain(self.choices, choices): if isinstance(option_label, (list, tuple)): output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value))) for option in option_label: output.append(self.render_option(*option)) output.append(u'</optgroup>') else: output.append(self.render_option(option_value, option_label, selected_choices)) return u'\n'.join(output) def __pk_or_unicode(self,obj): # If the object is already unicode, try to convert it to an int first. # If that doesn't work, then just return the force_unicode bit. # This is *probably* safe because when we try to put stuff in the db, we convert it anyway. if isinstance(obj,unicode): try: return int(obj) except: return force_unicode(obj) elif isinstance(obj,str): return force_unicode(obj) else: return obj.pk
I'm still exploring to come up with a better fix, but for now, this is functioning well for me.
comment:9 by , 16 years ago
Cc: | added |
---|
comment:11 by , 15 years ago
Has patch: | unset |
---|---|
Needs documentation: | set |
Needs tests: | unset |
Patch needs improvement: | unset |
removing has_patch since the patch isn't a doc patch
comment:12 by , 14 years ago
Patch should be aware of "to_field_name" param for ModelChoiceField, which is pk by default, but can be overriden by this param.
comment:13 by , 14 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
comment:14 by , 14 years ago
(In [13578]) [1.2.X] Fixed #13679, #13231, #7287 -- Ensured that models that have ForeignKeys/ManyToManyField can use a a callable default that returns a model instance/queryset. #13679 was a regression in behavior; the other two tickets are pleasant side effects. Thanks to 3point2 for the report.
Backport of r13577 from trunk.
comment:15 by , 13 years ago
Cc: | removed |
---|---|
Easy pickings: | unset |
Severity: | → Normal |
Type: | → Uncategorized |
UI/UX: | unset |
Cleans initial values that are model instances into their pk values