Extending User Profile - django

Please i need help .I have a problem in extending userprofile.At first everything seems to be working good until now .Please i need help in resolving this bellow is my code .
Model.py
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,related_name="userprofile")
date =models.DateField(blank=False,null= True)
bio = models.TextField(max_length=500,blank=False)
picture = models.ImageField(upload_to="profile_image",null=True,blank=True)
company = models.CharField(max_length=500,null=True)
def __str__(self):
return self.user.username
#receiver(post_save,sender=User)
def create_profile(sender,instance,created,**kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save,sender=User)
def save_user_profile(sender,instance,**kwargs):
instance.UserProfile.save()
views.py
def update_profile(request):
if request.method == 'POST':
profile_form = ProfileForm(request.POST,request.FILES,instance=request.user.userprofile)
if profile_form.is_valid():
profile_form.save()
messages.success(request,'Your Profile has been Updated')
return redirect('success:profile_account')
else:
messages.error(request,'fill out the fields correctly')
else:
profile_form = ProfileForm(instance=request.user.userprofile)
return render(request,"success/user_account/edit_profile.html",{'profile_form':profile_form})
html.form
<form action='{{ action_url }}' method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ profile_form.bio}}{{profile_form.bio.error}}
{{ profile_form.picture}}{{profile_form.picture.error}}
<div class="pc"><label>Company Name:</label>{{ profile_form.company}}{{profile_form.company.error}}
{{ profile_form.date}}{{profile_form.date.error}}
<button type="submit">Save changes</button>
Error I get
Forbidden (403)
CSRF verification failed. Request aborted. Help
Reason given for failure:
CSRF token missing or incorrect.
In general, this can occur when there is a genuine Cross Site Request
Forgery, or when Django's CSRF mechanism has not been used correctly.
For POST forms, you need to ensure:
Your browser is accepting cookies.
The view function passes a request to the template's render method.
In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as
well as those that accept the POST data.
The form has a valid CSRF token. After logging in in another browser tab or hitting the back button after a login, you may need to
reload the page with the form, because the token is rotated after a
login.
You're seeing the help section of this page because you have DEBUG =
True in your Django settings file. Change that to False, and only the
initial error message will be displayed.
You can customize this page using the CSRF_FAILURE_VIEW setting.

