Django Form not saving for Custom User Model - django

I have a register user form which is doing all the validation as expected. However, it is not saving. I am not able to figure out the reason. How do I debug it ? Any help ? I am a newbie to forms and formviews any good document with example would really help me.
class RegisterForm(forms.ModelForm):
phone_number = forms.IntegerField(required=True)
password1 = forms.CharField(widget=forms.PasswordInput())
password2 = forms.CharField(widget=forms.PasswordInput())
country_code = forms.IntegerField()
#schools = school.objects.all()
#school_name = forms.ModelChoiceField(queryset=school.objects.distinct())
MIN_LENGTH = 4
class Meta:
model = User
fields = ['username','country_code','phone_number', 'password1', 'password2',
'full_name' ]
def clean_phone_number(self):
phone_number = self.data.get('phone_number')
print(phone_number)
if User.objects.filter(phone_number=phone_number).exists():
raise forms.ValidationError(
_("Another user with this phone number already exists"))
if len(phone_number) == 10 and phone_number.isdigit():
pass
else:
raise forms.ValidationError(
_("Invalid Phone Number"))
return phone_number
def save(self, *args, **kwargs):
print("saving")
user = super(RegisterForm, self).save(*args, **kwargs)
user.set_password(self.cleaned_data['password1'])
print('Saving user with country_code', user.country_code)
user.save()
return user
Views.py
class RegisterView(SuccessMessageMixin, FormView):
template_name = 'register-2.html'
form_class = RegisterForm
success_message = "One-Time password sent to your registered mobile number.\
The verification code is valid for 10 minutes."
def form_valid(self, form):
full_name=self.request.POST["full_name"]
user = form.save()
print(user.id)
username = self.request.POST['username']
password = self.request.POST['password1']
user = authenticate(username=username, password=password)
kwargs = {'user': user}
self.request.method = 'POST'
print("User created")
The print in clean_phone_number works however, save does not work

I had issue in the my form. One of the field was disabled and the value was not captured because of that.
However to identify that I used
def form_invalid(self,form):
# Add action to invalid form phase
messages.error(self.request, form.errors)
return self.render_to_response(self.get_context_data(form=form))

Related

Object of type data is not JSON serializable error in Django

