1

I want to add an instance parameter to my forms.Form, this way I can initialize it in views and get the expected result in my forms. I know i can use 'initial', but I wanted to try something different.

Views.py

city_form = CityForm(instance=user.profile.city)

Forms.py

class CityForm(forms.Form):
    post_nr = forms.CharField(max_length=5, widget=forms.TextInput(attrs={'placeholder': 'Post nr.'}))
    city = forms.CharField(required=False, max_length=30, widget=forms.TextInput(attrs={'placeholder': 'By', 'readonly': True}))

    class Meta:
        model = City

    def __init__(self, *args, **kwargs):
        self.instance = kwargs.pop('instance', None)

        if self.instance:
            self.fields['post_nr'] = self.instance.post_code
            self.fields['city'] = self.instance.name

        super(CityForm, self).__init__(*args, **kwargs)

The referenced instance will be a City object, from my City class which has the variables I specify above.

But I am getting:

'CityForm' object has no attribute 'field'

2 Answers 2

5

A form doesn't have a fields attribute until you call super().__init__(). You need to move that code down. What you're currently doing is overriding the field instance with its value. Instead you should set the initial value:

def __init__(self, *args, **kwargs):
    instance = kwargs.get('instance', None)

    super(CityForm, self).__init__(*args, **kwargs)

    if instance:
        self.initial['post_nr'] = instance.post_code
        self.initial['city'] = instance.name

You might also want to take a look at using a ModelForm.

Sign up to request clarification or add additional context in comments.

8 Comments

Yes, I thought of it. However when I add the super method before my code, my fields simply vanish. My instance is empty, but shouldn't it create the fields anyway?
Did you move the super() up, or did you add a second call? The latter would explain your fields vanishing.
I did exactly as you showed and the fields vanished. It could have something with my code, because if I remove my condition down there, they come back. I guess it is replacing the field code instead of the value inside of it.
You're overriding the field instance with the value of the field. It is actually quite complex to change the value of the field after instantiating the form, you should rather modify the data or initial dicts before instantiating.
Also, I don't want to use ModelForm because I don't want to save information with this form. I just want to fetch data with a JavaScript function I have. This method is going to throw back the validated instance of the City the user typed in the field.
|
0

the answer posted by @knbk solves the OP's problem perfectly. but in user scenario, here are some improvements I'd like to add.

def __init__(self, *args, **kwargs):
    instance = kwargs.pop('instance', None)
    super(CityForm, self).__init__(*args, **kwargs)
    if instance:
        self.instance = instance
        if self.data.get('title', None) is None:
            self.initial['title'] = instance.title
  1. by inheriting forms.Form, use pop to get instance because the keyword is not expected by forms.Form constructor.

  2. if instance is passed in, assign it to the form so that you can use it in save method etcetera.

  3. always check whether request.POST is passed in, if the initial is not included in data, this means you are not submitting a update form within an instance, only then you should overwrite initial, otherwise the data will never be updated.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.