Advice on Django user profile edit views - django

I'm building a Django app that will have users and profile models associated with them and they should have the ability to edit their profile in a view. As I see there are two similar but slightly different approaches how that could be done.
An UpdateView could be used that retrieves a pk from the url and then verifies if the pk corresponds to the actual authenticated user, for instance:
class ProfileUpdateView(UpdateView):
model = Profile
fields = ['field1', 'field2']
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
if obj.user != self.request.user:
# If the object user does not match the logged in user,
# raise a 404 Not Found exception.
raise Http404("You do not have permission to edit this profile.")
return obj
Or an alternative way that would check/ reference the current user via Django's authentication backend, for instance:
def profile_update(request):
profile = request.user.profile
form = ProfileForm(request.POST or None, instance=profile)
if form.is_valid():
form.save()
context = {'form': form}
return render(request, 'profile_update.html', context)
The main question is a bit generic, hence the name 'advice' in the post title, but are there any benefits/ risks associated with one or the other way of implementing a profile edit view that one should definitely consider when choosing between the two? The relationship between User and Profile model is a OneToOne relationship.
Thank's

I would not say it makes any difference, as long as only authenticated user can login in and one user can not modify other's profile. You can take any of the approaches, even implement the second approach in the first view (using /update/profile within Class Based View), like this:
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
model = Profile
fields = ['field1', 'field2']
def get_object(self, queryset=None):
return self.request.user.profile
But you need to use LoginRequiredMixin with the view so that only authenticated users can log in.

A simpler way is to use the framework's own functions directly, for example, use login_required to check if a user is logged in, and forms to control user profile editing.
Here is my own code for your reference.
views.py
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import UserProfileForm
#login_required
def userinfo(request):
if request.method == 'POST':
form = UserProfileForm(request.POST)
if form.is_valid():
user = User.objects.get(pk=request.user.id)
user.first_name = form.cleaned_data['fullname']
user.email = form.cleaned_data['email']
user.save()
msg = "Profile updated!"
messages.add_message(request, messages.SUCCESS, msg)
return redirect('user.info')
else:
form = UserProfileForm()
context = {
'title': 'My Profile',
'form': form,
}
return render(request, 'accounts/info.html', context)
forms.py
from django import forms
class UserProfileForm(forms.Form):
fullname = forms.CharField(
label="Name",
error_messages={
'required': 'Required!',
})
email = forms.EmailField(
label="E-mail",
error_messages={
'required': 'Required!',
'invalid': 'The format is not correct!',
})

Related

How to prevent 'no username' in django?

This is what my registration form looks like:
I want to make it so that if the user doesn't write anything in the given text boxes, a warning sign pops up and say that ' a username must be inputted' or something like that
but when i click the submit button, Value error occurs, saying that "The given username must be set"
def register(request):
context = {
'error_message': None,
}
if request.method == 'POST':
username = request.POST['username']
password1 = request.POST['password1']
password2 = request.POST['password2']
email = request.POST['email']
if password1==password2:
if User.objects.filter(username=username).exists() or Person.objects.filter(username=username).exists():
context['error_message'] = '이미 사용중인 아이디입니다.'
return render(request, 'UserAdministration/register.html', context)
elif User.objects.filter(email=email).exists() or Person.objects.filter(username=username).exists():
context['error_message'] = '이미 사용중인 이메일입니다.'
return render(request, 'UserAdministration/register.html ', context)
else:
user = User.objects.create_user(
password=password1,
email=email,
username=username
)
user.save()
masked_username = generate_masked_username.generate_masked_username(username)
person = Person.objects.create(
username=username,
masked_username=masked_username,
email=email,
password=password1
)
person.save()
return redirect('login')
else:
context['error_message'] = '비밀번호가 맞지 않습니다.'
return render(request, 'UserAdministration/register.html', context)
# return redirect('/')
# originally was homepage.html. Doesn't know if this changed anything. just a note
else:
return render(request, 'UserAdministration/register.html', context)
This is my code... anybody know how to prevent this error?
This is what forms are for. You should use a Form (or ModelForm) rather than building the form and using request.POST manually. Then you will get better errors. These docs will be especially useful.
A quick example:
Form:
from django import forms
from django.contrib.auth import get_user_model
class RegistrationForm(forms.ModelForm):
class Meta:
model = get_user_model()
fields = ["username", "email", "password"]
View:
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView
from .forms import RegistrationForm
class UserRegistrationView(CreateView):
form_class = RegistrationForm
template_name = "registration/register.html"
success_url = reverse_lazy("login")
This will get you most of the way there, you just need to add a custom field and some logic for the password2 validation and handle whatever the masked username stuff is.
Registration is such a common workflow however, that you can also use a package like django-registration to handle the details for you, and you can just override what you need for your custom logic where it's needed.

