Search code examples
djangodjango-modelsdjango-formsdjango-viewsdjango-users

adding extra field ('city') to UserRegisterForm Django


When new users register I want to store:

fields = ['username', 'email', 'password1', 'password2', 'city']

so I extended UserRegisterForm by adding 'city' to the form.

It renders fine in the template and save everything except 'city'. There is no even column 'city' in the new users profile when checking by admin page so looks like its not creating one.

I found few similar posts and been following Doc but that didint help.

Have tried many different ways but will post two I think mostly sensible ones.

EXAMPLE 1


 - *forms.py*
...
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm

class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()
    city = forms.CharField(required=True)

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2', 'city']

    def save(self, commit=True):
        user = super(UserRegisterForm, self).save(commit=False)
        user.city = self.cleaned_data['city']

        if commit:
            user.save()
        return user


 - *views.py*
...
from django.contrib.auth.forms import UserCreationFormfrom
from .forms import UserRegisterForm

def register(request):
    if request.method == 'POST':
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            form.save()
            print('VALID')
            username = form.cleaned_data.get('username')
            messages.success(request, 
                '{} Your account has been created! You can now Log In'.format(username))
            return redirect('/login/')
    else:
        form = UserRegisterForm()

    context = {
        'form': form,
    }
    return render(request, 'users/register.html', context)


@login_required
def profile(request):
    return render(request, 'users/profile.html')


 - *template*
...
<form method="POST">
    {% csrf_token %}
    {{ form|crispy }}
    <!-- {{ form2 }} -->
    <button class="btn-signup" type="submit">Sign Up</button>
</form>

In Example 2 Iam creating new class 'ProfileForm' with new model as separate form and including it in the views.py in one function with UserRegisterForm.

EXAMPLE 2


 - *models.py*
...
class Profile(models.Model):
    city = models.CharField(max_length=25, blank=False)

    def __str__(self):
        return self.city


 - *forms.py*
...
class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()
    city = forms.CharField(required=True)

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2', 'city']

class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ['city']

    def save(self, commit=True):
        user = super(UserRegisterForm, self).save(commit=False)
        user.city = self.cleaned_data['city']

        if commit:
            user.save()  
        return user


 - *views.py*
...
from django.contrib.auth.forms import UserCreationFormfrom
from .forms import UserRegisterForm, ProfileForm

def register(request):
    if request.method == 'POST':
        form = UserRegisterForm(request.POST)
        form2 = ProfileForm(request.POST)
        if form.is_valid() and form2.is_valid():
            form.save()
            form2.save()
            print('VALID')
            username = form.cleaned_data.get('username')
            messages.success(request, 
                '{} Your account has been created! You can now Log In'.format(username))
            return redirect('/login/')
    else:
        form = UserRegisterForm()
        form2 = ProfileForm()
    context = {
        'form': form,
        'form2': form2
    }
    return render(request, 'users/register.html', context)


 - *template*
...
<form method="POST">
    {% csrf_token %}
    {{ form|crispy }}
    {{ form2 }}
    <button class="btn-signup" type="submit">Sign Up</button>
</form>

Solution

  • Your Profile model should have a OneToOne relation with the User model like this:

        Class Profile (models.Model):
            user = models.OneToOneField (User,on_delete=models.CASCADE)  
            city = models.CharField (max_length=25,blank=False)
    

    You don't need to define ProfileForm.You can create profile objects for the user like this.

        form = UserRegisterForm (request.POST)
        if form.is_valid ():
             city = form.cleaned_data ['city']
             user = form.save ()
             Profile.objects.create (user=user,city=city)
             return redirect ('some_view)