this is the best way to extend user model profile
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
from django.contrib.auth.models import BaseUserManager
class AccountManager(BaseUserManager):
def create_user(self, username, password=None, **kwargs):
if not username:
raise ValueError('Users must have a valid email username.')
if not kwargs.get('email'):
raise ValueError('Users must have a valid email.')
email = kwargs.get('email')
account = self.model(
username=username, email=self.normalize_email(email)
)
account.set_password(password)
account.save()
return account
def create_superuser(self, username, password, **kwargs):
account = self.create_user(username, password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
username = models.CharField(max_length=40, unique=True)
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email', 'first_name', 'last_name']
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
modify this example and then in forms.py just import you model user profile

Related

'Custom user with this Email address already exists' error when trying to update email using forms. Django

please read through my code, my question with screenshots and things i've already tried are after my code.
managers.py:
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
def create_user(self, email, password, **extra_feilds):
if not email:
raise ValueError(_('The Email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_feilds)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_feilds):
extra_feilds.setdefault('is_staff', True)
extra_feilds.setdefault('is_superuser', True)
extra_feilds.setdefault('is_active', True)
if extra_feilds.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff = True'))
if extra_feilds.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True'))
return self.create_user(email, password, **extra_feilds)
models.py:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.utils.translation import gettext_lazy as _
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser, StripeConnectSetup
from django import forms
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm):
model = CustomUser
fields = ('email', 'first_name', 'last_name')
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ('email','first_name', 'last_name')
views.py
from django.shortcuts import render, redirect
from .forms import CustomUserCreationForm, CustomUserChangeForm, StripeConnectSetupForm
from .models import CustomUser
from django.contrib.auth import login, logout
from django.contrib.auth.forms import AuthenticationForm
def update_user_view(request):
obj = CustomUser.objects.get(email=request.user.email)
data = {'email': obj.email, 'first_name': obj.first_name, 'last_name': obj.last_name}
form = CustomUserChangeForm(initial=data)
if request.method == 'POST':
form = CustomUserChangeForm(request.POST, request.FILES)
if form.is_valid():
obj = CustomUser.objects.get(email=request.user.email)
obj.email = form.cleaned_data['email']
obj.first_name = form.cleaned_data['first_name']
obj.last_name = form.cleaned_data['last_name']
obj.save()
return redirect('accounts:update_user')
context = {
'form': form
}
return render(request, 'accounts/update_user.html', context)
update_user.html:
<h1>Update User Profile</h1>
<hr>
<form method="POST" enctype="multipart/form-data"> {% csrf_token %}
{{form.as_p}}
<button type="submit" class="btn btn-primary">Update</button>
</form>
the problem:
when trying to update my account, if i don't wish to change my email (just my first name or lastname) it displays the error 'Custom user with this Email address already exists'
i have searched for many solutions and the only one that seemed to work is passing through the clean function on the update form as shown below:
forms.py
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ('email','first_name', 'last_name')
def clean(self):
pass
this fixes the issue and allows you to update your first or last name without updating your email.
However, this causes another bug.
when i try to change my email to a differnt email that already exists in my database instead of getting the 'Custom user with this Email address already exists' error, it loads this screen instead
error that is occuring with clean function fix in place
so the question is how do i stop the 'Custom user with this Email address already exists' error when im not updating my email but allow the error when i am changing my email and that email already exists.
Since you want to update the object you should simply pass the keyword argument instance to the form instead of passing initial. You get the error 'Custom user with this Email address already exists' because when you write CustomUserChangeForm(request.POST, request.FILES) and it tries to validate the form it considers this as creating a new user and this gives you an error.
From the documentation on The save() method of ModelForm:
A subclass of ModelForm can accept an existing model instance as
the keyword argument instance; if this is supplied, save()
will update that instance. If it’s not supplied, save() will
create a new instance of the specified model
So your view should be like:
def update_user_view(request):
obj = CustomUser.objects.get(email=request.user.email)
form = CustomUserChangeForm(instance=obj)
if request.method == 'POST':
form = CustomUserChangeForm(request.POST, request.FILES, instance=obj)
if form.is_valid():
obj = form.save()
return redirect('accounts:update_user')
context = {
'form': form
}
return render(request, 'accounts/update_user.html', context)
Next change your form back so that the clean method is unchanged:
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ('email','first_name', 'last_name')

How to assign users in groups from Django views?

I have CRUD operations for users, which can be done only from the admin role. I have 6 different roles, that I made by creating groups and assign users to specific group from the admin panel.
My question is is there some way to include these group fields with drop down menu when admin create new user, so he can choose what group to assign the new user but not from the admin panel?
any help would be appreciated :)
model.py
class CustomUserManager(BaseUserManager):
def create_user(self, email, password, **extra_fields):
if not email:
raise ValueError(_('The Email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.'))
if extra_fields.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True.'))
return self.create_user(email, password, **extra_fields)
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
username = models.CharField(max_length=30, blank=True, default='')
is_superuser = models.BooleanField(default=True)
is_admin = models.BooleanField(default=True)
is_employee = models.BooleanField(default=True)
is_headofdepartment = models.BooleanField(default=True)
is_reception = models.BooleanField(default=True)
is_patient = models.BooleanField(default=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
forms.py
class UserForm(ModelForm):
class Meta:
model = CustomUser
fields = ['email', 'password',]
useradd.html
<h1 class="display-4">Add new user</h1>
<form action="" method="post" autocomplete="off">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-success">Save </button>
views.py
#login_required
#admin_only
def users(request):
context = {'users': CustomUser.objects.all()}
return render(request, 'users.html', context)
#login_required
def users_add(request):
if request.method == "POST":
form = UserForm(request.POST)
if form.is_valid():
users = CustomUser.objects.create_user(**form.cleaned_data)
#login(request, users)
# redirect, or however you want to get to the main view
return redirect('feedback:users')
else:
form = UserForm()
return render(request, 'useradd.html', {'form': form})
You can use ModelChoiceField to include select widget in your form. So please try this code:
forms.py
from django.contrib.auth.models import Group
from django.forms import ModelForm, ModelChoiceField
from app.model import CustomUser
class UserForm(ModelForm):
class Meta:
model = CustomUser
fields = [
'email',
'password',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['group'] = ModelChoiceField(
queryset=Group.objects.all(),
empty_label='No group'
)
Change CustomUserManager.create_user method in model.py
from django.contrib.auth.models import Group
class CustomUserManager(BaseUserManager):
def create_user(self, email: str, password: str, group: Group, **extra_fields):
if not email:
raise ValueError(_('The Email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
if group is not None:
group.user_set.add(user)
return user

Users not writing to database (Postgresql)

When I create a user with my custom user model I cannot sign in. It just keeps telling me Please enter a correct email and password. Note that both fields may be case-sensitive. I thought I should check if I can see my test user in the db. I know only staff accounts can login to the admin but I'm using a different login form.
If I'm not mistaken it looks like there aren't any users in my database.
myproject=# \dt accounts_user
List of relations
Schema | Name | Type | Owner
--------+---------------+-------+------------------
public | accounts_user | table | myprojectuser
(1 row)
Here's my user model:
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser,
BaseUserManager,
PermissionsMixin,
)
# Create your models here.
class UserManager(BaseUserManager):
def create_user(self, email, first_name, last_name, company, phone, is_active=True, is_admin=False, is_staff=False, is_dealer=False, password=None):
if not email:
raise ValueError("Users must have an email address")
if not password:
raise ValueError("Users must have a password")
if not first_name:
raise ValueError("Users must have a first name")
if not last_name:
raise ValueError("Users must have a last name")
if not company:
raise ValueError("Users must have a company")
if not phone:
raise ValueError("Users must have a phone number")
user_obj = self.model(
email = self.normalize_email(email)
)
user_obj.set_password(password)
user_obj.first_name = first_name
user_obj.last_name = last_name
user_obj.company = company
user_obj.phone = phone
user_obj.admin = is_admin
user_obj.staff = is_staff
user_obj.dealer = is_dealer
user_obj.active = is_active
user_obj.save(using=self._db)
return user_obj
def create_superuser(self, email, first_name, last_name, company, phone, password=None):
user = self.create_user(
email,
first_name,
last_name,
company,
phone,
password=password,
is_admin=True,
is_staff=True
)
return user
Perhaps there's something wrong in my admin.py?
from django.contrib import admin
from django.contrib.auth import get_user_model
# Register your models here.
User = get_user_model()
admin.site.register(User)
Hopefully someone can help me as I'm really stuck here.
I'm not 100% sure why your issue arises (considered you enterer it in the correct format), but this should give you a basic working solution to build upon:
forms.py
from django.forms import ModelForm
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.contrib.auth.models import User
# Adjusting the default UserCreationForm
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', # etc.]
admin.py
from django.contrib import admin
from .models import User
admin.site.register(User)
models.py
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
# Inherit from AbstractBaseUser to extend User Model
class User(AbstractBaseUser):
email = models.EmailField(max_length=255, unique=True)
first_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255, blank=True, null=True)
company = models.CharField(max_length=255, blank=True, null=True)
phone = models.CharField(max_length=255, blank=True, null=True)
active = models.BooleanField(default=True) # can login
online = models.BooleanField(default=False)
admin = models.BooleanField(default=False)
staff = models.BooleanField(default=False)
dealer = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
Also I am not sure which tutorial (this?) you followed, but personally I don't have models both in models.py and admin.py.
Login extension - views.py
# function for login logic
def loginPage(request):
if request.method == 'POST':
# get input values
username = request.POST.get('username')
password = request.POST.get('password')
# check if user has account in db
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('render_calculator')
context = {}
return render(request, 'login.html', context)
def registerPage(request):
# define the form, in this case Djangos default UserCreation
form = CreateUserForm()
# check if form inputs are valid after submitting, the save 'post' user
if request.method == 'POST':
form = CreateUserForm(request.POST)
if form.is_valid():
# Save User to db
form.save()
# get input values
username = request.POST.get('username')
password = request.POST.get('password')
email = request.POST.get('email')
return redirect('render_login')
context = {'form': form}
return render(request, 'register.html', context)
login.html
<form method="post" action="">
{% csrf_token %}
<div class="formFieldLoginContainer">
<input type="text" name="username" placeholder="username">
<input type="password" name="password" placeholder="password">
</div>
<input type="submit" id="loginSubmitButton" name="Login User" value="Login">
<div id="noAccountDiv"> Don't have an account yet?
Sign Up
</div>
</form>
register.html
<form method="post" action="">
{% csrf_token %}
<!-- loop form object and create div for each form field included -->
{% for field in form %}
<div class="formFieldContainer">
<div class="registerLabel">{{field.label}}</div>
<div class="registerField">{{field}}</div>
</div>
{% endfor %}
<!-- render possible input form errors -->
{{form.errors}}
<input type="submit" id="submitButton" name="Create User" value="Register">
<div id="alreadyAccountDiv"> Already have an account?
Login
</div>
</form>
ulrs.py
urlpatterns = [
path('login/', views.loginPage, name='render_login'),
path('logout/', views.logout_view),
path('email/', include(mail_urls)),
path('password/', views.change_password, name='change_password'),
]

Django 3.0: Unable to get username in template

I have issue is that in template, I automatically get access of username of Superuser (if that is logged-in) but not username of user based on Buyer (Model). User is getting registered and also can login (I can see it in database). But in template I am unable to get username of user other than superuser. I don't what i am missing here. So i am putting all code of view.py here. Also after user logging he sees bookrepo:home and i am using user logic in header.html (bookrepo:home extends header.html)
I have model named Buyer in models.py file. And based on this model, two modelForm has made.
This is code of model.py
class Buyer(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(User, on_delete=models.DO_NOTHING)
# additional attributes
contact = models.CharField('Contact #', max_length=16, unique=True, validators=[
RegexValidator(
regex=r'^\+?1?\d{9,15}$',
message="Phone number (Up to 15 digits) must be entered in the format: '+923001234567'."
),
], )
devices = models.CharField('Devices', unique=False, max_length=115, blank=True)
picture = models.ImageField(upload_to=profile_pic_path, null=True,blank=True)
def __str__(self):
return "{} {}".format(self.user.first_name, self.user.last_name)
class Meta:
get_latest_by = '-user.date_joined'
This is code of modelForms
class RegistrationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
confirm_password = forms.CharField(widget=forms.PasswordInput())
class Meta():
model = User
fields = ('username',
'email',
'password')
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
if password != confirm_password:
raise forms.ValidationError(
"Password and Confirm Password does not match"
)
class RegistrationFormPlus(forms.ModelForm):
class Meta():
model = Buyer
fields = ('contact',)
This is code of header.html (NOT the home.html)
{% if user.is_authenticated %}
<a class="nav-link" href="{% url 'bookrepo:logout' %}">Logout</a>
<h2>Welcome {{ user.username }}!</h2>
{% else %}
<a class="nav-link" href="{% url 'bookrepo:user_login' %}">Login</a>
{% endif %}
This is code of views.py
def home(req):
bookz = Book.objects.order_by('title')
var = {'books': bookz, 'range': 10}
return render(req, 'bookrepo/home.html', context=var)
def registration(request):
registered = False
if request.method == 'POST':
reg_form = RegistrationForm(data=request.POST)
reg_form_plus = RegistrationFormPlus(data=request.POST)
if reg_form.is_valid() and reg_form_plus.is_valid():
user = reg_form.save()
user.set_password(user.password)
user.save()
user_plus = reg_form_plus.save(commit=False)
user_plus.user = user
user_plus.save()
registered = True
else:
print(reg_form.errors, reg_form_plus.errors)
print("else 2 chala")
else:
reg_form = RegistrationForm()
reg_form_plus = RegistrationFormPlus()
return render(request, 'bookrepo/signup.html',
{'reg_form': reg_form,
'reg_form_plus': reg_form_plus,
'registered': registered
})
#login_required
def special(request):
return HttpResponse("You are logged in. Nice!")
#login_required
def user_logout(request):
logout(request)
return HttpResponseRedirect(reverse('bookrepo:home'))
def user_login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username, password=password)
if user:
return HttpResponseRedirect(reverse('bookrepo:home'))
else:
print("Someone tried to login and failed.")
print("They used username: {} and password: {}".format(username, password))
return HttpResponse("Invalid login details supplied.")
else:
return render(request, 'bookrepo/login.html', {})
You should use {{ buyer.user.username }} in your template to show the username of a user.
You are trying to extend your django user framework with a One To One Field . new extended Django Model used to store the extra information that relates to the User Model. {{user.username}} gives the username from session.
use this code to authenticate users don't forgot to import
from django.contrib.auth.models import User,auth
def login(request):
if request.method == "POST":
username = request.POST['username']
password = request.POST['password']
user=auth.authenticate(username=username,password=password)
if user is not None:
auth.login(request,user)
return redirect('index')
else:
messages.info(request,'invalid user')
return redirect('login')
else:
return render(request,"login.html")

Creating a User Profile page using OneToOne field with User Model

I'm currently using Django all-auth, and it has a /accounts/profile page which I want to create/populate with a form which updates user information.
I have a Teacher field, which extends the User Model using OneToOne field.
models.py
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, related_name='Teacher')
bio = models.TextField(max_length=500, blank=True)
availability = models.BooleanField(default=False)
teacher_logo = models.FileField()
This teacher model is what I want the user to update in /accounts/profile.
forms.py
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
class TeacherForm(forms.ModelForm):
class Meta:
model = Teacher
fields = ('availability', 'bio','teacher_logo')
views.py
#login_required
#transaction.atomic
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
teacher_form = TeacherForm(request.POST, instance=request.user.teacher)
if user_form.is_valid() and teacher_form.is_valid():
user_form.save()
teacher_form.save()
messages.success(request, _('Your profile was successfully updated!'))
return redirect('users:index')
else:
messages.error(request, _('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
teacher_form = TeacherForm(instance=request.user.teacher)
return render(request, 'accounts/profile.html', {
'user_form': user_form,
'teacher_form': teacher_form
})
template users/profile.html
<form method="post">
{% csrf_token %}
{{ user_form.as_p }}
{{ teacher_form.as_p }}
<button type="submit">Save changes</button>
</form>
urls.py
url(r'^profile/$', views.update_profile, name='Update-Profile')
I can use an update view, but then I need to specify in the URL, which seems an incorrect way of doing it; Also, users will be able to edit someone else profiles.
When I run the above, I get a complaint that 'User' object has no attribute 'teacher'.
When I remove .teacher from TeacherForm(instance=request.user.teacher) It loads the page with the form, but when I update, it still gives me the same complaint (removed in both places in views.py)
EDIT: models.py extra
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Teacher.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.Teacher.save()
you set related name as Teacher, so you need:
teacher_form = TeacherForm(instance=request.user.Teacher)
# ^^^^
or better set related_name to 'teacher'
class Teacher(models.Model):
user = models.OneToOneField(
User,
on_delete=models.PROTECT,
related_name='teacher')
# ^^^