How do I add users automatically to groups as they register on a UserRegister form which inherits from a UserCreation form

I am trying to add a user to groups that I have created in the django admin. How do I implement my view function so that the user is added to a group on registration?
I have created a custom UserRegisterform as illustrated below to so that users can indicate Group they are in. I have a problem with implementing my view function so that the users are automatically added to groups
I have researched and found this links :
https://docs.djangoproject.com/en/2.2/topics/forms/
https://www.guguweb.com/2014/09/10/group-combo-box-django-user-profile-form/
have created a form as follows:
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import Group,User
class UserRegisterForm(UserCreationForm):
group = forms.ModelChoiceField(queryset=Group.objects.all(), required=True)
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'group']
my views.py file
from django.shortcuts import render,redirect
from .forms import UserRegisterForm
from django.contrib.auth.models import Group
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
for value in form.cleaned_data['group']:
group = Group.objects.get(name= value)
group.user_set.add(user)
form.save()
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form':form})
when I log into the admin site, i expect to see the user assigned to the group he/she chose. The User is added but not assigned a group
it worked. the solution to this, I refactored my views as follows:
views.py
from django.shortcuts import render,redirect
from .forms import UserRegisterForm
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
user = form.save()
group = form.cleaned_data['group']
group.user_set.add(user)
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form':form})
In Django we have two ways to do add new users to groups, one is Function based on views and the second is Class-based views.
Points to remember:
The primary key values that you will add with the group, should be integer only.
Or you have to make sure that all the columns in related tables for groups will have character(varchar) data type. By default, it's an integer data type. I prefer to go with integer only.
You can do easily with function based views:
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
user = form.save()
group = form.cleaned_data['group']
group.user_set.add(user)
#group.user_set.add(user.id) #if data in user is not of int, then use id with that to get primary int value. Else no need
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form':form})
In class-based views, after saving the value in DB, at the same pick the ID using the current user and save it to related to the group.
Second Class-based view:
class UserCreationView():
pass
def form_valid(self, form):
group = Group.objects.get(group_name)
self.object = form.save()
user_id = User.objects.values('id').filter(username=self.object)
group.user_set.add(user_id)
return super().form_valid(form)
#django #django-forms #django_groups #python
You add user to group, but this user is not ready(by ready I mean saved in database). User is saved to database by form.save(). Call form.save() just before for loop. And give me feedback

How do I login with with the user credentials in Django?

I'm working on a simple login and logout app in Django.
I wrote two views one for login and another for register.
Register view is working as expected. But login view is causing issues.
I'm using form.is_valid() in login view. That is where the issue is arising. If I print the form in else block, it is saying A user with that username already exists. This is happening even before trying to authenticate the user. Some one help me with this.
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.http.response import HttpResponse
from django.shortcuts import render
from notes.forms import UserForm
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(username=username, password=password)
if user:
login(request, user)
return HttpResponse("Logged in")
else:
return HttpResponse("Wrong creds")
else:
print(form)
return HttpResponse("else of is_valid()")
def register(request):
if request.method == 'GET':
return render(request, 'register.html')
elif request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
email = form.cleaned_data['email']
existing = User.objects.filter(username=username)
if existing:
return HttpResponse('Username is already taken')
else:
User.objects.create(username=username, password = password, email=email)
return HttpResponse("User created with "+ username +" username")
else:
return HttpResponse("Hi")
forms.py
from django.contrib.auth.models import User
from notes.models import Note
from django import forms
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = '__all__'
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ['username', 'email', 'password']
The form.is_valid() call will validate the form, this is done through several steps. Depending on the fields of the model, it thus also checks the uniqness of the data.
The User [Django-doc] model has a uniqness constraint on the username, hence the UserForm can only be valid, if the username is not yet taken, or when the form contains a instance that is already stored in the database.
I therefore think that it might be better to create a LoginForm, like Django does with an AuthenticationForm [Django-doc] [GitHub]. For example:
class UserForm(forms.Form):
username = forms.CharField()
password = forms.CharField()

How to automatically login a user after registration in django

