Django User Registration - not sure what is going wrong - django

Very, very new to Django. I'm attempting to create a user registration process with custom form inputs. However, I have a few issues with the general persisting of new users to the database. This is what I have defined in my forms.py:
class UserRegistrationForm(UserCreationForm):
required_css_class = 'required'
email = forms.EmailField()
first_name = forms.CharField()
last_name = forms.CharField()
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name')
def __init__(self, *args, **kwargs):
super(UserRegistrationForm, self).__init__(*args, **kwargs)
self.fields['username'].widget = TextInput(attrs={'placeholder': 'Username'})
self.fields['username'].required = True
self.fields['username'].error_messages = {'required': 'Please enter your username'}
self.fields['email'].widget = EmailInput(attrs={'placeholder': 'Email'})
self.fields['email'].required = True
self.fields['email'].error_messages = {'required': 'Please enter your email'}
self.fields['first_name'].widget = TextInput(attrs={'placeholder': 'Forename'})
self.fields['first_name'].required = True
self.fields['first_name'].error_messages = {'required': 'Please enter your first_name'}
self.fields['last_name'].widget = TextInput(attrs={'placeholder': 'Surname'})
self.fields['last_name'].required = True
self.fields['last_name'].error_messages = {'required': 'Please enter your last_name'}
self.fields['password1'].widget = PasswordInput(attrs={'placeholder': 'Password'})
self.fields['password1'].required = True
self.fields['password1'].error_messages = {'required': 'Please enter your Password'}
self.fields['password2'].widget = PasswordInput(attrs={'placeholder': 'Confirm password'})
self.fields['password2'].required = True
self.fields['password2'].error_messages = {'required': 'Please confirm your Password'}
I also have the following in my views.py file:
class UserRegistrationView(FormView):
disallowed_url = ''
form_class = UserRegistrationForm
success_url = '/blog'
template_name = 'oauth/user/registration_form.html'
def registration_allowed(self):
return getattr(settings, 'REGISTRATION_OPEN', True)
def register(self, request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
new_user = form.save(commit=False)
new_user.set_password(form.cleaned_data['password1'])
new_user.save()
return render(request, 'blog/post/list.html', { 'new_user': new_user })
else:
return render(request, 'oauth/user/registration_form.html', { 'form': form })
def get_success_url(self, user=None):
return super(UserRegistrationView, self).get_success_url()
What's worrying:
It doesn't create a new user in the database to be logged in (should I expect to be able to see the newly created user in the general admin dashboard?)
It is able to sign in with the superuser! (I really don't get that one bit, like, seriously couldn't write that if I tried!)
Any help in where I may have gone wrong would be much appreciated!

You're confusing class based and function based views. Class based views don't have a method called "register"; yours will never be called.
Actually the method you have written is a complete function based view in its own right. Extract it from the class, remove the self parameter, and change the urlconf to call the function directly, and it should work.

Create a post method (or change the name of register to post) and add it to your class so that as_view() knows what to do with your class. then add it to your urlconf as a view.
class UserRegistrationView(FormView):
disallowed_url = ''
form_class = UserRegistrationForm
success_url = '/blog'
template_name = 'oauth/user/registration_form.html'
def post(self, request, *arg, **kwargs):
return self.register(request)
def registration_allowed(self):
return getattr(settings, 'REGISTRATION_OPEN', True)
def register(self, request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
new_user = form.save(commit=False)
new_user.set_password(form.cleaned_data['password1'])
new_user.save()
return render(request, 'blog/post/list.html', {'new_user': new_user})
else:
return render(request, 'oauth/user/registration_form.html', {'form': form})
def get_success_url(self, user=None):
return super(UserRegistrationView, self).get_success_url()
Then, in your urls:
urlpatterns = [
# . . .
url(r'^register_user/$', UserRegistrationView.as_view(), name='register_user'),
# . . .
]
You could also add a get method and use it to render your form if you are interested in keeping all of your form logic contained to one view class.

Related

Populate custom field in Django form

I would like users to have the ability to update their email address. I created a profile that has fields, but the email address is in the users table. I created a form that adds a custom form field and it works for update. However, I can't find a way to pre-populate this field on a REQUEST.GET.
# forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'timezone')
class ProfileUpdateForm(ProfileForm):
email = forms.EmailField(max_length=254)
class Meta(ProfileForm.Meta):
fields = ProfileForm.Meta.fields + ('email',)
# views.py
#login_required
#require_http_methods(["GET","POST"])
def profile_update_view(request):
context = {}
# Get the logged in users profile
profile_object = Profile.objects.get(user=request.user.id)
if request.method == 'GET':
profile_form = ProfileUpdateForm(None, instance=profile_object)
context["form"] = profile_form
# how can I add User.objects.get(id=request.user.id).email to the custom field
if request.method == 'POST':
profile_form = ProfileUpdateForm(request.POST or None, instance=profile_object)
context["form"] = profile_form
if profile_form.is_valid():
try:
# email address exists
user = User.objects.get(email=profile_form.cleaned_data.get('email'))
messages.error(request, 'Failed profile update. Email address already exists.')
except:
# email address available
# get user object
user = User.objects.get(id=request.user.id)
user.email = profile_form.cleaned_data.get('email')
# update user object
user.save()
profile_form.save()
messages.success(request, 'Successful profile update.')
return render(request, "profile.html", context)
I tend to favour class-based views, and things like this are where they come into their own. The form:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'timezone')
email = forms.EmailField(max_length=254) #add non-model form field
And a class-based view. Handle the initial value for email in get_initial(), and updating of self.request.user in form_valid():
class ProfileUpdateView( UpdateView):
model = Profile
form_class = ProfileUpdateForm
template_name = 'profile.html' # profiles/update_profile.html would be better
# other declarations ...?
def get_initial(self):
initial = super().get_initial()
initial['email'] = self.request.user.email
return initial
# #transaction.atomic might be a good idea
def form_valid(self, form):
new_email = form.cleaned_data['email']
user = self.request.user
if user.email != new_email: # don't do a pointless non-update save
user.email = new_email
user.save()
return super().form_valid( form) # will save the profile
# forms.py
def __init__(self, *args, **kwargs):
self.email = kwargs.pop("email")
super(ProfileUpdateForm, self).__init__(*args, **kwargs)
self.initial['email'] = self.email
# views.py
if request.method == 'GET':
profile_form = ProfileUpdateForm(None, instance=profile_object, email=request.user.email)
context["form"] = profile_form
if request.method == 'POST':
profile_form = ProfileUpdateForm(request.POST or None, instance=profile_object, email=request.POST.get('email'))
context["form"] = profile_form

django rest framework RetrieveUpdate

I'm now making user profile update API using drf with RetreiveUpadteAPIView
there is one question that i can't figure out what the solution is.
This is what i want. I wanna update password and update other user data once for all.
changing password is worked as well as i supposed but other user datas (nick_name', 'wannabe', 'profile_img') are not save on DB through this logic below.
When i do self.object.set_password(), self.object.save() first and then do perform_update after. then user datas are well updated on DB. but password is saved without hashed even if i do set_password which is make the password hashed.
How can i fix it..
your best regard
Here is my code below.
#views.py
#permission_classes([IsAuthenticated])
class UpdatePartialUserView(RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserProfileSerializer
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
obj = queryset.get(pk=self.request.user.id)
self.check_object_permissions(self.request, obj)
return obj
def retrieve(self, request, *args, **kwargs):
serializer = UserSerializer(request.user)
return Response(status=status.HTTP_200_OK, data = serializer.data)
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
self.object = self.get_object()
serializer = self.get_serializer(request.user, data = request.data, partial=partial)
# serializer = self.get_serializer(self.object, data = request.data, partial=partial)
if not serializer.is_valid(raise_exception=True):
return Response(status=status.HTTP_409_CONFLICT, data = {'message':serializer.errors})
self.perform_update(serializer=serializer)
#make password hashed
self.object.set_password(request.data['password'])
self.object.save()
return Response(status=status.HTTP_202_ACCEPTED, data={"message": "success!"})
#serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True)
password2 = serializers.CharField(write_only=True, required=True)
old_password = serializers.CharField(write_only=True, required=True)
profile_img = serializers.ImageField(use_url=True, required = False)
def validate(self, attrs):
if attrs.get('password') != attrs.get('password2'):
raise serializers.ValidationError({
"password" : "passwords are not paired."})
return attrs
def validate_old_password(self, value):
#check user
request = self.context.get('request')
if request and hasattr(request, "user"):
user = request.user
if not user.check_password(value):
raise serializers.ValidationError({
"old_password" : "Old password is not correct."
})
return value
class Meta:
model = User
fields = ['nick_name', 'wannabe', 'old_password', 'password', 'password2', 'profile_img']
I think something like that should work.
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
self.object = self.get_object()
serializer = self.get_serializer(request.user, data = request.data, partial=partial)
if not serializer.is_valid(raise_exception=True):
return Response(status=status.HTTP_409_CONFLICT, data = {'message':serializer.errors})
self.object.set_password(new_password)
self.object.update(
nick_name=serializer.data["nickname"],
wannabe=serializer.data["wannabe"],
profile_img=serializer.data["profile_img"],
)
self.object.save()
return Response(status=status.HTTP_202_ACCEPTED, data={"message": "success!"})

How to get UpdateView to work with generic FormView

I have a Formview and an UpdateView which are conflicting with each other. They are supposed to work together but not in this way. Whenever I try to use the UpdateView the values get passed through the FormView for saving which causes it to send back form validation errors
e.g. 'A video with this Title already exists'.
Forms.py:
class BaseVideoUploadForm(forms.ModelForm):
"""
Form for uploading videos using related to:
:model: 'videos.Video'.
"""
title = forms.CharField(
widget=forms.TextInput(
attrs={'placeholder': 'Blackjack shuffle procedures'}))
description = forms.CharField(
widget=forms.Textarea(attrs={'rows': 3}),
help_text='A description of the content in this video.')
category = forms.ModelChoiceField(
queryset=Category.objects.all(),
required=True)
receiver = forms.ModelMultipleChoiceField(
queryset=EmployeeType.objects.all(),
required=True,
widget=forms.CheckboxSelectMultiple,
help_text='Employees in these groups will be able to view the video.')
video_file = forms.FileField()
thumbnail = forms.ImageField(required=False)
class Meta:
model = Video
fields = (
'title',
'description',
'receiver',
'category',
'video_file',
'thumbnail',
)
widgets = {
'date_time': forms.HiddenInput(),
'sender': forms.HiddenInput(),
'unviewed': forms.HiddenInput(),
'viewed': forms.HiddenInput(),
}
def __init__(self, *args, **kwargs):
super(BaseVideoUploadForm, self).__init__(*args, **kwargs)
self.fields['receiver'].label = "Viewers"
self.fields['receiver'].queryset = EmployeeType.objects.all()
self.fields['category'].queryset = Category.objects.all()
class VideoUploadForm(BaseVideoUploadForm):
send_notifications = forms.BooleanField(
required=False,
help_text='Send viewers a notification about the new video.')
class Meta(BaseVideoUploadForm.Meta):
fields = BaseVideoUploadForm.Meta.fields + ('send_notifications', )
FormView:
class VideoUploadView(FormView):
form_class = VideoUploadForm
success_url = '/videos'
template_name = 'videos/video_form.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request.FILES)
form.instance.sender = self.request.user
if form.is_valid():
form.save()
# doing stuff
form.save()
messages.success(self.request, self.success_message)
if request.is_ajax():
return JsonResponse({'success': True, 'url': reverse('videos-home')})
else:
return redirect(self.success_url)
else:
if request.is_ajax():
return JsonResponse({'success': False, 'error': form.errors})
else:
return render(request, self.template_name, {'form': form})
UpdateView:
class VideoUpdateView(UpdateView):
model = Video
form_class = VideoUploadForm
Urls.py:
urlpatterns = [
path('', views.VideoListView.as_view(), name='videos-home'),
path('upload/', views.VideoUploadView.as_view(), name='videos-upload'),
path('<int:pk>', VideoDetailView.as_view(), name='videos-detail'),
path('<int:pk>/delete/', VideoDeleteView.as_view(), name='videos-delete'),
path('<int:pk>/viewed/', views.mark_as_viewed, name='mark-as-viewed'),
path('<int:pk>/update/', VideoUpdateView.as_view(), name='videos-update'),
path('<int:pk>/notify', VideoNotificationView.as_view(), name='videos-notify'),
]
How do I get the UpdateView to not rely on the FormView? Or to work with the FormView? It seems all the UpdateView is doing is pointing to the FormView.

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

Django formset set current user

Related to this question, but expanding on it - How would I use this technique in a formset?
I'd like to use the current logged in user in a form, but I'm using the form in a formset. The referenced solution for a single form is to pass request.user to the form and process in init. How do I add to the kwargs for each form in the formset?
Example in my code:
in forms.py
class NewStudentForm (forms.Form):
username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^\w+$',
help_text = _("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."),
error_message = _("This value must contain only letters, numbers and underscores."))
first_name = forms.CharField(label=_('first name'), max_length=30 )
last_name = forms.CharField(label=_('last name'), max_length=30, )
email = forms.EmailField(label=_('e-mail address') )
password = forms.CharField(label=_('password'), max_length=64, )
class Meta:
model = User
fields = ("username","first_name", "last_name", "email", "password")
def __init__(self, *args, **kwargs):
self._user = kwargs.pop('user')
super(NewStudentForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
user = super(NewStudentForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
profile = Profile.objects.create_profile(user)
profile.activiation_key = profile.ACTIVATED_KEY
profile.authorized = True
profile.save()
user.is_active=True
user.save()
student = models.Student()
student.user = user
student.teacher = self._user
student.plaintext_pwd = self.cleaned_data["password"]
student.save()
return UserWarning
then in views.py
#login_required
def new_student(request):
from django.forms.formsets import formset_factory
try:
if request.method == 'GET':
newStudentFormset = formset_factory(forms.NewStudentForm, extra=2)
formset = newStudentFormset()
return shortcuts.render_to_response('NewStudent.html', { 'newStudentFormSet':formset, 'active_username': request.user.username })
elif request.method == 'POST':
if LOGIN_FORM_KEY in request.POST:
return _handle_login(request)
data = request.POST.copy()
newStudentFormset = formset_factory(forms.NewStudentForm)
formset = newStudentFormset(data) ### Pass current user to formset? ###
if formset.is_valid():
formset.save()
request.user.message_set.create(message="Save successful.")
return shortcuts.redirect(student)
else:
return shortcuts.render_to_response('NewStudent.html', { 'newStudentFormSet':formset, 'active_username': request.user.username, 'error_message':formset.errors})
return http.HttpResponseNotAllowed(['GET', 'POST'])
except models.Student.DoesNotExist:
return http.HttpResponseNotFound('<h1>Requested Student not found</h1>')
By adding a class that extends BaseFormSet you can add custom code to pass a parameter to the form.
in forms.py:
class NewStudentFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(NewStudentFormSet, self).__init__(*args, **kwargs)
def _construct_forms(self):
self.forms = []
for i in xrange(self.total_form_count()):
self.forms.append(self._construct_form(i, user=self.user))
Then in views.py:
# ...
data = request.POST.copy()
newStudentFormset = formset_factory(forms.NewStudentForm, formset=forms.NewStudentFormSet)
formset = newStudentFormset(data, user=request.user)
# ...
Thanks to Ashok Raavi.
I rather to iterate forms directly in the view:
for form in formset.forms:
form.user = request.user
formset.save()
It avoid creating unecessary BaseFormSet
It is cleaner
Based on Paulo Cheque answer (which didn't really work for my case).
I loved the idea of not writing a custom BaseFormSet inherited class.
if formset.is_valid():
new_instances = formset.save(commit=False)
for new_instance in new_instances:
new_instance.user = request.user
new_instance.save()
I tried the solution of selfsimilar but the BaseFormSet didn't work in my Django 1.6.
I followed the steps in: https://code.djangoproject.com/ticket/17478 and the way that worked for me is:
class NewStudentFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user',None)
super(NewStudentFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
def _construct_forms(self):
if hasattr(self,"_forms"):
return self._forms
self._forms = []
for i in xrange(self.total_form_count()):
self._forms.append(self._construct_form(i, user=self.user))
return self._forms
forms = property(_construct_forms)
Here is a similar question about passing form parameters to a formset:
Django Passing Custom Form Parameters to Formset
Personally, I like the second answer on there about building the form class dynamically in a function because it is very fast to implement and easy to understand.