1
votes

I'm developing a registration app that use User model with OneToOneField.

models.py

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.db import models
from django.dispatch import receiver

class UserProfile(models.Model):
    GENDER_CHOICES = (
        ('M', 'Male'),
        ('F', 'Female'),
    )

    user = models.OneToOneField(User, on_delete=models.CASCADE)
    name = models.CharField('First name', max_length=30)
    surname = models.CharField('Last name', max_length=150)
    gender = models.CharField('Gender', max_length=1, choices=GENDER_CHOICES)
    date_of_birth = models.DateField('Date of birth', null=True)
    profile_image = models.ImageField('Profile image', upload_to='users_upload/profiles/%Y/%m/%d')
    company_name = models.CharField('Company Name', max_length=75, blank=True, null=True)
    fiscal_data = models.CharField('Fiscal data', max_length=200)
    city = models.CharField('City', max_length=150)
    address = models.CharField('Address', max_length=200)
    postcode = models.CharField('Zip Code', max_length=50)
    country = models.CharField('Country', max_length=75)
    telephone_number = models.CharField('Telephone', max_length=20)
    email_address = models.EmailField('Email')

class Meta:
    unique_together = ['fiscal_data', 'email_address']


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.userprofile.save()

forms.py

from django import forms
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

from .models import UserProfile

class UserForm(UserCreationForm):
    class Meta:
        model = User
        fields = ('username', 'password1', 'password2')

class UserProfileForm(forms.ModelForm):

    class Meta:
        model = UserProfile
        fields = [
            'name', 'surname', 'gender', 'date_of_birth',
            'user_type', 'profile_image', 'company_name',
            'address', 'postcode', 'city', 'country',
            'telephone_number', 'email_address',
            'fiscal_data',
            ]

views.py

from django.contrib.auth.forms import UserCreationForm
from django.db import transaction
from django.shortcuts import render

from .forms import UserProfileForm
from .models import UserProfile

@transaction.atomic
def createUser(request):
    if request.method == 'POST':
        user_form = UserCreationForm(request.POST)
        profile_form = UserProfileForm(request.POST or None, request.FILES or None)
        if user_form.is_valid() and profile_form.is_valid():
            user = user_form.save()
            user.refresh_from_db()  
            profile_form = UserProfileForm(request.POST, instance=user.userprofile) 
            profile_form.full_clean()  
            profile_form.save()  
    else:
        user_form = UserCreationForm()
        profile_form = UserProfileForm()
    return render(request, 'usermanager/editing/create_user.html', {
        'user_form': user_form,
        'profile_form': profile_form
    })

create_user.html

<form method="POST" enctype="multipart/form-data" novalidate>{% csrf_token %}

  <div class="card-body">
    {{ user_form.as_p }}
    {{ profile_form.as_p }}
  </div>

  <div class="card-footer">
    <div class="row justify-content-md-center">
      <div class="col-md-auto">
        <input type="submit" class="btn btn-danger shadow" value="Create Profile">
      </div>
    </div>
  </div>

</form>

When I try to register a new user using the form I see this error message:

UNIQUE constraint failed: usermanager_userprofile.email_address

This happen even if I try to register a new user using the administration panel.

How I can resolve this?

I've used this strategy to create my model, for for createUser view I've used this.

Traceback:

Environment:

Request Method: POST Request URL: http://127.0.0.1:8000/create-user/

Django Version: 2.2.5 Python Version: 3.6.8 Installed Applications: ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'kernel', 'usermanager'] Installed Middleware: ['django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware']

Traceback:

File "/home/max/Django/area-test/app_usermanager_dev/devenv/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner 34. response = get_response(request)

File "/home/max/Django/area-test/app_usermanager_dev/devenv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response 115. response = self.process_exception_by_middleware(e, request)

File "/home/max/Django/area-test/app_usermanager_dev/devenv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response 113. response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/lib/python3.6/contextlib.py" in inner 52. return func(*args, **kwds)

File "/home/max/Django/area-test/app_usermanager_dev/app_usermanager/usermanager/views.py" in createUser 30. profile_form.save() # Gracefully save the form

File "/home/max/Django/area-test/app_usermanager_dev/devenv/lib/python3.6/site-packages/django/forms/models.py" in save 453. 'created' if self.instance._state.adding else 'changed',

Exception Type: ValueError at /create-user/ Exception Value: The UserProfile could not be changed because the data didn't validate.

1
This means there is already such UserProfile with that email_address.Willem Van Onsem
This is impossible bacause I'm trying to add the first userMassimiliano Moraca
@MassimilianoMoraco: likely there is somehow already data in the database. Perhaps due to some triggers, other views? Can you inspect the db?Willem Van Onsem
I've deleted the DB before follow the indication of @ruddra , then the DB is total empty. Also before write this question I've do same thing.Massimiliano Moraca

1 Answers

3
votes

In your UserProfile model, there is a field:

email_address = models.EmailField('Email', unique=True)

When you add unique=True it means that field will be unique for all the entries in that model. You can either remove that keyword argument and run makemigrations and migrate. Or you need to enter unique email address for each UserProfile entry.


Update:

Probably the error is occurring because you are not validating the profile form when you initiated it second time:

profile_form = UserProfileForm(request.POST, instance=user.userprofile) 
profile_form.full_clean()  
# before calling save, you need to call profile_form.is_valid()
profile_form.save()  

TBH, I think you should remove your post save signal create_user_profile and save_user_profile, because it is over complicating your implementation of createUser method. Also, post save signals would be useful if you didn't have a mechanism to create a UserProfile but you have a form to do that, so why need a signal?

If you remove those signals, then try the following code:

def createUser(request):
    if request.method == 'POST':
        user_form = UserCreationForm(request.POST)
        profile_form = UserProfileForm(request.POST or None, request.FILES or None)
        if user_form.is_valid() and profile_form.is_valid():
            user = user_form.save()
         
            profile_form = profile_form.save(commit=False)
            profile_form.user = user
            profile_form.save()

FYI, you should use snake_case when naming methods as per pep8 standard. So its better if you name createUser to create_user.