I have a registration page that allows a user to sign up. After doing so, I want to call an API and then, save the data to my model (not saving it to a form though). I tried doing this:
models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE, primary_key=True, related_name = 'profile')
address = models.TextField()
birthday = models.DateField()
def __str__(self):
return str(self.user)
views.py:
def signup(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
register_form = RegisterForm(request.POST)
if user_form.is_valid() and register_form.is_valid():
username = user_form.cleaned_data.get('username'),
first_name = user_form.cleaned_data.get('first_name'),
last_name=user_form.cleaned_data.get('last_name'),
email=user_form.cleaned_data.get('email'),
password=user_form.cleaned_data.get('password2'),
birthday = register_form.cleaned_data.get('dob'),
address=register_form.cleaned_data.get('address'),
payload = {'username': username,'first_name': first_name,'last_name': last_name,'email':email,'password':password,'register' : {'birthday': birthday,'address': address}}
response = requests.post('http://127.0.0.1:8000/my_api/',json=payload)
return redirect("home") #re-direct if login is successful
else:
user_form = UserForm()
register_form = RegisterForm()
return render(request, 'users/register.html', {'user_form': user_form, 'register_form': register_form})
class RegisterAPI(APIView):
permission_classes = [AllowAny]
def post(self, request, format=None):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
content = {'status': 'You are registered'}
return Response(content, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py:
from users.models import Profile
from django.contrib.auth.models import User
class ProfileSerializer(serializers.ModelSerializer):
birthday = serializers.DateField(format="%Y-%m-%d")
class Meta:
model = Profile
fields = ('birthday','address')
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('username','first_name','last_name','email', 'password', 'profile')
def create(self, request, validated_data, *args, **kwargs):
register_data = validated_data.pop('profile')
password = validated_data.pop('password', None)
user = User.objects.create(**validated_data)
if password is not None:
user.set_password(password)
user.save()
Profile.objects.create(user = user, **register_data)
return validated_data
However, I am getting this error:
Object of type data is not JSON serializable error in Django
It seems that it's got to do with the birthday. On my template, a user can display the date of birth as 'YYYY-MM-DD'. How can I fix this error?
The create method in your UserSerializer should return a User instance instead of validated_data.
def create(self, request, validated_data, *args, **kwargs):
register_data = validated_data.pop('profile')
password = validated_data.pop('password', None)
user = User.objects.create(**validated_data)
if password is not None:
user.set_password(password)
user.save()
Profile.objects.create(user = user, **register_data)
return user

The key "(User_id) = (51)" already exists

ERROR: repetitive record violates the singular constraint "user_otherinfo_user_id_key"
DETAIL: The key "(user_id) = (52)" already exists.
models.py
class OtherInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
phone = models.CharField(max_length=11,verbose_name="phone number")
location = models.CharField(max_length=50,verbose_name="location")
profile_image = models.FileField(blank = True,null = True,verbose_name="image")
class Meta:
verbose_name_plural = 'Other informations'
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
OtherInfo.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.otherinfo.save()
forms.py
class LoginForm(forms.Form):
username = forms.CharField(label = "Username")
password = forms.CharField(label = "Parola",widget = forms.PasswordInput)
class RegisterForm(forms.ModelForm):
password1 = forms.CharField(max_length=100, label='Parola',widget=forms.PasswordInput())
password2 = forms.CharField(max_length=100, label='Parola again', widget=forms.PasswordInput())
phone_number = forms.CharField(required=False, max_length=11, label='Phone Number')
location = forms.CharField(required=False, max_length=50, label='Location')
profile_image = forms.FileField(required=False, label="Image")
class Meta:
model = User
fields = [
'first_name',
'last_name',
'email',
'username',
'password1',
'password2',
'phone_number',
'location',
'profile_image',
]
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords do not match!")
return password2
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
class ProfileForm(forms.ModelForm):
class Meta:
model = OtherInfo
fields = ('phone', 'location', 'profile_image')
views.py
#login_required
#transaction.atomic
def updateprofile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, instance=request.user.otherinfo)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, _('Your profile has been successfully updated!'))
return redirect('/user/profile/')
else:
messages.error(request, _('Please correct the following error.'))
else:
user_form = UserForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.otherinfo)
return render(request, 'user/update_user.html', {
'user_form': user_form,
'profile_form': profile_form
})
def register(request):
form = RegisterForm(request.POST or None,request.FILES or None )
if form.is_valid():
user = form.save(commit=False)
first_name = form.cleaned_data.get('first_name')
last_name = form.cleaned_data.get('last_name')
username = form.cleaned_data.get("username")
email = form.cleaned_data.get('email')
password = form.cleaned_data.get('password1')
phone = form.cleaned_data.get('phone_number')
location = form.cleaned_data.get('location')
profile_image = form.cleaned_data.get('profile_image')
user.set_password(password)
user.save()
new_user = authenticate(username=user.username, first_name=first_name, last_name=last_name, email=email, password=password)
OtherInfo.objects.create(user=new_user,phone=phone,location=location,
profile_image=profile_image)
login(request,new_user)
messages.info(request,"Successfully Register ...")
return redirect("/")
context = {
"form" : form
}
return render(request,"user/register.html",context)
In Django, the user can register before updating the profile. When I add the profile update code, now the user is failing to register. How can I solve the problem?
It's simply a matter of not creating twice a OtherInfo for the same User:
in the post_save handler of saving a User, you create a OtherInfo object for the user.
in your view, you first create the User, hence you already create the OtherInfo associated with the user.
then you have OtherInfo.objects.create(user=new_user,phone=phone,location=location,
profile_image=profile_image)
This last instruction will fail, because you're trying to create a second OtherInfo for the same user. So you can do two things:
Remove the post_save signal handler that creates the OtherInfo object
or
Update new_user.otherinfo by setting the various values and saving it instead of creating it.
You need to delete the data entry with id=51.
The error you get is related to database, that the entry with id you are trying to create already exists.
Drop your database and create a new one(If you are not on a production environment)

Prevent form refresh on validation error django