This is what I am currently using for registration:
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
messages.info(request, "Thanks for registering. Please login to continue.")
return HttpResponseRedirect("/dashboard/")
else:
form = UserCreationForm()
return render_to_response("accounts/register.html", {
'form': form,
}, context_instance=RequestContext(request))
Is it possible not to require the user to login manually after creating an account, but rather simply to log them in automatically? Thanks.
edit: I had tried the login() function without success. I believe the problem is that AUTHENTICATION_BACKENDS was not set.
Using the authenticate() and login() functions:
from django.contrib.auth import authenticate, login
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
messages.info(request, "Thanks for registering. You are now logged in.")
new_user = authenticate(username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
)
login(request, new_user)
return HttpResponseRedirect("/dashboard/")
for class based views here was the code that worked for me (originally Django 1.7, updated for 2.1)
from django.contrib.auth import authenticate, login
from django.contrib.auth.forms import UserCreationForm
from django.http import HttpResponseRedirect
from django.views.generic import FormView
class SignUp(FormView):
template_name = 'signup.html'
form_class = UserCreateForm
success_url='/account'
def form_valid(self, form):
#save the new user first
form.save()
#get the username and password
username = self.request.POST['username']
password = self.request.POST['password1']
#authenticate user then login
user = authenticate(username=username, password=password)
login(self.request, user)
return HttpResponseRedirect(self.get_success_url)
The accepted answer doesn't seem to work with Django 4.0 (for me, at least), or alternatively it doesn't work with custom user models that have custom user managers.
This is how I solved the issue (adapted from https://stackoverflow.com/a/31491942 and https://stackoverflow.com/a/68515276):
from django.views.generic import CreateView
from django.urls import reverse_lazy
from django.contrib.auth import authenticate, login
from your.custom.user.models import User
class SignUpView(CreateView):
model = User
fields = ["username", "email", "password"]
success_url = reverse_lazy("success_url_name") # change this with your own URL
def form_valid(self, form):
# create the user object
user = form.save(commit=False)
# set password manually
# as otherwise the User will be saved with unhashed password
user.set_password(form.cleaned_data.get("password"))
# save your User object to the database
user.save()
# get email and password
email = form.cleaned_data.get("email")
password = form.cleaned_data.get("password")
# authenticate your user with unhashed password, because `authenticate` hashes it again
authenticated_user = authenticate(email=email, password=password)
# log in
login(self.request, authenticated_user)
return redirect(self.success_url)
You need to manually set the password, so that the database contains the hashed password. Without that, your unhashed password will be saved to the database, which will prevent you from logging in afterwards, as authentication involves hashing the password and checking that against the database.
using only "login()" in django-4.0.3
from django.contrib.auth import login
def registration(request):
if request.POST:
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
username = form.cleaned_data.get('username')
messages.success(request, f'Account created for {username}')
return redirect('home')
You can subclass Django's UserCreationForm and override it's save method to log them in when commit=True.
forms.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login
class CustomUserCreationForm(UserCreationForm):
"""
A ModelForm for creating a User and logging
them in after commiting a save of the form.
"""
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
class Meta(UserCreationForm.Meta):
pass
def save(self, commit=True):
user = super().save(commit=commit)
if commit:
auth_user = authenticate(
username=self.cleaned_data['username'],
password=self.cleaned_data['password1']
)
login(self.request, auth_user)
return user
You just need to make sure you pass in a request object when you instantiate the form. You can do that by overriding the view's get_form_kwargs method.
views.py
def get_form_kwargs(self):
form_kwargs = super().get_form_kwargs()
form_kwargs['request'] = self.request
return form_kwargs
Or, make sure when you instantiate a form_class you do CustomUserCreationForm(data=request.POST, request=self.request).
The Django auth.login function makes it easy to log in a user, given a request and User instance.
Note: remember to add the necessary imports for the following examples.
from django.contrib.auth import login
from django.shortcuts import render, redirect
In a function-based view, the following should work.
if form.is_valid():
user = form.save()
login(request, user)
return redirect("desired-url")
For a class-based view (such as CreateView or FormView), you can override the form_valid method:
def form_valid(self, form):
"""If the form is valid, save the associated model and log the user in."""
user = form.save()
login(self.request, user)
return redirect(self.success_url)

How to save the users last logout time

I am looking to save the users last logout time.My idea was to add it to the users profile model. I am using Django 1.11.15
Example:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
last_logout = models.DateTimeField(blank=True, null=True)
below is how I created my profile model just for reference
def signup(request):
if request.method == 'POST':
form = UserCreateForm(request.POST or None)
if form.is_valid():
new_user = form.save()
Profile.objects.create(user=new_user)
return redirect('accounts:edit_profile')
else:
form = UserCreateForm()
context = {'form': form}
return render(request, 'accounts/signup.html', context)
Below is what I intend to do. Is this the correct way. I want to add to django's default signout/logout method I am not sure if its called signout or logout
class LoggedOut(TemplateView):
template_name = 'logged_out.html'
def signout(self):
"""logout user """
self.request.user.profile.last_logout = datetime.now()
self.request.user.profile.save()
My URL's
url(r'^loggedout/$', views.LoggedOut.as_view(), name='loggedout'),
You can use Django logout signal for such purpose.
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.dispatch import receiver
#receiver(user_logged_out)
def sig_user_logged_out(sender, user, request, **kwargs):
user.profile.last_logout = datetime.now()
user.profile.save()