Django form save and update model fields in view - django

I have SetPasswordForm that only sets user password
class SetPasswordForm(forms.Form):
password = forms.CharField(label="Password", widget=forms.PasswordInput)
def __init__(self, user, *args, **kwargs):
self.user = user
super(SetPasswordForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
self.user.set_password(self.cleaned_data['password'])
if commit:
self.user.save(update_fields=['password'])
return self.user
and User model that has activate() method to make new user activate after setting a password
class User(BaseUser):
activation_code = models.CharField(max_length=100, blank=True, null=True)
activated_at = models.DateTimeField(blank=True, null=True)
def activate(self):
self.is_active = True
self.activation_code = None
self.activated_at = datetime.now()
self.save(update_fields=['is_active', 'activation_code', 'activated_at'])
In view, when user submits a form, It should sets new password and activates user
class ActivateUserView(View):
def post(self, request, activation_code):
try:
user = User.objects.get(activation_code=activation_code)
except User.DoesNotExist:
return Http404()
form = SetPasswordForm(user=user, data=request.POST)
if form.is_valid():
user = form.save(commit=False)
user.activate()
return render(request, 'users/activate_user_done.html', {'user': user})
return render(request, self.template_name, {'user': user})
Question is I don't know where to call user.activate()?
In form or in view?
I don't think form.save() should also calls this method because form will not be reusable in other places.

Call user.activate before form.save because you only want to save the user if he has activated his account.

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

UNIQUE constraint failed: auth_user.username while updating user info

I am trying to give a user the option to change his/her first/last name through a ModelForm. When I press submit, I get hit with the UNIQUE constraint failed: auth_user.username error. Here are my codes:
students/forms.py:
class EditProfileForm(UserChangeForm):
def clean_password(self):
# Overriding the default method because I dont want user to change
# password
pass
class Meta:
model = User
fields = (
'first_name',
'last_name',
)
students/views.py:
User = get_user_model()
def student_profile_view(request, slug):
if request.method == 'GET':
# forms
edit_name_form = EditProfileForm(instance=request.user)
context = {
'edit_name_form': edit_name_form,
}
return render(request, "students/profile.html", context)
class ChangeNameView(SuccessMessageMixin, UpdateView):
template_name = 'students/edit_profile.html'
model = User
form_class = EditProfileForm
success_message = "Your name has been updated"
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
form.instance.student_profile = StudentProfile.objects.get(slug=request.user.student_profile.slug)
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
"""If the form is valid, save the associated model."""
form.instance.username = self.request.user
self.object = form.save(commit=False)
return super().form_valid(form)
def get_success_url(self):
return reverse('students:student_profile_view', kwargs={'slug': self.object.student_profile.slug})
also fyi, User model is foreign key with StudentProfile.
students/models.py:
class StudentProfile(models.Model):
user = models.OneToOneField(User, related_name='student_profile', on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
avatar = models.ImageField(upload_to='student_profile/', null=True, blank=True)
description = models.CharField(max_length=120, null=True, blank=True)
objects = models.Manager()
def __str__(self):
return self.user.username
def get_absolute_url(self):
return reverse("students:student_profile_view", kwargs={"slug": self.slug})
I am pretty new to class based view so maybe I'm doing something wrong there?
I assume you do not have the user within the form so you need the form
def get_context_data (self, *args, **kwargs)
ctx = super().get_context_data(*args, **kwargs)
if self.request.method == 'POST':
ctx['form'] = EditProfileForm(instance=self.request.user)
and remove def form_valid()

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

how to access/modify django model fields before validating its form in Listview

I have a model Course that has the following attr:
class Course(models.Model):
user= models.ForeignKey(
User,
on_delete=models.CASCADE,
)
# email= models.EmailField(default=user.email)
courseName= models.CharField(max_length=20, blank=False)
class Meta:
unique_together= ('user','courseName',)
def __str__(self):
return self.courseName
I have created a form where I want the user to enter just the courseName and after they POST it, I will add the requested user in the model as well.
This is my form which is getting passed on to the template via my ListView
forms.py
class CourseForm(forms.ModelForm):
class Meta:
model= Course
fields = ['courseName']
**Here is my views.py where I am struggling with **
class CoursesListView(ListView, FormMixin):
model = Course
form_class = CourseForm
template_name = "userApp/courseList.html"
def get_queryset(self):
return Course.objects.filter(user__exact=self.request.user)
def get_context_data(self,*args,**kwargs):
context= super(CoursesListView,self).get_context_data(*args, **kwargs)
context['courseForm'] = self.form_class
return context
def post(self,request, *args, **kwargs):
form = self.form_class(request.POST)
user = User.objects.get(username__exact=self.request.user)
**I want to add the user to my model.user field here**
return self.get(redirect, *args, **kwargs)
def get(self,request, *args, **kwargs):
self.object=None
self.form = self.get_form(self.form_class)
return ListView.get(self, request, *args, **kwargs)
So basically my question is how can I add the user in my model before calling form.is_valid().
something like this?
def post(self,request, *args, **kwargs):
form_data = copy.copy(request.POST)
form_data['user'] = User.objects.get(username__exact=self.request.user).pk
form = self.form_class(form_data)
# form handling follows
return self.get(redirect, *args, **kwargs)
This Answer was suggested by a user who deleted this answer. Didnt get his user id but whoever you were thanks a lot for the help!!!
Just use form.save(commit=False) and then make the necessary changes.
def post(self,request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
user = User.objects.get(username__exact=self.request.user)
instance = form.save(commit=False)
instance.user = user
instance.save()

Django - The additional fields from UserProfile don't save

I'm new with Django and Python... I'm trying to do my own registration form for add some additional fields (Following the docs about Profiles)... All fine when I test, but after save, the additional field don't save, without error, just don't save, only the User is created... but If I write manually some values on create_profile function, it's saved correct.. How I can pass fields from my UserCreateForms to create_profile routine?
Sorry for my english, and thanks for your help, I'm involved in a little project for learn and I'm stopped for this detail.. :S
Models
class UserProfile(models.Model):
user = models.OneToOneField(User)
cedula_de_identidad = models.CharField(max_length=9)
def create_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance, cedula_de_identidad='If I write here it's saved correctly')
post_save.connect(create_profile, sender=User)
Forms
class UserCreateForm(UserCreationForm):
email = forms.EmailField(required=True)
cedula_de_identidad = forms.CharField(required=True)
class Meta:
model = User
fields = ("email", "username", "password1", "password2")
def clean_username(self):
username = self.cleaned_data['username']
# if not re.search(r'^\w+$', username):
# raise forms.ValidationError('Username can contain only alphanumeric characters')
try:
User.objects.get(username=username)
except ObjectDoesNotExist:
return username
raise forms.ValidationError('Username is already taken')
def save(self, *args, **kwargs):
user = super(UserCreateForm, self).save(*args, **kwargs)
profile = UserProfile()
profile.user = user
profile.cedula_de_identidad = self.cleaned_data['cedula_de_identidad']
profile.save
return profile
Views
def nuevo_usuario(request):
if request.method == 'POST':
formulario = UserCreateForm(request.POST)
if formulario.is_valid():
formulario.save()
return HttpResponseRedirect('/')
else:
formulario = UserCreateForm()
return render_to_response('nuevousuario.html', {'formulario': formulario}, context_instance=RequestContext(request))
I'm using Python 2.7 and Django 1.4.. Regards,