I want to create a Registration form which includes two models. One model is my custom model (Profile) and the other is the default User model in Django. I created two separate forms within the same template but the data is not successfully stored. This is what I had done so far:
models.py:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
company = models.CharField(max_length=100, blank=True, null=True)
address = models.TextField()
views.py:
def register(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
profile_form = ProfileForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
return redirect('login')
else:
user_form = UserForm()
profile_form = ProfileForm()
return render(request, 'register_page.html', {'user_form': user_form, 'profile_form': profile_form})
forms.py:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['company', 'address']
However, when I tried to register a new user, the data gets saved in the User model (username, email, password) but not in the Profile model (company, address).
I am getting this error instead:
RelatedObjectDoesNotExist at /
Profile has no user.
What should I do?
Since your Profile model is connected to the User model through OneToOne relation so you need to assign the user to your profile like this.:
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
profile = profile_form.save(commit = False)
# assign user to your profile
profile.user = user
profile.save()
return redirect('login')
Related
I have a model named profile and it has a one to one relationship with the User model. profile contains unnecessary information about the user like bio, gender, country, etc. So when the user signs up, they won't have a profile yet. When the user goes into settings and starts entering details, then they will have a profile.
That being said, how do I create a view in Django that can both create profile details about the user or update existing information.
Form
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = ['avatar', 'bio', 'gender', 'dob', 'country']
View
class SettingsView(FormView):
template_name = 'oauth/settings.html'
form_class = ProfileForm
success_url = reverse_lazy('oauth:settings')
redirect_field_name = "next"
Try this:
from django.views import View
from .forms import ProfileForm
from .models import Profile
class Settings(View):
form_class = ProfileForm
template_name = 'oauth/settings.html'
def post(self, request):
form = self.form_class(request.POST)
data = {'error':''}
if form.is_valid():
user = request.user
bio = form.cleaned_data['bio']
# more fields here
try:
profile = user.profile
# update user profile
profile.bio = bio
# add more fields here
profile.save()
data['response'] = 'profile updated!'
except:
profile = Profile(user=user,bio=bio) #add more fields
profile.save()
data['response'] = 'profile created!'
else:
data['error'] = 'Invalid form!'
return render(request, self.template_name,{'data':data})
Edit:
Or we can use update_or_create() as suggested by the OP in comments.
By replacing try and except blocks with:
updated_values = {'bio':bio} # add more fields
obj, created = Profile.objects.update_or_create(user=user,bio=bio, defaults=updated_values) # add more fields
if created:
data['response'] = 'profile created!'
else:
data['response'] = 'profile updated!'
I was trying to implement a basic login system using Django with a custom user using the AbstractUser class.
Here is my models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
class Stock(models.Model):
stock_name = models.CharField(max_length=10)
stock_price = models.FloatField()
def __str__(self):
return self.stock_name
class CustomUser(AbstractUser):
stocks = models.ManyToManyField(Stock)
def __str__(self):
return self.username
My forms.py:
from .models import CustomUser,Stock
from django.contrib.auth.forms import AuthenticationForm
class loginform(AuthenticationForm):
class Meta:
model = CustomUser
fields = ('username', 'password')
My views.py:
def successful_login(request, pk):
user = get_object_or_404(CustomUser, pk=pk)
return render(request, '../templates/stock_portfolio.html', {'user':user})
def loginview(request):
err=0
if request.method=="POST":
form = loginform(request.POST)
pdb.set_trace()
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(username=username, password=password)
if user is not None:
pdb.set_trace()
login(request, user)
pk = user.id
pdb.set_trace()
return redirect('successful_login', pk=pk)
else:
err=1
return render(request,'../templates/login.html',{'response':err,'form':form})
else:
form = loginform()
return render(request, '../templates/login.html',{'form':form})
While logging using pdb here is what I am getting for the form.
<loginform bound=False, valid=Unknown, fields=(username;password)>
How do I proceed now?
Answering this since I just had the same issue and found the problem. Change your
form = loginform(request.POST)
to
form = loginform(data=request.POST)
Worked like a charm for me.
I have a model named Profile which is created to extend the User auth model. I have created two forms one is UserForm and ProfileForm. In register.html template I show this two forms and wish to save in the database through the user.
But it constantly shows the exception: Integrity Error
NOT NULL constraint failed: core_profile.user_id
whenever I try to submit the post filling out all the fields and hit submit button.
Here are my models:
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
And here is my view for posting the forms:
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, authenticate
from django.shortcuts import render, redirect
from .forms import SignUpForm, ProfileForm
#login_required
def home(request):
return render(request, 'home.html')
def signup(request):
if request.method == 'POST':
user_form = SignUpForm(request.POST)
profile_form = ProfileForm(request.POST)
if user_form.is_valid():
user = user_form.save()
profile_form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.profile.birth_date = user_form.cleaned_data.get('birth_date')
user.save()
raw_password = user_form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('home')
else:
user_form = SignUpForm()
profile_form = ProfileForm()
return render(request, 'signup.html', {'user_form': user_form, 'profile_form': profile_form})
And here are the forms:
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import Profile
class SignUpForm(UserCreationForm):
birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')
class Meta:
model = User
fields = ('username', 'password1', 'password2', 'birth_date')
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('bio', 'location')
Thank you,
When you are trying to save the profile_form, it doesn't know to which user it is related to. And in your case, when you save the user form, it will create the profile, and what you need to do is just update the profile of you saved user, so I suggest something like:
def signup(request):
if request.method == 'POST':
user_form = SignUpForm(request.POST)
profile_form = ProfileForm(request.POST)
if user_form.is_valid():
user = user_form.save()
user.profile.bio = profile_form.cleaned_data.get('bio')
user.profile.location = profile_form.cleaned_data.get('location')
user.profile.save()
...
In addition to #Gagik Sukiasyan's answer: I added some additional things to reduce errors / ease your life:
transaction.atomic -> if errors occur, the database is being rolled back
and profile_form.is_valid() makes sure profile_form is validated
instead of going through the profile attributes manually I added a loop
Modified Code:
from django.db import transaction
#transaction.atomic
def register(request):
""" register a new user view """
if request.method == 'POST':
user_form = UserRegisterForm(request.POST)
profile_form = ProfileForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
for field in profile_form.changed_data:
setattr(user.profile, field, profile_form.cleaned_data.get(field))
user.profile.save()
I have an account app in which I have created a Profile model by extending the custom user model. I have created a view which allows the user to edit his profile info and also I have corresponding UserEditForm and ProfileEditForm. As of now, no user has a profile so when I open the edit form I get an error: "RelatedObjectDoesNotExist at /account/edit/".
" User has no profile "
I tried to create the profile using admin , then the error goes away. How can I correct this in my views.py file.
views.py
#login_required
def edit(request):
if request.method =='POST':
user_form = UserEditForm(instance=request.user,data=request.POST)
profile_form = ProfileEditForm(instance=request.user.profile,data=request.POST,files=request.FILES)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request,'Profile updated successfully')
else:
messages.error(request,'Error updating your profile')
else:
user_form = UserEditForm(instance=request.user)
profile_form = ProfileEditForm(instance=request.user.profile)
context = {
'user_form':user_form,
'profile_form': profile_form
}
return render(request,'account/edit.html',context)
models.py
CATEGORY_CHOICES = (
('SA','School Admin'),
('T','Teacher'),
('S','Student'),
('P','Parent'),
)
class Profile(models.Model):
eduser = models.OneToOneField(settings.AUTH_USER_MODEL)
photo = models.ImageField(upload_to='users/%Y/%m/%d',blank=True)
about_me = models.TextField(max_length=200,blank=True)
category = models.CharField(max_length=1,choices=CATEGORY_CHOICES,blank=True)
date_of_birth = models.DateField(blank=True,null=True)
def __str__(self):
return 'Profile for user {}'.format(self.eduser.username)
forms.py
class UserEditForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name','last_name','email')
class ProfileEditForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('category','date_of_birth','about_me','photo')
You need to catch the error. You can do it at the top of the function:
try:
profile = request.user.profile
except ObjectDoesNotExist:
profile = Profile(user=request.user)
and pass that profile into the ProfileEditForm in both if branches.
I have created a custom User registration form, from the UserCreationForm. When I try to register, it does register successfully, and I can see a newly created user with the username and its email. But there's no password for that user.
In the admin, the password field for that user is No password set.. Please correct me where I am wrong. Thank you.
forms.py:
from album.forms import MyRegistrationForm
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2',)
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
views.py:
def register_user(request):
if request.method == "POST":
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success/')
else:
form = MyRegistrationForm()
return render(request, 'register.html', {'form':form})
When calling save on the superclass using super, use the form MyRegistrationForm, not its superclass UserCreationForm.
user = super(MyRegistrationForm, self).save(commit=False)