I am creating an art marketplace with django. I have the registration and the auto-create signal set up for the profile. There are three types of Users who will register on this app: Artists, Patrons, and Brokers (I call these user_status.) I would like the User to choose one of these three fields on the registration form. I have gotten to the point where the reg form has the choicefield and the choices and the user can select one and the form saves to create the User and Profile, but the profile does not have a selected 'user_status' when I go in to admin.
I am aware it is heavily discouraged to bundle up profile modelfields at registration, but I'd like for just this one field to be selectable since it will have a major effect on how the site will look for the user. I have read (here) that there would be a lot of annoying customization involved to properly fill profile fields at registration. Is this true?
I think if there is a solution it'll be on Signals.py but am not sure.
In signals.py when I type in:
user_status='Patron',
Can I replace the 'Patron' with something like instance.get('user_status')
? I know there are something called 'defaults' to use here as well but I am unfamiliar.
user/signals.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
# This functions but cannot get it to grab the form chosen field from the user.
# just default creates status as patron for now :(
Profile.objects.update_or_create(
user=instance,
user_status='Patron',
)
@receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
user/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from users.models import Profile
STATUS_CHOICES = (
('Artist', 'Artist'),
('Patron', 'Patron'),
('Broker', 'Broker')
)
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
user_status = forms.ChoiceField(choices=STATUS_CHOICES)
class Meta:
model = User
fields = ['username', 'email', 'user_status', 'password1', 'password2']
users/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import UserRegisterForm
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Hello {username}! Your account has been created. You are now ready to log in.')
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form': form})
@login_required
def profile(request):
return render(request, 'users/profile.html')
user/models.py
class Profile(models.Model):
STATUS_CHOICES = (
('Artist', 'Artist'),
('Patron', 'Patron'),
('Broker', 'Broker')
)
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
user_slug = models.SlugField(blank=True)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
user_status = models.CharField(max_length=100, choices=STATUS_CHOICES)
def __str__(self):
return f'{self.user.username} Profile'
Edit reflecting 1st recommendation:
Views.py
def register(request):
if request.method == 'POST':
# create profile object but don't save it to db
profile = ProfileSetupForm.save(request.POST)
profile = profile_setup_form.save(commit=False)
user_form = UserRegisterForm(request.POST)
if user_form.is_valid():
# create user object
user = user_form.save(commit=False)
# set profile attribute of user object
user.profile = profile
# save user - calls post_save
user.save()
username = user.username
messages.success(request, f'Hello {username}! Your account has been created. You are now ready to log in.')
return redirect('login')
else:
user_form = UserRegisterForm()
profile_setup_form = ProfileSetupForm()
return render(
request,
'users/register.html',
{
'user_form': user_form,
'profile_setup_form': profile_setup_form
}
)
signals.py
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.update_or_create(
user=instance,
user_status=instance.profile.user_status,
)
register.html
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
{{ user_form|crispy }}
{{ profile_setup_form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Sign Up</button>
</div>
</form>
<div class="border-top pt-3">
<small class="muted">
Already have an account? <a class="ml-2" href="{% url 'login' %}">Sign In</a>
</small>
</div>
</div>
{% endblock content %}
Error Code
Traceback (most recent call last):
File "/Users/Tom/Desktop/art_project/art_env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/Users/Tom/Desktop/art_project/art_env/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/Tom/Desktop/art_project/users/views.py", line 12, in register
profile = ProfileSetupForm.save(user_status, request.POST)
File "/Users/Tom/Desktop/art_project/art_env/lib/python3.8/site-packages/django/forms/models.py", line 451, in save
if self.errors:
Exception Type: AttributeError at /register/
Exception Value: 'DeferredAttribute' object has no attribute 'errors'
Here is the error code if i take that 'user_status' out so it's your suggested code:
Traceback (most recent call last):
File "/Users/Tom/Desktop/art_project/art_env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/Users/Tom/Desktop/art_project/art_env/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/Tom/Desktop/art_project/users/views.py", line 12, in register
profile = ProfileSetupForm.save(request.POST)
File "/Users/Tom/Desktop/art_project/art_env/lib/python3.8/site-packages/django/forms/models.py", line 451, in save
if self.errors:
Exception Type: AttributeError at /register/
Exception Value: 'QueryDict' object has no attribute 'errors'
Adjusted forms.py file
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from users.models import Profile
STATUS_CHOICES = (
('Artist', 'Artist'),
('Patron', 'Patron'),
('Broker', 'Broker')
)
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
# user_status = forms.ChoiceField(choices=STATUS_CHOICES)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class ProfileSetupForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['user_status']
Current views.py file with adjustments from comments
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import UserRegisterForm, ProfileSetupForm
def register(request):
if request.method == 'POST':
# create profile object but don't save it to db
profile_setup_form = ProfileSetupForm.save(request.POST)
profile = profile_setup_form.save(commit=False)
user_form = UserRegisterForm(request.POST)
if user_form.is_valid():
# create user object
user = user_form.save(commit=False)
# set profile attribute of user object
user.profile = profile
# save user - calls post_save
user.save()
username = user.username
messages.success(request, f'Hello {username}! Your account has been created. You are now ready to log in.')
return redirect('login')
else:
user_form = UserRegisterForm()
profile_setup_form = ProfileSetupForm()
return render(
request,
'users/register.html',
{
'user_form': user_form,
'profile_setup_form': profile_setup_form
}
)