Keeping user logged in after changing password (Django) custom user model - python

I have a customized Django model. My problem is whenever I update any of the user profile information, it logged out. I know that Django logged out the user after changing their password and I have to use update_session_auth_hash() function. However, in my case it didn't work and I don't want to use Django changePasswordForm because I want the user to be able to change all his information in one form. Any help will be appreciated.
This is my custom user model:
class MyUser(AbstractBaseUser):
firstName = models.CharField(max_length=100, validators =[firstnameCheck] )
lastName = models.CharField(max_length=100, validators =[lastnameCheck] )
#nationalID = models.IntegerField(unique=True)
nationalID = models.CharField(max_length=10, validators=[RegexValidator(r'^[0-9]{10}$')], unique=True)
GENDER_CHOICES = [('male', 'Male'), ('female', 'Female')]
gender = models.CharField(choices=GENDER_CHOICES, max_length=10)
dateofbirth = models.DateField(auto_now=False, auto_now_add=False)
email = models.EmailField(max_length=100, unique=True)
password = models.CharField(max_length=100)
password_confirm = models.CharField(max_length=100, default='')
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'email' #enable authentication by using email instead of default 'username'
REQUIRED_FIELDS = ['firstName', 'lastName', 'nationalID','gender','dateofbirth','password','password_confirm']
objects = MyUserManager()
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self,app_label):
return True
This is my view
def update_admin(request,id):
context = {}
if request.method == "POST":
pi = MyUser.objects.get(pk=id)
fm = UserRegisterationForm(request.POST,instance=pi)
if fm.is_valid():
request.user.set_password(fm.cleaned_data['password1'])
fm.save()
update_session_auth_hash(request, request.user)
messages.add_message(request, messages.INFO, 'The profile updated successfully.')
context['form'] = fm
else:
pi = MyUser.objects.get(pk=id)
fm = UserRegisterationForm(instance=pi)
context['form'] = fm
return render(request,'project/update_admin.html', context)

The problem is that the change of the password will make the session invalid.
See doc
You could also heredity the django change password form for your own form.

You could do something like this.
Template:
<form method="POST" class="contact-form" action="{% url 'change-password' %}"> ##make sure the action links to the `changepassword` view
{% csrf_token %}
<input type="password" name="password" required id="password" pattern="^[a-zA-Z0-9]{6,}$">
</div>
<button type="submit">Submit</button>
</form>
Views.py:
def changepassword(request):
user = request.user
if 'password' in request.POST:
user.set_password(request.POST['password'])
return render(request, 'project/changepassword.html')
This will keep your user logged in after the password change, but you will need to add validation handling & error handling to this to make it a viable solution.

Related

Django Profile model not showing first_name and last_name fields in sign up form

I created a Profile model with Django. The profile inherits from a CustomUser class which I wrote by inheriting AbstractBaseUser. However, when I go to the sign up page, even though first_name and last_name are required in the Custom User class, it doesn't show. It only shows the fields of the profile model, and email and password. I am using Django all auth.
How do I make the required fields of the custom user (and other fields I want e.g middle_name) and the fields of the profile all show up in the same form, and still save both the custom user model, and the profile model in the database all at the same time?
models.py
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
USER_TYPE_CHOICES = (
(1, 'student'),
(2, 'department'),
(3, 'admin'),
)
first_name = models.CharField(max_length=70, blank=False, default="")
middle_name = models.CharField(max_length=70, blank=False, default="")
last_name = models.CharField(max_length=70, blank=False, default="")
is_active = models.BooleanField(default=True)
user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)
is_staff = models.BooleanField(default=False) # a admin user; non super-user
is_superuser = models.BooleanField(default=False) # a superuser
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name'] # Email & Password are required by default.
objects = UserManager()
# I left out some unimportant code
class StudentUser(models.Model):
# This is the model class for students
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
matric_number = models.CharField(max_length=11, blank=False)
department = models.CharField(max_length=40, blank=False)
views.py:
from users.forms import StudentUserSignupForm
from allauth.account.views import SignupView
class StudentUserSignupView(SignupView):
# The referenced HTML content can be copied from the signup.html
# in the django-allauth template folder
template_name = 'account/signup.html'
# the previously created form class
form_class = StudentUserSignupForm
forms.py:
class UserCreationForm(forms.ModelForm):
"""
A form for creating new users. Includes all the required
fields, plus a repeated password.
"""
class Meta:
model = User
fields = ('email', 'first_name', 'last_name')
def save(self, commit=True):
user = super(UserChangeForm, self).save(commit=False)
# Save the provided password in hashed format
if self.cleaned_data.get("password1"):
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
# I removed some unimportant code
class StudentUserSignupForm(SignupForm):
first_name = forms.CharField(max_length=70)
middle_name = forms.CharField(max_length=70, required=False)
last_name = forms.CharField(max_length=70)
matric_no = forms.CharField(min_length=10, max_length=11)
department = forms.CharField(max_length=40)
def save(self, request):
# Save the User instance and get a reference to it
user_form = super(StudentUserSignupForm, self).save(request)
# user_form.user_type = 1 To correct
student_user = StudentUser(
user=user_form,
matric_no=self.cleaned_data.get('matric_number'),
department=self.cleaned_data.get('department')
)
student_user.save()
return student_user.user
html template
<div class="row justify-content-center align-items-center">
<div class="col-auto m-auto">
<div class="jumbotron mt-4">
<h3>Sign Up</h3>
<form method="post">
{% csrf_token %}
{% for field in form %}
{{ field|add_class:"form-control-sm"|as_crispy_field }}
{% endfor %}
<button class="btn btn-sm btn-primary" type="submit">Sign Up</button>
</form>
</div>
</div>
You see that in the image above, it shows email, password, and department, but it does not show first_name, last_name, middle_name etc. How do I make the required and non required fields of the User model show up in the form and still save both models? Does the issue have anything to do with django-allauth?

