Opened 16 years ago

Closed 16 years ago

Last modified 12 years ago

#7006 closed (invalid)

Overridden fields/widgets for ForeignKeys in ModelForms

Reported by: Simon Litchfield <simon@…> 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 Django Trac, 16 years ago

Re: Documentation on this page

http://www.djangoproject.com/documentation/modelforms/#overriding-the-default-field-types

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

Output result is "Title: <input ...>"

So it seems the lack of an override is generic, and not just for ForeignKeys.

comment:2 by Django Trac, 16 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

in reply to:  2 comment:3 by Django Trac, 16 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 Malcolm Tredinnick, 16 years ago

milestone: 1.0 beta1.0

Looks like just a normal bug, not a 1.0-beta thing.

comment:5 by Eric Holscher, 16 years ago

Triage Stage: UnreviewedAccepted

comment:6 by James Bennett, 16 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 Manuel Saelices, 16 years ago

Owner: changed from nobody to Manuel Saelices
Status: newassigned

comment:8 by Manuel Saelices, 16 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 Manuel Saelices, 16 years ago

Resolution: invalid
Status: assignedclosed

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.

comment:10 by Jacob, 12 years ago

milestone: 1.0

Milestone 1.0 deleted

Note: See TracTickets for help on using tickets.
Back to Top