14

I'm using exclude in my form's Meta class to exclude a field from my form that I want to fill in programatically, but it's still showing up in the form.

Here are some excerpts from the code:

# Model
class Info(models.Model):
    completed_by = models.ForeignKey(User, related_name='+')

# Form
class InfoForm(forms.ModelForm):
    class Meta:
        model = Info
        exclude = ('created_by',)  #ETA: added comma to make this a tuple
        widgets = {
            'some_other_field': forms.HiddenInput(),
            'some_other_field2': forms.DateInput(attrs={'readonly': True}),
        }

# View
form = InfoForm(initial={'some_other_field': value}, 
                          prefix='info', instance=info)
return direct_to_template(request, 'myapp/info.html', locals())

# Template
<form class='uniForm' method='POST'>
{% csrf_token %}
<fieldset class='inlineLabels'>{{ form|as_uni_form }}</fieldset>
<input type='submit' name='action' value='Save' />
</form>

This seems like it should be pretty simple, and I know I've done it successfully before. I've deleted/recreated my DB and cleared my browser cache, just to be sure that's not a factor. I also tried making it a HiddenInput field, just like some_other_field (which is a ForeignKey field also), but it still shows up on the form.

Is there something here I'm missing? Is uni_form overriding the setting somehow? If not, any advice as to what I might look for in debug to see where/why this is happening?

(Using Django version 1.2.7)

Ennael
  • 861
  • 3
  • 14
  • 31

3 Answers3

16

exclude needs a tuple, so you need

# note the extra comma
exclude = ('created_by',)

django iterates through the exclude, and since strings are iterable (return each character) this doesn't throw an error

second
  • 28,029
  • 7
  • 75
  • 76
  • You're right, that is supposed to be a tuple. Thanks. The field is still showing up in the form after that fix, though. – Ennael Nov 15 '11 at 16:09
  • The tuple + correcting `completed_by`/`created_by` (as you recommended above) did it. Thanks again. – Ennael Nov 15 '11 at 16:35
  • 9
    Well, I'm out of luck here guys. I excluded the field and made sure there was comma after the field. The field still gets rendered. – Shailen Aug 16 '13 at 15:37
3

For older Django versions, there's a problem with excluding non-model fields that you explicitly declare, e.g. in a parent form class. Adding the following in your form's init will take care of it even for non-model fields (see https://code.djangoproject.com/ticket/8620).

def __init__(self, *args, **kwargs):
    super(MyForm, self).__init__(*args, **kwargs)
    [self.fields.pop(f) for f in self.fields.keys() if f in self.Meta.exclude]
Felix Böhme
  • 508
  • 5
  • 12
  • 1
    Thanks! That works also for inherited ModelForm like my case UserForm of wagtail. – Dat TT Mar 15 '18 at 08:47
  • 1
    This worked for me also, though I needed to make `self.fields.keys()` a list to be able to mutate whilst iterating (in list comprehension) – autopoietic Aug 08 '19 at 10:57
0

I know this is old but posting here as a reference.

I encountered this same issue because I overloaded clean_fields() on the model, but didn't call the superclass correctly.

 class MyModel(models.Model):
    foo = models.BooleanField()    

    # Wrong!  Don't do this!
    def clean_fields(self, exclude=None):
        # Bah!  Forgetting to pass exclude defaults it to None!
        super(MyModel, self).clean_fields()
        if not self.foo:
            raise ValidationError('Ah fooey.')

    # Correct!  Do it this way!
    def clean_fields(self, exclude=None):
        # That's better!  Let's take a coffee break.  I'm tired.
        super(MyModel, self).clean_fields(exclude)
        if not self.foo:
            raise ValidationError('Ah fooey.')
pyrospade
  • 7,870
  • 4
  • 36
  • 52