#7006 closed (invalid)
Overridden fields/widgets for ForeignKeys in ModelForms
| Reported by: | Owned by: | Manuel Saelices | |
|---|---|---|---|
| Component: | Forms | Version: | dev | 
| Severity: | Keywords: | ||
| Cc: | Triage Stage: | Accepted | |
| Has patch: | no | Needs documentation: | no | 
| Needs tests: | no | Patch needs improvement: | no | 
| Easy pickings: | no | UI/UX: | no | 
Description
The docs give the impression one can easily override fields/widgets in ModelForm declarations, but for ForeignKeys that doesn't seem to work.
http://www.djangoproject.com/documentation/modelforms/#overriding-the-default-field-types
class Booking(models.Model):
    ...
    affiliate = models.ForeignKey(Affiliate, null=True, blank=True, related_name='bookings')
...
class BookingForm(forms.ModelForm):
    affiliate = forms.CharField(max_length=20, required=False, label='Affiliate Code')
    
    class Meta:
        model = Booking
        fields = ('first_name', 'last_name', 'company', 'email', 'phone', 'mobile',
                  'adults', 'children', 'affiliate', 'notes')
I need the user to be able to type the 'affiliate' key value straight in without using a dropdown, but using the above approach doesn't attempt to save the affiliate value at all, using r7409. Am I missing something or has this functionality not been catered for?
Change History (10)
comment:1 by , 17 years ago
follow-up: 3 comment:2 by , 17 years ago
Sorry - Apologies for my stupidity regarding the formatting and double post. 
class Article(models.Model):
    title = models.CharField(max_length=100)
class ArticleForm(ModelForm):
    pub_date = models.CharField('Bump!',max_length=10)
    
    class Meta:
        model = Article
comment:3 by , 17 years ago
| milestone: | → 1.0 beta | 
|---|
FYI The correct output can be obtained by doing the following:
class Article(models.Model):
    title = models.CharField(max_length=100)
class ArticleForm(ModelForm):    
    class Meta:
        model = Article
af = ArticleForm()
af.base_fields['pub_date'] = forms.CharField(...)
Which then shows two fields. You can also overwrite a field using this same method.
An example of ModelForm has been referenced on this page:
http://www.djangoproject.com/documentation/models/model_forms/
which is also not working.
comment:4 by , 17 years ago
| milestone: | 1.0 beta → 1.0 | 
|---|
Looks like just a normal bug, not a 1.0-beta thing.
comment:5 by , 17 years ago
| Triage Stage: | Unreviewed → Accepted | 
|---|
comment:6 by , 17 years ago
I suspect the issue here is that it still needs to be a ModelChoiceField, though you can drop in whatever widget you like. Please check whether this works or not.
comment:7 by , 17 years ago
| Owner: | changed from to | 
|---|---|
| Status: | new → assigned | 
comment:8 by , 17 years ago
I've tested ticket with this models and form (used admin site for test):
# Models class Affiliate(models.Model): name = models.CharField(max_length=100) class Booking(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) affiliate = models.ForeignKey(Affiliate, null=True, blank=True, related_name='bookings') # Forms class BookingForm(forms.ModelForm): affiliate = forms.CharField(max_length=20, required=False, label='Affiliate Code') class Meta: model = Booking fields = ('first_name', 'last_name', 'affiliate') # Admin class BookingAdmin(admin.ModelAdmin): form = BookingForm admin.site.register(Affiliate) admin.site.register(Booking, BookingAdmin)
Admin rendered successfully a CharField, but problem was adding a Booking object.
The traceback I got when tried to adding a Booking object with an affiliate value of '1' was:
Traceback:
File "/var/lib/python-support/python2.5/django/core/handlers/base.py" in get_response
  86.                 response = callback(request, *callback_args, **callback_kwargs)
File "/var/lib/python-support/python2.5/django/contrib/admin/sites.py" in root
  173.                 return self.model_page(request, *url.split('/', 2))
File "/var/lib/python-support/python2.5/django/views/decorators/cache.py" in _wrapped_view_func
  44.         response = view_func(request, *args, **kwargs)
File "/var/lib/python-support/python2.5/django/contrib/admin/sites.py" in model_page
  192.         return admin_obj(request, rest_of_url)
File "/var/lib/python-support/python2.5/django/contrib/admin/options.py" in __call__
  185.             return self.add_view(request)
File "/var/lib/python-support/python2.5/django/db/transaction.py" in _commit_on_success
  238.                 res = func(*args, **kw)
File "/var/lib/python-support/python2.5/django/contrib/admin/options.py" in add_view
  483.                 new_object = self.save_form(request, form, change=False)
File "/var/lib/python-support/python2.5/django/contrib/admin/options.py" in save_form
  361.         return form.save(commit=False)
File "/var/lib/python-support/python2.5/django/forms/models.py" in save
  218.         return save_instance(self, self.instance, self._meta.fields, fail_message, commit)
File "/var/lib/python-support/python2.5/django/forms/models.py" in save_instance
  43.         f.save_form_data(instance, cleaned_data[f.name])
File "/var/lib/python-support/python2.5/django/db/models/fields/__init__.py" in save_form_data
  406.         setattr(instance, self.name, data)
File "/var/lib/python-support/python2.5/django/db/models/fields/related.py" in __set__
  264.                                  self.field.name, self.field.rel.to._meta.object_name))
Exception Type: ValueError at /admin/ticket7006/booking/add/
Exception Value: Cannot assign "u'1'": "Booking.affiliate" must be a "Affiliate" instance.
comment:9 by , 17 years ago
| Resolution: | → invalid | 
|---|---|
| Status: | assigned → closed | 
I think error is the normal behaviour here. Validation success because is a simple CharField, but when you save form, form.cleaned_data['affiliate'] is '1' instead of a instance to Affiliate.
This can be quickly done with this form (if you don't want to use other custom Field):
class BookingForm(forms.ModelForm): affiliate = forms.CharField(max_length=20, required=False, label='Affiliate Code') class Meta: model = Booking fields = ('first_name', 'last_name', 'affiliate') def clean_affiliate(self): value = self.cleaned_data['affiliate'] try: return Affiliate.objects.get(id=value) except Affiliate.DoesNotExist: raise ValidationError('Affiliate %s does not exist' % value)
I will mark this as invalid, because I think behaviour is right.
Re: Documentation on this page
http://www.djangoproject.com/documentation/modelforms/#overriding-the-default-field-types
class Article(models.Model):
class ArticleForm(ModelForm):
Output result is "Title: <input ...>"
So it seems the lack of an override is generic, and not just for ForeignKeys.