Higure
Higure

Reputation: 235

Set form's default values from existing objects in Django

I'm trying to create a page of my Django 1.5 project where users can change their presonal informations. I wrote a form:

class UserModify(forms.Form):
    middleschool = 'MS'
    highschool = 'HS'
    university = 'U'
    blank = ''
    male = 'M'
    female = 'F'
    mastercard = 'MC'
    maestro = 'M'
    visa = 'V'
    americanexpress = 'A'

    school_choices = ((middleschool, 'Middle School'), (highschool, 'High school'), (university, 'University'), (blank, 'Not Defined'),)
    sex = ((male, 'Male'), (female, 'Female'), (blank, 'Not defined'),)
    card_choices = ((mastercard, 'MasterCard'), (maestro, 'Mestro'), (visa, 'Visa/Visa Electron'), (americanexpress, 'American Express'),(blank, 'Not Defined'))

    password = forms.CharField(label = 'Password', widget=forms.PasswordInput)
    repassword = forms.CharField(label = ' Reinstert password', widget=forms.PasswordInput)
    name = forms.CharField(label = 'Name', required=False)
    surname = forms.CharField(label = 'Surname', required=False)
    school = forms.ChoiceField(choices = school_choices, required=False, label='What school are you enrolled?', initial = blank) 
    birthdate = forms.DateField(label = 'Birth date', required=False, widget=forms.extras.SelectDateWidget(years=range(1950, 2015)))
    sex = forms.ChoiceField(choices = sex, required=False, label='Sex', initial = blank)
    city = forms.CharField(label = 'City', required=False)
    paypal_info = forms.EmailField(required=False,)
    card_type = forms.ChoiceField(choices = card_choices, label='Card type', required=False)
    card_info = forms.CharField(min_length = 16, max_length = 16, label = 'Your card number', required=False,)
    def clean_repassword(self):
        repassword = self.cleaned_data['repassword']
        password = self.cleaned_data['password']
        if repassword != password:
            raise forms.ValidationError("Verification password different from original password!")
    widgets = {
            'password': forms.PasswordInput(),
            'birthdate': forms.extras.SelectDateWidget(years=range(1950, 2014))
    }

and this is the relative view:

def usermodify(request, user):
    mod_user = User.objects.get(username = user)
    if request.method == 'POST':
        form = UserModify(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            mod_user.password = cd['password']
            mod_user.first_name = cd['name']
            mod_user.last_name = cd['surname']
            mod_user.save()
            profile = mod_user.get_profile()
            profile.school = cd['school']
            profile.birthdate = cd['birthdate']
            profile.sex = cd['sex']
            profile.city = cd['city']
            profile.save()
            return render(request, 'user.html', {'request':request, 'view_user': mod_user, 'view_user_profile': profile})
    else:
        form = UserModify()
    return render(request, 'user_modify.html', {'form': form, 'request': request, 'modify_user': mod_user})

The form's rendering is as simple as it can get for now:

<form action="" method="post">
{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Modify">
    <input type="reset" value="Reset">
</form>

What I wantetd to do is populate the form by default with the current values in the object so that the User have to change only the one in which is interested and keep the others.

Upvotes: 0

Views: 1397

Answers (3)

Higure
Higure

Reputation: 235

For future reference: I solved using Initial when rendering the form as showed here

Upvotes: 0

sk1p
sk1p

Reputation: 6735

Use a ModelForm instead of a regular Form. A ModelForm can be associated with an instance, which is the used to prepopulate the form. If you want to update both the User and the profile, you can use two ModelForms in one view. For example:

class UserModelForm(forms.ModelForm):
    repassword = ... # additional fields that have no represenation in the model

    class Meta:
        model = User
        fields = ["username", "password", ]  # ...

    def clean(self):
        # ... your validation logic

class ProfileForm(forms.ModelForm):
    class Meta:
        model = YourProfileModel
        fields = []  # ...

Then use both in the view:

def usermodify(request, user):
    # mod_user = ...

    user_form = UserModelForm(request.POST or None, instance=mod_user)
    profile_form = ProfileForm(request.POST or None, instance=mod_user.get_profile())
    if user_form.is_valid() and profile_form.is_valid():
        user_form.save()  # this saves to the associated mod_user, as passed via the `instance` argument
        profile_form.save() 
        # ... redirect etc.
    return render(..., {'user_form': user_form, 'profile_form': profile_form})  # pass both forms to the template

In the template, put both forms inside of one <form> tag. Be sure that they don't have conflicting field names, or use a prefix.

Upvotes: 1

Aaron Lelevier
Aaron Lelevier

Reputation: 20780

  1. If you are trying to get default values in the object. You can set that in your model, and these will carry over to your forms, and populate. Example:

    school_choices = models.CharField(blank=True, default='HS')

  2. Combine that with using Django's CBV's (Class Based Views) and the UdpdateView:

    https://docs.djangoproject.com/en/dev/ref/class-based-views/generic-editing/#updateview

The UpdateView will pre-populate your forms for you.

Upvotes: 0

Related Questions