Search code examples
pythondjangodjango-registration

How to Register different type user with profile in django?


I am new to django and I started a new project. shortly, I want to register different type of users (Teacher, Student, Stuff) with their profile picture. I made a one to one field from user to profile and below is my code and I get the following error. if you have any suggestion for my registration purpose, I want to register a different type of user with a profile picture.

models.py

from django.db import models
from django.contrib.auth.models import User,AbstractUser
from django.conf import settings
from django.dispatch import receiver
from django.db.models.signals import post_save

class User(AbstractUser):
    USER_TYPE_CHOICES = (
        (1,'Student'),
        (2,'Teacher'),
        (3,'Stuff')
    )
    profile = models.OneToOneField(on_delete=models.CASCADE, related_name='user')
    user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, related_name='profile')
    photo = models.ImageField(upload_to='users/%Y/%m/%d/')

    def __str__(self):
        return 'Profile {}'.format(self.user.username)

its the form

from django import forms
from django.contrib.auth.models import User
from .models import Profile

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

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

    def clean_password(self):
        cd = self.cleaned_data
        if cd['password']!=cd['password2']:
            raise forms.ValidationError('password no match')
        return cd['password2']


class ProfileCreationForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ('photo',)

its my view module

from django.shortcuts import render
from .forms import ProfileCreationForm, UserCreationForm
from .models import Profile
from django.http import HttpResponse
def sign_up(request):
    if request.method == 'POST':
        user_form = UserCreationForm(instance=request.user,data=request.POST)
        profile_form = ProfileCreationForm(instance=request.user.profile,data=request.POST,
                                           files=request.FILES)
        if user_form.is_valid() and profile_form.is_valid():
            new_user = user_form.save(commit=False)
            new_user.set_password(user_form.cleaned_data['password'])
            new_user.save()
            Profile.objects.create(user=new_user)
            return HttpResponse('user created')

    else:
        user_form = UserCreationForm()
        profile_form = ProfileCreationForm()
    return render(request, '',{'user_form':user_form, 'profile_form':profile_form})

here is the error:

Traceback (most recent call last):
  File "manage.py", line 15, in <module>
    execute_from_command_line(sys.argv)
  File "C:\Users\Mahdi\AppData\Local\Programs\Python\Python37\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "C:\Users\Mahdi\AppData\Local\Programs\Python\Python37\lib\site-packages\django\core\management\__init__.py", line 357, in execute
    django.setup()
  File "C:\Users\Mahdi\AppData\Local\Programs\Python\Python37\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "C:\Users\Mahdi\AppData\Local\Programs\Python\Python37\lib\site-packages\django\apps\registry.py", line 112, in populate
    app_config.import_models()
  File "C:\Users\Mahdi\AppData\Local\Programs\Python\Python37\lib\site-packages\django\apps\config.py", line 198, in import_models
    self.models_module = import_module(models_module_name)
  File "C:\Users\Mahdi\AppData\Local\Programs\Python\Python37\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "C:\Users\Mahdi\PycharmProjects\MyProject\accounts\models.py", line 7, in <module>
    class User(AbstractUser):
  File "C:\Users\Mahdi\PycharmProjects\MyProject\accounts\models.py", line 13, in User
    profile = models.OneToOneField(on_delete=models.CASCADE, related_name='user')
TypeError: __init__() missing 1 required positional argument: 'to'

Solution

  • In your model you forgot to specify to which model the OneToOneField of the User model should point (a Profile), since that profile is defined later in the Python file, you can not use an identifier, you can however use a string literal that contains the name of the model:

    class User(AbstractUser):
        USER_TYPE_CHOICES = (
            (1,'Student'),
            (2,'Teacher'),
            (3,'Stuff')
        )
        profile = models.OneToOneField('Profile', on_delete=models.CASCADE, related_name='user')
        user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)

    You will need to make migrations, and migrate in order to add a table that corresponds to your model in the database.

    Since you reference to the Profile, that means you will need to first construct a Profile before saving the User (since otherwise the profile will be NULL, which is not allowed here):

    from django.shortcuts import render
    from .forms import ProfileCreationForm, UserCreationForm
    from .models import Profile
    from django.http import HttpResponse
    
    def sign_up(request):
        if request.method == 'POST':
            user_form = UserCreationForm(instance=request.user,data=request.POST)
            profile_form = ProfileCreationForm(instance=request.user.profile,data=request.POST,
                                               files=request.FILES)
            if user_form.is_valid() and profile_form.is_valid():
                profile = profile_form.save()
                new_user = user_form.save(commit=False)
                new_user.set_password(user_form.cleaned_data['password'])
                new_user.profile = profile
                new_user.save()
                return HttpResponse('user created')
    
        else:
            user_form = UserCreationForm()
            profile_form = ProfileCreationForm()
        return render(request, 'my_template.html',{'user_form':user_form, 'profile_form':profile_form})

    You also forgot to specify the template. Furthermore it is advicable to return a redirect(..) to a view, since otherwise if the user refreshes the browser, a second POST request will be made, and this can thus trigger creating another user.