How to get a specific attribute from an object in the database in Python Django?

For an assignement I have to create a website where a user can add other users from the database as his friends. To accomplish that I'd like to create a dropdownlist where the user can select other users under their firstname + lastname. However, how do I create a list with all user's names in it ? How do I make the drop down list ?
Here is my models.py
class User(models.Model):
firstname = models.CharField(max_length=30)
lastname = models.CharField(max_length=50)
country = models.CharField(max_length=50)
email = models.EmailField(max_length=50)
phone = models.CharField(max_length=30)
password = models.CharField(max_length=128)
GENDER_CHOICES = (
('M','Male'),
('F','Female'),
('X','X'),
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
Here is my register_view in views.py :
def register_view(request):
if 'email' in request.GET:
newUser = User(firstname=request.GET['firstname'],
lastname=request.GET['lastname'],
country=request.GET['country'],
email=request.GET['email'],
phone=request.GET['phone'],
password=request.GET['password'],
gender=request.GET['gender'])
newUser.save()
return redirect('/login')
else:
return render(request, 'register.html')
Please, help… I deeply apologize for my silly questions and my English since it is not my mother tongue. I however really need at least a breakthrough concerning this.
Many thanks !
If you mean dropdown like this, pure Django maybe is not enough, try something like bootstrap, it is easy to use.
But if you don't feel like bothering to use some frontend framework, and just want a simple select input box with a submit button beside it, Django can do that.
First you will need to add a many-to-many field to track friends, and specify how to "show" those users:
class User(models.Model):
firstname = models.CharField(max_length=30)
lastname = models.CharField(max_length=50)
country = models.CharField(max_length=50)
email = models.EmailField(max_length=50)
phone = models.CharField(max_length=30)
password = models.CharField(max_length=128)
friends = models.ManyToManyField("self")
GENDER_CHOICES = (
('M','Male'),
('F','Female'),
('X','X'),
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
def __str__(self):
return f"{self.firstname} {self.lastname}"
Then add a form for the User model.
from django import forms
from appname.models import User
class RegisterForm(forms.ModelForm):
class Meta:
model = User
fields = '__all__'
In view.py, pass it to the template
def register_view(request):
if request.method == 'POST':
# your code
register_form = RegisterForm(request.POST)
if register_form.is_valid():
register_form.save()
return redirect('/login')
elif request.method == 'GET':
# your code
register_form = RegisterForm()
return render(
request,
'register.html',
{
'register_form': register_form,
}
)
Finally, render the form in the template:
<form action="{% url 'appname:register' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
There might be many unfamiliar conceptions to you, leave a comment if anything confuses you.

One field (ManyToMany) in a Django ModelForm is not saving to database

My app is project management tool where users can add, edit, and view Projects. Projects have titles, summaries, and authors (users). Users have ManyToMany relationships with Projects.
Adding new Projects was working fine until I added an edit project view. I can still create a new Project or edit an existing one, and the new title and summary get saved to the database, but the selected authors do not get saved. Note that I can still go into the shell and add authors to a project manually.
Here are the Project and User models:
class MyUser(AbstractBaseUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
bank_id = models.CharField("researcher's four-letter bank id", null=True, max_length=4, unique=True)
#division = models.CharField(max_length=30, blank = True)
department = models.CharField(max_length=3, choices=DEPARTMENTS)
job_title = models.CharField("job title", max_length=30, choices=JOB_TITLES)
citations = models.IntegerField(null=True, blank=True)
institution = models.CharField(max_length=30, choices=DEPARTMENTS, blank=True)
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
#history = HistoricalRecords()
REQUIRED_FIELDS = ['email']
USERNAME_FIELD = 'username'
class Project(models.Model):
title = models.TextField('Title')
summary = models.TextField('Summary', default=DEFAULT_TEXT)
authors = models.ManyToManyField(MyUser)
internal_status = models.CharField('Internal Status', max_length = 20, choices = INTERNAL_STATUS,
default='wip')
external_status = models.CharField('External Status', max_length = 20, choices = EXTERNAL_STATUS,
blank=True)
mtp_goal = models.CharField(max_length = 50, choices = MTP_GOALS,
blank=True)
topics = ArrayField(models.CharField('Topics', max_length=30), size=4, null=True)
created_on = models.DateTimeField(auto_now_add=True, null=True)
updated_on = models.DateTimeField(auto_now=True, null=True)
history = HistoricalRecords()
views.py
def add_new(request):
if request.method == 'POST':
form = ProjectForm(request.POST)
if form.is_valid():
project = form.save(commit=False)
project.created_on = timezone.now()
project.save()
return redirect('project_profile', pk=project.pk)
else:
form = ProjectForm()
return render(request, 'add_new.html', {'form': form})
def edit_project(request, pk):
project = get_object_or_404(Project, pk=pk)
if request.method == 'POST':
form = ProjectForm(request.POST, instance=project)
if form.is_valid():
project = form.save(commit=False)
project.updated_on = timezone.now()
project.save()
return redirect('project_profile', pk=project.pk)
else:
form = ProjectForm(instance=project)
return render(request, 'edit_project.html', {'form': form})
forms.py:
from django import forms
from .models import Project, MyUser
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ('title', 'authors', 'summary')
And finally, the add_project.html page (please excuse the horrific html):
<html>
<head>
<title>ResearchTracker</title>
</head>
<body>
<div>
<nav>
About us
Login
Admin
</nav>
</div>
<h1>New project</h1><br>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
Go home
</body>
</html>
Since you use commit=False in save, you need to explicitly call save_m2m to save the many to many fields.
From the documentation:
Calling save_m2m() is only required if you use save(commit=False).
When you use a simple save() on a form, all data – including
many-to-many data – is saved without the need for any additional
method calls. For example:
You would do
project.save()
project.save_m2m()

Django - Form not showing up on template only the button

I've been trying everything, but I can't figure out why it's not showing-up. I searched everywhere on this site with no luck. The button shows up but when I click than it give me a TypeError at get_profile, saying:
Exception Value:
get_profile() got an unexpected keyword argument 'username'
Here's my code:
models.py
class CustomUser(AbstractBaseUser):
email = models.EmailField('email address', unique=True, db_index=True)
username = models.CharField('username', max_length=50, unique=True, db_index=True)
first_name = models.CharField(max_length=50, blank=False)
last_name = models.CharField(max_length=50, blank=False)
joined = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __unicode__(self):
return self.username
forms.py
from django import forms
from apps.accounts.models import CustomUser
class RegistrationForm(forms.ModelForm):
email = forms.EmailField(widget=forms.TextInput, label='email')
username = forms.CharField(widget=forms.TextInput, label='username')
password1 = forms.CharField(widget=forms.PasswordInput, label='Enter your password')
password2 = forms.CharField(widget=forms.PasswordInput, label='Re-type your password')
first_name = forms.CharField(widget=forms.TextInput, label='First Name')
last_name = forms.CharField(widget=forms.TextInput, label='Last Name')
class Meta:
model = CustomUser
fields = ['email', 'username', 'password1', 'password2', 'first_name', 'last_name']
def clean(self, password1, password2):
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 don't match. Please enter both fields 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
views.py
def register(request):
"""
User Registration View
"""
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
CustomUser = form.save()
CustomUser.save()
return redirect('home.html')
else:
form = RegistrationForm()
return render_to_response('register.html', {
'form': form,
}, context_instance=RequestContext(request))
def get_profile(request):
username = CustomUser.username
return render_to_response(request, 'profile.html', {'username': username})
urls.py
urlpatterns = patterns ('',
url(r'register$', 'apps.accounts.views.register', name='register'),)
register.html
{% load staticfiles %}
{% load crispy_forms_tags %}
<!DOCTYPE html>
<html lang="en">
<body>
{% block body %}
<form method='POST' action="register" enctype= 'multipart/form-data'>{% csrf_token %}
<table>{{ form.ast_table}}</table>
<input type='submit' class="btn btn-default" value="Register" />
</form>
{% endblock %}
</body>
</html>
It seems that you don't have the right signature for the get_profile view function.
You should check your urls, you probably have something like
url(r'^profile/(?P<username>\w+/$', get_profile),
If so, your view should be
def get_profile(request, username):
#you can get the user
user = CustomUser.objects.get(username=username)
return render_to_response(request, 'profile.html', {'username': username, 'user': user})
CustomUser is the class. You need to fetch an instance before you can actually fetch its username.
Eg:
CustomUser.objects.get(pk=1).username
Gives you the username of the first user.
Also, its supposed to be {{form.as_table}} not ast_table

Display user data in template

I'm new in Django. I want to display in templates
user's information where he connect, but when I change page, data disappears!
views.py:
def login(request):
if request.method == "POST":
username = request.POST.get('username', None)
password = request.POST.get('password', None)
user = authenticate(username=username, password=password)
data = {}
data['user'] = user
if user is not None:
if user.is_authenticated:
return redirect(accueil)
else:
messages.error(request, 'Compte inactif.')
else:
messages.error(request, 'Identifiant et/ou mot de passe invalide.')
return render(request, 'login.html')
Models.py:
class AbstractCustomerUser(AbstractBaseUser):
"""
An abstract base class implementing a fully featured User model with
admin-compliant permissions.
Username, password and email are required. Other fields are optional.
"""
username = models.CharField(_('username'), max_length=30, unique=True,
help_text=_('Required. 30 characters or fewer. Letters, digits and '
'#/./+/-/_ only.'),
validators=[
validators.RegexValidator(r'^[\w.#+-]+$', _('Enter a valid username.'), 'invalid')
])
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('email address'), blank=True, unique=True)
is_staff = models.BooleanField(_('staff status'), default=False,
help_text=_('Designates whether the user can log into this admin '
'site.'))
is_active = models.BooleanField(_('active'), default=True,
help_text=_('Designates whether this user should be treated as '
'active. Unselect this instead of deleting accounts.'))
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
company = models.ForeignKey(Customer)
referent_for_customers = models.ManyToManyField('Customer',
related_name='referents',
verbose_name="est référent pour")
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['id', 'company']
class Meta:
# For an unknown reason, fails to save a modified object with this
# constaint. Comment it for now:
#unique_together = ('username', 'company')
verbose_name = _('utilisateur')
abstract = True
class CustomerUser(AbstractCustomerUser):
class Meta(AbstractCustomerUser.Meta):
db_table = 'customers_customeruser'
swappable = 'AUTH_USER_MODEL'
verbose_name = 'utilisateur portail'
templates:
<div id="header">
<h2>Portail Client</h2>
Bienvenue {{ user.username }}
CustomerUser table info: {{ user.company_id }}, {{ user.last_login }}
Customer table info: {{ user.company.name }}
Modifier mot de passe
Déconnexion
</div>
user is automatically provided to the template via the context processor. But these only run if you are using a RequestContext when rendering your template: either by specifically passing it in (eg with the context_instance parameter to render_to_response), or by using the newer render shortcut.
So in all your other views, you need to be sure you are doing:
return render(request, 'your_template.html', params)
in your views.py you should use RequestContext
from django.template import RequestContext
from django.shortcuts import render_to_response
def login(request):
args={}
...
return render_to_response('login.html', args, context_instance=RequestContext(request))
Looks like the user object is not available inside your template. If the user object is not available in the template, nothing will be displayed when the template evaluates {{ user.username }}
From the docs -
If you use a variable that doesn’t exist, the template system will
insert the value of the TEMPLATE_STRING_IF_INVALID setting, which is
set to '' (the empty string) by default.
You can pass the user object to the template by modifying the appropriate view which returns the welcome template, using either render() which forces the use of a RequestContext implicitly or render_to_response() where you have to explicitly pass a RequestContext instance

Categories