I have a RegisterForm that inherits from ModelForm with RegisterView that inherits from FormView. If every field data is valid, the user gets successfully created and is redirected to login page. But if there is a validation error, it shows the field error below that field and the form gets refreshed and all the fields data is lost. How to avoid form refreshing so that user need not to fill the details again and again.
forms.py
class RegisterForm(forms.ModelForm, PasswordValidatorMixin):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField( label='Confirm password', widget=forms.PasswordInput)
class Meta:
model = UserModel
fields = (
'first_name',
'last_name',
'username',
'password1',
'password2',
'current_email',
)
def __init__(self, social_email=None, social_fname=None, social_lname=None,
social_uname=None,*args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs)
self.current_email = None
self.social_email = social_email
self.social_fname = social_fname
self.social_lname = social_lname
self.social_uname = social_uname
def clean(self, *args, **kwargs):
username = self.cleaned_data.get('username')
self.current_email = self.cleaned_data.get('current_email')
if self.social_email:
self.current_email = self.social_email
if not username:
raise forms.ValidationError({"username":"Username can't be empty"})
if not self.current_email:
raise forms.ValidationError({"current_email":"Email can't be empty"})
qs = UserModel.objects.filter(username=username)
qs_email = UserModel.objects.filter(current_email=self.current_email)
if qs.exists():
raise forms.ValidationError({"username":"Username is already taken"})
if qs_email.exists():
raise forms.ValidationError({"current_email":"Email has already been registered"})
return self.cleaned_data
def save(self, commit=True):
user = super().save(commit=False)
current_email = self.cleaned_data.get('current_email')
password = self.cleaned_data.get('password1')
user.set_password(password)
if self.social_email:
user.is_active = True
user.save()
return user
views.py
class RegisterView(ContextMixin, FormView):
form_class = RegisterForm
template_name = 'accounts/register.html'
title = 'Register'
#method_decorator(sensitive_post_parameters('password'))
#method_decorator(csrf_protect)
#method_decorator(never_cache)
def dispatch(self, *args, **kwargs):
self.kwargs['social_email'] = SOCIAL_USER_EMAIL
self.kwargs['social_fname'] = SOCIAL_USER_FNAME
self.kwargs['social_lname'] = SOCIAL_USER_LNAME
if SOCIAL_USER_EMAIL:
self.kwargs['social_uname'] = SOCIAL_USER_EMAIL.split('#',1)[0]
return super(RegisterView, self).dispatch(*args, **kwargs)
# Passes view kwargs to html
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if SOCIAL_USER_EMAIL:
context['social_email'] = self.kwargs['social_email']
context['social_fname'] = self.kwargs['social_fname']
context['social_lname'] = self.kwargs['social_lname']
context['social_uname'] = self.kwargs['social_uname']
# context['social_image'] = SOCIAL_USER_IMAGE
return context
# Passes view kwargs to form
def get_form_kwargs(self):
kwargs = super(RegisterView, self).get_form_kwargs()
kwargs.update(self.kwargs)
return kwargs
def form_valid(self, form):
form.save()
if not self.kwargs['social_email']:
return render(self.request, 'accounts/success.html', {
'title':"You've registered successfully",
'body':"You've successfully registered at antef! Please verify the link sent at " +
form.current_email
})
return render(self.request, 'accounts/success.html', {
'title':"You've registered successfully",
'body':"You've successfully registered with your " + self.kwargs['social_email'] + " account."})
First, you don't need validation error for empty inputs, just add required = True in your forms.py or in your model.
Second you are not returning anything after validation error, which making your form empty after refresh.
You can also check email and username separately, for better use,
def clean_email(self):
email = self.cleaned_data.get('email')
if your_condition:
raise forms.ValidationError()
return email
def clean_username(self):
username = self.cleaned_data.get('username')
if your_condition
raise forms.ValidationError
return username

Field validation in a Django form

