3

I am trying to extend Djangos authentication system and have a problem when trying to create the modelForm for it. As you can see below I have referenced the authentication backend via the suggested OneToOnefield however when I am creating the ModelForm if I try to reference the fields such as 'username', 'password' etc. it spits an error saying they are unknown fields. The form I am creating is a registration from. What am I doing wrong here? Cheers

Model -

class StudentModel(models.Model):
    user = models.OneToOneField(User, unique=True)
    birth_date = models.DateField()
    contact_number = models.IntegerField()
    referral = models.CharField(max_length=100, choices=referral_choices)

ModelForm -

from django import forms
from opus_login.models import StudentModel, EmployerModel

class StudentForm(forms.ModelForm):

    class Meta:
        model = StudentModel
        fields = ['username', 'first_name']

Error -

django.core.exceptions.FieldError: Unknown field(s) (username, first_name) specified for StudentModel
eZ_Harry
  • 816
  • 9
  • 25
  • Those fields are on the User model, do you not just need your modelform to that model? `birth_date` and the other fields can't be null so showing those two fields on their own wouldn't work anyway – Sayse Feb 09 '16 at 08:55
  • The ModelForm has access only to the model's Fields. http://stackoverflow.com/questions/27832076/modelform-with-onetoonefield-in-django – Aviah Laor Feb 09 '16 at 08:58

2 Answers2

5

A common solution when you need to extend the User model with another model is use two ModelForms: one for User and another for extending model (Student in your case). In the first you access the needed fields of the User model, in the second those of the Student model.

class UserForm(forms.ModelForm):
    password = forms.CharField(label='Password',widget=forms.PasswordInput)
    password2 = forms.CharField(label='Repeat password',widget=forms.PasswordInput)

    class Meta:
    model = User
    fields = ('username', 'first_name')

    def clean_password2(self):
        .......
        return password2

class StudentForm(forms.ModelForm):
    class Meta:
        model = StudentModel
        fields = ['birthdate', 'contact_number']

Then, in the view, you work with two forms instead of one. For example:

def register(request):
    if request.method == 'POST':
        user_form = UserForm(request.POST)
        student_form = StudentForm(request.POST)
        if user_form.is_valid() and student_form.is_valid():
            user_form.save()
            student_form.save()

And in your template you combine both forms in one:

<form action="." method="post">
  {{ user_form.as_p }}
  {{ student_form.as_p }}
  {% csrf_token %}
  <p><input type="submit" value="Register"></p>
</form>
doru
  • 9,022
  • 2
  • 33
  • 43
  • Hello, I have been trying to use your method as it seemed the most logical imo. However I have come across one problem being that you cannot modify the User model, for example I plan to add a clean() function for the password field however this wouldn't be possible with this method. Any suggestions? – eZ_Harry Feb 10 '16 at 03:25
  • @eZ_Harry I've edited the answer. You don't have to modify the `User` model because it has a password field. – doru Feb 10 '16 at 08:23
2

You can't directly access One2One fields like this. You need to first create a object of User and add to One2One relation. You can try like this:

from django import forms
from opus_login.models import StudentModel, EmployerModel

class StudentForm(forms.ModelForm):
    username = forms.CharField()
    first_name = forms.CharField()

    class Meta:
        model = StudentModel
        fields = ['__all__']

    def save(self, **kwargs):
        student = super().save(commit=False)
        user = User.objects.create(username=self.cleaned_data['username'], first_name=self.cleaned_data['first_name'])
        user.set_password(self.cleaned_data['password']) #if there is a password field
        student.user = user
        student.save(commit=True)
        return student
ruddra
  • 50,746
  • 7
  • 78
  • 101
  • I suppose `save()` method would raise an error when calling `super().save()` first, since it is a `ModelForm` and those fields are not in the model. – zaidfazil Jul 23 '17 at 15:44