I have a Django project where each user belongs to just one group, Doctor and Nurse but not both.
I showed a form in my front-end to let Admin users add the user profiles, where each user profile is made with First name, Last name, Email, Groups etc.
My challenge is when an admin user assigns a user profile to a group from the front-end that user is not added to the group when I check the Django admin i.e when I click on the on user on the Django admin I can't see the user being assigned to the group.
The important part of my forms.py is this part
from django.contrib.auth.models import Group
groups = forms.ModelChoiceField(label='Role', queryset=Group.objects.all(), widget=forms.Select(attrs={'class':'form-control'}))
The full form can be seen here
from django.contrib.auth.forms import UserCreationForm
from django_starter_app.models import User, Post
from django.contrib.auth.models import Group
class RegisterForm(UserCreationForm):
username = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Enter Username'}))
email = forms.CharField(widget=forms.EmailInput(attrs={'class':'form-control', 'placeholder':'Email'}))
first_name = forms.CharField(label='Firstname', widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Firstname'}))
last_name = forms.CharField(label='Lastname', widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Lastname'}))
groups = forms.ModelChoiceField(label='Role', queryset=Group.objects.all(), widget=forms.Select(attrs={'class':'form-control'}))
password1 = forms.CharField(label='Password', widget=forms.PasswordInput(attrs={'class':'form-control', 'placeholder':'Password'}))
password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput(attrs={'class':'form-control', 'placeholder':'Confirm Password'}))
class Meta():
model = User
fields = ('username', 'email', 'first_name', 'last_name', 'groups', 'password1', 'password2')
def save(self, commit=True):
user = super().save(commit=False)
user.username = self.cleaned_data['username']
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.groups = self.cleaned_data['groups']
user.password1 = self.cleaned_data['password1']
user.password2 = self.cleaned_data['password2']
if commit:
user.save()
return user
on my views
def register_user(request):
if request.method == 'POST':
reg = RegisterForm(request.POST)
if reg.is_valid():
reg.save()
role= reg.cleaned_data.get('groups')
if role== 1:
doctor_group = Group.objects.get(name='Doctor')
reg.groups.add(doctor_group)
return redirect('register_user')
elif role == 2:
nurse_group = Group.objects.get(name='Nurse')
reg.groups.add(nurse_group)
return redirect('register_user')
else:
reg = RegisterForm()
return render(request, 'django_starter_app/register.html', {'register':reg})
Please I will like to know where I am getting it wrong I suspect where I am making the checks
if role == 1:
doctor_group = Group.objects.get(name='Doctor')
reg.groups.add(doctor_group)
return redirect('register_user')
elif role == 2:
nurse_group = Group.objects.get(name='Nurse')
reg.groups.add(nurse_group)
return redirect('register_user')
You can try like below. In my opinion and based on your code there is no need to do elif.
def register_user(request):
if request.method == 'POST':
reg = RegisterForm(request.POST)
if reg.is_valid():
user = reg.save(commit=False)
role = reg.cleaned_data.get('groups')
group = user.set_add(user)
reg.save()
return redirect('register_user')
else:
reg = RegisterForm()
return render(request, 'django_starter_app/register.html', {'register':reg})
reg = RegisterForm(request.POST)
if reg.is_valid():
**user = reg.save()**
role= reg.cleaned_data.get('groups')
if role== 1:
doctor_group = Group.objects.get(name='Doctor')
**user.groups.add(doctor_group)**
ModelName.objects.create( user = user,)
return redirect('register_user')
elif role == 2:
nurse_group = Group.objects.get(name='Nurse')
**user.groups.add(nurse_group)**
ModelName.objects.create( user = user,)
return redirect('register_user')
Once you have validated the form you need a variable to hold the save form.
After getting the groups you need to use that variable.
I have highlighted the required changes in the code.
Related
I am trying to extend the Django User model by creating a user Profile model. When users register for the site, I want them to be able to select what class period they are in. To do this, I've tried to create a form that alters the User model, and a form that alters the Profile model. The problem is that when I try to put both forms into 'users/register.html' I am getting an error that says 'Anonymous User has to data _meta'. Below is my original code that only has the form for altering the User model in 'users/register.html'. How can I configure the registration so that users are able to save to the User and Profile model when they are first signing up for the site?
models.py
class Profile(models.Model):
'''
periods = [
('-','-'),
('1','1'),
('2','2'),
('3','3'),
('4','4'),
('6','6'),
('7','7'),
]
'''
user = models.OneToOneField(User,on_delete=models.CASCADE)
period = models.IntegerField(default=1)
first_name = models.CharField(max_length=100,default='Home')
last_name = models.CharField(max_length=100,default='Simpson')
def __str__(self):
return f'{self.user.username}'
forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username','email','password1','password2']
class UserProfileForm(forms.ModelForm):
periods = [
(1,1),
(2,2),
(3,3),
(4,4),
(6,6),
(7,7),
]
period = forms.ChoiceField(choices=periods)
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
class Meta:
model = Profile
fields = ['first_name','last_name','period']
signals.py
#receiver(post_save,sender=User)
def create_profile(sender,instance,created,**kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save,sender=User)
def save_profile(sender,instance,**kwargs):
instance.profile.save()
views.py
def login(request):
context = {
'title':'Login',
}
return render(request,'users/login.html',context)
def register(request):
if request.method == "POST":
form = UserRegisterForm(request.POST)
if form.is_valid():
email = form.cleaned_data.get('email')
email_domain = re.search("#[\w.]+", email)
if email_domain.group() == EMAIL_DOMAIN:
form.save()
username = form.cleaned_data.get('username')
messages.success(request,f'Account created for {username}! You are now able to sign in.')
return redirect('users-login')
else:
messages.error(request,f'Sorry. You are not authorized to register.')
else:
form = UserRegisterForm()
context = {
'title':'Register',
'form':form
}
return render(request,'users/register.html',context)
This is happening because you are putting both the forms in the register page . When you have not created any user so how you can create a profile and how would you be able to add or retrieve data from it?
Now the solution for that is ,
1 . You create a page for registering the user say "users/register.html" , when they successfully register there, create a signal for creating the Profile for him , then successfully log the user in . Then redirect the just registered user to the profile change page.
Take both the forms in the user register page but do not validate the profile_change form .Create the user in the view and then reference it to the profile_change_form .
A simple code bit for that .
def registerUser(request) :
if request.method == "POST" :
user_form = UserRegisterForm(request.POST)
if user_form.is_valid():
username = request.POST.get("username")
# fields you require
user = User(username = username , password = password)
user.save()
profile_field_objects = request.POST.get("profile_field_data")
profile = Profile(user = user , profile_field_objects = profile_field_objects)
profile.save()
# rest you can code
The views.py
def login_form_student(request):
if request.method =='POST':
roll_no = request.POST.get('username')
password = request.POST.get('pass')
user = authenticate(request, uniq_id=roll_no,password=password)
if user is not None:
login(request,user)
return redirect('/userdetails/prof_page/')
else:
return redirect('/userdetails/thankyoupage/')
return render(request,'user_interaction/login_form.html')
The custom backend
from .models import user_detail
from django.contrib.auth.backends import ModelBackend
class user_auth_custom(ModelBackend):
def authenticate(self,request,uniq_id,password):
flag = 0
try:
user = user_detail.objects.get(roll_no=uniq_id)
if password == user.password:
flag = 1
return user
except flag == 0:
pass
return None
def get_user(self,user_id):
try:
return user_detail.objects.get(pk=user_id)
except user_detail.DoesNotExist:
return None
The models.py
class user_detail(models.Model):
full_name = models.CharField(max_length=264,null=True)
roll_no = models.CharField(max_length=264,unique=True,primary_key=True)
management_name = models.CharField(max_length=264)
contact_no = models.CharField(max_length=10)
email = models.EmailField(max_length=254)
department = models.CharField(max_length=264)
residential_status = models.CharField(max_length=264)
password = models.CharField(max_length=32,default='fmspsg2020')
last_login = models.DateTimeField(auto_now=True)
I get a Validation Error.
['“18pw13” value must be an integer.']
18pw13 is actually a roll_no.
What should i do?
Should i create a new user_id attribute to the model of IntegerField?
Why don't you just inherit the user model from djangos AbstactBaseUser, it makes it alot easier, because you get all the user functionallity that the Django user provides.
Here is a link:
https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#specifying-a-custom-user-model
I this in my forms.py
from django.contrib.auth.models import User
from .models import DirectReferral, IndirectReferral
class RegistrationForm(forms.Form):
username = forms.RegexField()
referrer = forms.CharField()
def clean_username(self):
try:
user = User.objects.get(username__iexact=self.cleaned_data['username'])
except User.DoesNotExist:
return self.cleaned_data['username']
raise forms.ValidationError(_("The username already exists."))
How do I check if the referrer instance exist in User Database?
I did the code below but it has an error of TypeError at /register/
def clean_referrer(self):
try:
user = User.objects.get(username__iexact=self.cleaned_data['referrer'])
except User.DoesNotExist:
raise forms.ValidationError(_("User does not exist."))
return self.cleaned_data['referrer']
I did also try to do this but I get an error of string indices must be integers.
def clean(self):
if 'referrer' in self.cleaned_data:
if User.objects.filter(username=self.cleaned_data['referrer']).exists():
return self.cleaned_data['referrer']
else:
raise forms.ValidationError(_("Referrer doesn't exist!"))
Here is my models.py
from django.contrib.auth.models import User
class DirectReferral(models.Model):
name = models.OneToOneField(User, primary_key=True)
referrer = models.ForeignKey(User, related_name="direct_referrals")
def __str__(self):
return self.name.username
class IndirectReferral(models.Model):
name = models.OneToOneField(User, primary_key=True)
referrer = models.ForeignKey(User, related_name="indirect_referrals")
def __str__(self):
return self.name.username
How do I get the referrer instance data?
Here is my views.py (Updated)
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username']
)
ref_user = User.objects.get(
username=form.cleaned_data['referrer'])
direct = DirectReferral.objects.create(
name = user,
referrer = ref_user
)
if DirectReferral.objects.filter(referrer=user).exists():
indirect = IndirectReferral.objects.create(
name = user,
referrer = ref_user
)
return HttpResponseRedirect('/register/success/')
This is what you need:
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
)
# Assuming form.cleaned_data['referrer'] has usernames.
# If it has pks then just use pk=form.cleaned_data['referrer']
# You should wrap this in a try-except
dr_user = User.objects.get(username=form.cleaned_data['referrer'])
direct_referral = DirectReferral.objects.create(
user=user,
referrer=dr_user,
)
# Wrap this in try-except as well.
ir_user = dr_user.direct_referral.referrer
indirect_referral = IndirectReferral.objects.create(
user=user,
referrer=ir_user,
)
return HttpResponseRedirect('/register/success/')
Also, this looks like a job for post_save signals. Check them out here: https://docs.djangoproject.com/es/1.9/ref/signals/#post-save
I think the problem is here
DirectReferral.name = username,
DirectReferral.referrer = referrer,
IndirectReferral.name = username,
IndirectReferral.referrer = referrer,
you have to create objects first and than assign the values like this:
direct_referal = DirectReferral()
direct_referal.name=....
....
direct_referal.save()
also from your code, it seems that you are assigning string to user object:
DirectReferral.name = username
here username is string and DirectReferral.name is User object. You have to get this user object first and than assign to referral's name.
direct_referal = DirectReferral()
direct_referal.name=User.objects.get(username=username)
....
direct_referal.save()
I create an userprofile as following:
class UserProfile(models.Model):
# This field is required:
user = models.OneToOneField(User, related_name="User can view study permission")
# Other fields:
phone = models.CharField(max_length=20)
disease = models.ManyToManyField(Disease)
date_assigned = models.DateTimeField("Date Assigned")
query = models.ManyToManyField(Query, blank=True)
def __unicode__(self):
studies = ', '.join(study.name for study in self.disease.all())
return "%s can view %s" % (self.user, studies)
It's extended information for users.
Now I am creating a admin page by myself to allow the admin to update the users account information which included the above user profile.
The form and code for this is as following:
def edit(request, user_id):
"""Edit a user's details"""
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
user = None
# user exists:
if user:
# Initial form field data:
initial={'user_id': user_id,
'username': user.username,
'fname': user.first_name,
'lname': user.last_name,
'email': user.email,
'phone': user.get_profile().phone,
'groups': user.groups.all(),
'studies': user.get_profile().disease.all(),
'is_admin': user.is_staff,
'is_active': user.is_active
}
request.breadcrumbs(
(_("Home"), reverse('home')),
(_("All Users"), reverse('all users')),
(_("User Details"), reverse('user details', args=[user_id])),
)
if request.method == "GET":
form = UserProfileEditForm(initial=initial,extra=request.user.id)
response = {'heading': 'Edit', 'form': form}
return render_to_response('accounts/edit.html',
response,
context_instance=RequestContext(request)
)
elif request.method == "POST":
form = UserProfileEditForm(request.POST,extra=request.user.id)
if form.is_valid():
Log().add(request, "Edit", "W", "userprofile", user_id)
if form.cleaned_data['password1'] and form.cleaned_data['password2']:
user.set_password(form.cleaned_data['password1'])
user.username = form.cleaned_data['username']
user.first_name = form.cleaned_data['fname']
user.last_name = form.cleaned_data['lname']
user.email = form.cleaned_data['email']
user.groups = form.cleaned_data['groups']
user.is_staff = form.cleaned_data['is_admin']
user.is_active = form.cleaned_data['is_active']
user.save()
# Oddly to make the extra fields found in UserProfile are saved, you
# have to call get_profile().ATTRIBUTE, assign a value, then call
# get_profile().save(). Calling user.save() as the last step won't
# save any changes made to UserProfile:
disease_pks = form.cleaned_data['studies']
user.get_profile().disease = Disease.objects.filter(pk__in=disease_pks)
user.get_profile().phone = form.cleaned_data['phone']
user.get_profile().save()
return HttpResponseRedirect("/accounts/view/%s" % user_id)
else:
# form is not valid:
return render_to_response("accounts/edit.html",
{'form': form, 'heading': 'Edit'},
context_instance=RequestContext(request)
)
# user does not exist:
else:
error = "User #%s cannot be found. Press the 'BACK' button on your browser." % user_id
return HttpResponseRedirect(reverse('DigitalRecords.views.error', args=(error,)))
class UserProfileEditForm(forms.Form):
user_id = forms.IntegerField(widget=forms.HiddenInput())
username = forms.CharField(label=_("Username"))
password1 = forms.CharField(label=_("Password"),
widget=forms.PasswordInput(),
required=False
)
password2 = forms.CharField(label=_("Password (again)"),
widget=forms.PasswordInput(),
required=False
)
fname = forms.CharField(label=_("First name"))
lname = forms.CharField(label=_("Last name"))
email = forms.EmailField()
phone = forms.CharField(label=_("Phone"))
is_admin = forms.BooleanField(label=_("Is an administrator?"), required=False)
is_active = forms.BooleanField(label=_("Is an active user?"), required=False)
groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all(),
widget=forms.CheckboxSelectMultiple()
)
#Attach a form helper to this class
helper = FormHelper()
helper.form_id = "edituserprofile"
helper.form_class = "userprofile"
#Add in a submit and reset button
submit = Submit("Save", "Save Changes")
helper.add_input(submit)
reset = Reset("Reset", "Reset")
helper.add_input(reset)
def __init__(self, *args,**kwargs):
extra = kwargs.pop('extra')
super(UserProfileEditForm, self).__init__(*args, **kwargs)
self.fields["studies"] = forms.ModelMultipleChoiceField(queryset=User.objects.get(id=extra).get_profile().disease.all(),
widget=forms.CheckboxSelectMultiple()
)
def clean_username(self):
"""Check if the username does not already exist"""
username = self.cleaned_data['username']
user_id = self.cleaned_data['user_id']
# handle:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return self.cleaned_data['username']
else:
if user.id == user_id:
return self.cleaned_data['username']
else:
raise forms.ValidationError('User "%s" already exists' % username)
def clean_fname(self):
fname = self.cleaned_data['fname']
# Remove any diacritics/accented characters:
fname = strip_diacritic(fname).strip()
# Match names that may have hyphens, apostrophes, or periods in them.
# example: John Doe, O'Brien, Leroy-Brown, Cpt. James T. Kirk
pattern = '^([a-zA-Z]+(?:\.)?(?:[\-\' ][a-zA-Z]+(?:\.)?)*)$'
# match the regex to the input string.
results = re.match(pattern, str(fname))
if results == None:
raise ValidationError(u'%s is not a valid name' % fname)
return fname
def clean_lname(self):
"""
Determine if the patient's name is valid. It removed any accented/diacritic
characters and replaces them with the base character for simplicity. Names with
hyphens and/ore apostrophes like Hanna-Barbara and O'Brien are allowed. If the
check fails a validation error is raised.
"""
lname = self.cleaned_data['lname']
# Remove any diacritics/accented characters:
lname = strip_diacritic(lname).strip()
# Match names that may have hyphens, apostrophes, or periods in them.
# example: John Doe, O'Brien, Leroy-Brown, Cpt. James T. Kirk
pattern = '^([a-zA-Z]+(?:\.)?(?:[\-\' ][a-zA-Z]+(?:\.)?)*)$'
# match the regex to the input string.
results = re.match(pattern, str(lname))
if results == None:
raise ValidationError(u'%s is not a valid name' % lname)
return lname
def clean(self):
"""Check if password1 and password2 match"""
cleaned_data = self.cleaned_data
password1 = cleaned_data.get('password1')
password2 = cleaned_data.get('password2')
# Notice it's an 'or' condition because a password change is optional:
if password1 or password2:
if password1 != password2:
msg = "Passwords do not match"
self._errors['password1'] = self.error_class([msg])
self._errors['password2'] = self.error_class([msg])
del cleaned_data['password1']
del cleaned_data['password2']
return cleaned_data
return self.cleaned_data
It works fine if an administrator trying to edit other user's account information.
However, when an administrator tried to update his own account. The disease field will always be blank whatever this field was changed or not.
Does anyone the reason and how should I change my code?
Thank you very much.
I'm not sure about other code, but these lines in your view does not seem appropriate.
user.get_profile().disease = Disease.objects.filter(pk__in=disease_pks)
user.get_profile().phone = form.cleaned_data['phone']
user.get_profile().save()
You are getting new profile object each time by calling user.get_profile() and updating it. Not saving it, instead a new object. So change it to
profile = user.get_profile()
profile.disease = Disease.objects.filter(pk__in=disease_pks)
profile.phone = form.cleaned_data['phone']
profile.save()
I've been trying to figure this out for hours, and believe me, I really looked everywhere on Stack Overflow.
In my UserProfile, I have a ForeignKey reference to another model (called "Company"), and upon registration, I create a new Company and point my UserProfile ForeignKey to that Company.
models.py is as follows:
class UserProfile(models.Model):
company = models.ForeignKey(Company)
title = models.CharField(max_length = 50, default = '')
user = models.OneToOneField(User, default = 0, null = True)
class Company(models.Model):
"""A company profile."""
name = models.CharField(max_length = 50)
I use a Form to do the signing up. Here's the form:
class SignupForm(ModelForm):
name = forms.CharField(label = "Name")
company = forms.CharField(max_length = 50)
email = forms.EmailField(label = "Email")
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ("name", "company", "email", "password")
def save(self, commit=True):
user = super(SignupForm, self).save(commit=False)
name = self.cleaned_data["name"].split()
if len(name) == 1:
# User did not enter a last name.
user.first_name = name
else:
user.first_name, user.last_name = name
user.email = self.cleaned_data["email"]
user.set_password(self.cleaned_data["password"])
user.username = user.email
if commit:
user.save()
return user
and here's the signup view:
def signup(request):
if request.method == 'POST':
form = SignupForm(request.POST)
if form.is_valid():
# Check if email has already been used for an account.
email = request.POST['email']
existing_account = User.objects.filter(email = email)
if existing_account:
form = SignupForm()
return render_to_response('registration/signup.html',
{ 'form': form,
'error': 'duplicate_email',
})
# Otherwise, save the form and create a new user.
new_user = form.save()
company = Company(name=request.POST['company'])
company.save()
user_profile = new_user.get_profile()
user_profile.company = company
user_profile.save()
new_user = authenticate(
username = email,
password = request.POST['password']
)
# Log the user in automatically.
login(request, new_user)
# Redirect user to Thank You page.
return HttpResponseRedirect('/thanks')
else:
form = SignupForm()
return render_to_response('registration/signup.html', {
'form': form,
})
The error I am getting is telling me that company_id cannot be null. I clearly add a new Company. Please let me know what you think might be wrong.
Thanks
I've had this exact error today, with no reason, except that it was caused by SQLite. With SQLite, the id field of one table went from INTEGER PRIMARY KEY to INTEGER. If you're using SQLite, try deleting the offending table and recreate it with a syncdb.
What is the value of
user_profile = new_user.get_profile()
?
Not sure if this feels too hackish for your tastes but perhaps you could create/save the Company object and pass it in to your SignupForm.save() method as a positional/keyword argument.
The issue you'd get there is that you'd be expecting a CharField and you'd be passing in a company object. So you'd probably want to give company.pk to the company field in your form.