I have a Django form consisting of a email and name field. I want to validate the name to have more than 8 characters. I have used the following code. But it is not working.
class SignUpForm(forms.ModelForm):
class Meta:
model=SignUp
fields=('email','name')
def emailValidation(self):
name=self.cleaned_data.get('name')
if len(name) <=8:
raise forms.ValidationError("name cannot be less than 8")
models.py
class SignUp(models.Model):
name=models.CharField(max_length=200)
email=models.EmailField()
timestamp=models.DateTimeField(auto_now_add=True, auto_now=False)
updated=models.DateTimeField(auto_now=True,auto_now_add=False)
def __unicode__(self):
return self.name
views.py
def home(request):
form=SignUpForm(request.POST or None)
if form.is_valid():
instance=form.save(commit=False)
instance.save()
print instance.timestamp
return render(request, 'home.html',{'form':form})
In your SignUpForm, in the function emailValidation, you haven't returned 'name'. Also a major mistake is that you have to name the function clean_(field_name) and NOT emailValidation.
This should do it I guess:
class SignUpForm(forms.ModelForm):
class Meta:
model=SignUp
fields=('email','name')
def clean_name(self):
name=self.cleaned_data.get('name')
if len(name) <=8:
raise forms.ValidationError("name cannot be less than 8")
return name
You need to use correct name for your validation method. Django forms will call methods with the format clean_<fieldname>.
Also you seem to be confused about which field you are validating; your email validation method should be called clean_email and should access the email value via form.cleaned_data['email'], and the name one should be called clean_name and access form.cleaned_data['name'].
Something like this may give you some guidance.
class RegistrationForm(forms.ModelForm):
"""
Form for registering a new account.
"""
firstname = forms.CharField(label="First Name")
lastname = forms.CharField(label="Last Name")
phone = forms.CharField(label="Phone")
email = forms.EmailField(label="Email")
password1 = forms.CharField(label="Password")
password2 = forms.CharField(label="Password (again)")
min_password_length = 8
class Meta:
model = User
fields = ['firstname', 'lastname', 'phone', 'email', 'password1', 'password2']
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise forms.ValidationError(u'Email "%s" is already in use! Please log in or use another email!' % email)
return email
def clean_password1(self):
" Minimum length "
password1 = self.cleaned_data.get('password1', '')
if len(password1) < self.min_password_length:
raise forms.ValidationError("Password must have at least %i characters" % self.min_password_length)
else:
return password1
def clean(self):
"""
Verifies that the values entered into the password fields match
NOTE: Errors here will appear in ``non_field_errors()`` because it applies to more than one field.
"""
cleaned_data = super(RegistrationForm, self).clean()
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise forms.ValidationError("Passwords didn't match. Please try again.")
return self.cleaned_data
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user

UserProfile Registration erases all rows

I have created a user registration view and this is it:
def register(request):
if request.method == 'POST':
data = request.POST.copy() # so we can manipulate data
# random username
data['username'] = hashlib.md5( data['email'] ).hexdigest()
data['username'] = data['username'][0:30]
#data['username'] = ''.join([choice(letters) for i in xrange(30)])
form = RegisterForm(data)
if form.is_valid():
new_user = form.save()
#UserProfile.objects.create(user=new_user)
return HttpResponse("Thanks for Registering")
else:
form = RegisterForm()
return render_to_response("CTUser/register.html", { 'form': form, })
When I uncommented the UserProfile line:
#UserProfile.objects.create(user=new_user)
the email and password are saved correctly, but when It is there, all the info is erased. Am I doing something wrong here?
here is the UserProfile classs:
class UserProfile(User):
user = models.OneToOneField(User)
#user = models.ForeignKey(User, unique=True)
#profile sub URL
pagelink = models.CharField(max_length=40)
#one or many albums
albums = models.ManyToManyField(Album)
UPDATE(10/19/11):
Here is the registration form function:
class RegisterForm(UserCreationForm):
email = forms.EmailField(label = "Email Address", max_length=75)
class Meta:
model = User
#exclude = ['username',]
fields = ("username", "email")
def clean_email(self):
email = self.cleaned_data["email"]
try:
user = User.objects.get(email=email)
raise forms.ValidationError("This email address already exists. Did you forget your password?")
except User.DoesNotExist:
return email
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.email = self.cleaned_data["email"]
user.is_active = True # change to false if using email activation
if commit:
user.save()
return user
Check if new_user contains all data you want to save. Paste RegisterForm code.
EDIT
try to change:
user = super(UserCreationForm, self).save(commit=False)
into
user = super(RegisterForm, self).save(commit=False)