Opened 7 years ago

Closed 7 years ago

Last modified 4 years ago

#7006 closed (invalid)

Overridden fields/widgets for ForeignKeys in ModelForms

Reported by: Simon Litchfield <simon@…> Owned by: msaelices
Component: Forms Version: master
Severity: Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

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 Changed 7 years ago by mikeblake

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

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 follow-up: Changed 7 years ago by mikeblake

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 in reply to: ↑ 2 Changed 7 years ago by mikeblake

  • milestone set to 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 Changed 7 years ago by mtredinnick

  • milestone changed from 1.0 beta to 1.0

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

comment:5 Changed 7 years ago by ericholscher

  • Triage Stage changed from Unreviewed to Accepted

comment:6 Changed 7 years ago by ubernostrum

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 Changed 7 years ago by msaelices

  • Owner changed from nobody to msaelices
  • Status changed from new to assigned

comment:8 Changed 7 years ago by msaelices

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 Changed 7 years ago by msaelices

  • Resolution set to invalid
  • Status changed from assigned to 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.

comment:10 Changed 4 years ago by jacob

  • milestone 1.0 deleted

Milestone 1.0 deleted

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