I got an issue when using HiddenField, i cant'get the data from it
the form is rendred properly
{{form.user_id(value=user.id)}}
class EditUserForm(FlaskForm):
username = StringField("Username", validators=[
DataRequired(), Length(min=5, max=20)])
email = StringField("Email", validators=[Email()])
password = PasswordField("Password", validators=[DataRequired()])
password_confirmation = PasswordField("Confirm Password", validators=
[EqualTo('password')])
role = SelectField('Role',
choices=[(Role.name, Role.value)
for Role in Roles],
default=Roles.admin.value)
user_id = HiddenField("user.id")
submit = SubmitField("Save")
def validate_username(self, username):
#run validation only if the username has changed
user = User.query.get(self.user_id.data)
if username.data != user.username:
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError(f"{self.username.data} is already taken")
def validate_email(self, email):
#run validation only if the email has changed
user = User.query.get(self.user_id.data)
if email.data != user.email:
email = User.query.filter_by(email=email.data).first()
if email:
raise ValidationError(f"{self.email.data} is already taken")
when i tried to use StringField and set type="hidden"
{{form.user_id(type="hidden, value=user.id)}}
it does work
Try to use in html-template:
{{ form.user_id() }}
Create hidden form field:
user_id = HiddenField('user_id', default=user.id)
Related
I am trying to update a django ModelForm with email as a field. In the create form, I am checking if the field is already present in database, if so raise validation error. That part is working as expected.
however, if I use the same check in update form, it throws validation error as the existing record has the given email. Is there a direct method to ensure this validation?
forms.py
class MyObjCreateForm(forms.ModelForm):
first_name = forms.CharField(max_length=50, label='First name')
last_name = forms.CharField(max_length=50, label='Last name')
email = forms.EmailField(label='Email')
location = forms.ChoiceField(choices=TP_TM_Location, label='Location')
designation = forms.ChoiceField(choices=TP_TM_Designation, label='Designation')
date_of_joining = forms.DateField(label='Date of joining',
widget=forms.TextInput(attrs={'type': 'date'}),
initial=date.today())
username = forms.CharField(label='Login username')
password = forms.CharField(widget=forms.PasswordInput(), label='Login password')
current_status = forms.ChoiceField(choices=TP_Status, label='Current status')
def clean_email(self):
email = self.cleaned_data.get('email')
try:
match = MyObj.objects.get(email=email)
raise forms.ValidationError("email already exists in system. Please check the existing list.")
except MyObj.DoesNotExist:
return email
class MyObjUpdateForm(forms.ModelForm):
first_name = forms.CharField(max_length=50, label='First name')
last_name = forms.CharField(max_length=50, label='Last name')
email = forms.EmailField(label='Email')
location = forms.ChoiceField(choices=TP_TM_Location, label='Location')
designation = forms.ChoiceField(choices=TP_TM_Designation, label='Designation')
date_of_joining = forms.DateField(label='Date of joining',
widget=forms.TextInput(attrs={'type': 'date'}),
initial=date.today())
current_status = forms.ChoiceField(choices=TP_Status, label='Current status')
username = forms.CharField(label='Login username')
def clean_email(self):
email = self.cleaned_data.get('email')
try:
match = MyObj.objects.get(email=email)
raise forms.ValidationError("email already exists in system. Please check the existing list.")
except MyObj.DoesNotExist:
return email
Thanks,
For this you need to exclude the current user from the queryset like this .
def clean_email(self):
email = self.cleaned_data.get('email')
email_match = MyObj.objects.filter(email=email).exclude(pk=self.instance.pk)
if self.instance and self.instance.pk and not email_match:
return email
else:
raise forms.ValidationError("email already exists in system. Please check the existing list.")
You need to remove the validation in update class. You can simply return the email without validation.
def clean_email(self):
return self.cleaned_data.get('email')
However, if you want to check if the user already has the new email,then you can use following validation method:
def clean_email(self):
email = self.cleaned_data.get('email')
if self.instance.email == email:
raise forms.ValidationError("Email already present.")
return email
Test doesn't give status_code 302 in user profile UpdateView and so there's no updates occurs on the object
the model code
class User(AbstractBaseUser, PermissionsMixin):
'''
This a replaced user profile instead of the default django one
'''
language_choices=[('en',_('English')),('se',_('Swedish'))]
email=models.CharField(verbose_name=_('Email'), max_length=128, blank=False, unique=True)
first_name=models.CharField(verbose_name=_('First Name'), max_length=128)
last_name=models.CharField(verbose_name=_('Last Name'), max_length=128)
joined_at=models.DateField(
verbose_name=_('Joined at'),
auto_now_add=True,
blank=False
)
language=models.CharField(
verbose_name=_('Language'),
max_length=2,
choices=language_choices,
default=language_choices[0][0]
)
active=models.BooleanField(verbose_name=_('Active'), default=False)
is_superuser=models.BooleanField(verbose_name=_('Is Superuser'), default=False)
is_active=models.BooleanField(verbose_name=_('Is Active'), default=True)
is_staff=models.BooleanField(verbose_name=_('Is Staff'), default=False)
The form code
class EditUserForm(UserChangeForm):
'''
Profile form to update existing user information
'''
# error message for email matches
error_messages = {
'email_mismatch': _("The two email fields didn't match."),
}
# create field for email
email1 = forms.EmailField(
label=_("Email"),
widget=forms.EmailInput,
help_text=_("If you change your email your account will be inactive untill your reactivate by email link."),
)
# get the email from confirmed email field
email2 = forms.EmailField(
label=_("Confirm Email"),
widget=forms.EmailInput,
help_text=_("Enter the same email as before, for verification."),
)
# hide password field
password = ReadOnlyPasswordHashField(label="Password")
class Meta:
'''
Initial fields and model for the form
'''
model = models.User
fields = ('first_name','last_name','email1','email2', 'language')
def clean_email2(self):
'''
Method for if email and confirmed email are the same
This method works when confirmed email cleared
'''
# get the email from email field
email1 = self.cleaned_data.get("email1")
# get the email from confirmed email field
email2 = self.cleaned_data.get("email2")
# check if both emails are equal
if email1 and email2 and BaseUserManager.normalize_email(email1) != BaseUserManager.normalize_email(email2):
# give an error message if emails not matches
raise forms.ValidationError(
self.error_messages['email_mismatch'],
code='email_mismatch')
# return the confirmed email
return BaseUserManager.normalize_email(email2)
def save(self, commit=True):
'''
Method tosave the edited user data
'''
# get the initial method
user = super().save(commit=False)
# set the email on the model field
user.email = self.cleaned_data["email1"]
# save edited user data
if commit:
user.save()
return user
def __init__(self, *args, **kwargs):
'''
Method for initial values and functions for the SignUp form class
'''
# get user data from User model
user = get_user_model().objects.get(email=kwargs['instance'])
# get the initial form class values
super(EditUserForm, self).__init__(*args, **kwargs)
# Add the current email as the inital email
self.fields['email1'].initial = user.email
# Add the current email as the intial confirmed email
self.fields['email2'].initial = user.email
# Add help text in the password field for change
self.fields['password'].help_text=(
_("Raw passwords are not stored, so there is no way to see "
"this user's password, but you can change the password "
"using this form.")
.format(reverse(
'core:ChangePassword',
kwargs={'pk':user.pk})))
and the view code
class EditUser(UserPassesTestMixin, UpdateView):
'''
Class view to update user details
'''
# used template
template_name = 'core/edit.html'
# View model
model = models.User
# View form
form_class = forms.EditUserForm
def test_func(self):
return self.request.user == get_user_model().objects.get(pk=self.kwargs['pk'])
def get_success_url(self):
'''
Metho to redirect after a valid form
'''
# check if the email is verified
if self.request.user.active:
# get the user key
pk=self.request.user.pk
# redirect to profile details
return reverse_lazy('core:details', kwargs={'pk':pk})
else:
# send a verification email
return SendActivationEmail(self.request, self.request.user)
the test code
self.viewuser_url = reverse('core:details', kwargs={'pk':self.user.pk})
self.edituser_url = reverse('core:edit', kwargs={'pk':self.user.pk})
def test_edit_user_post(self):
first_name = 'Osama'
response = self.client.post(self.edituser_url,
data={
'first_name': first_name,
'last_name': self.last_name,
'email': self.email,
})
self.assertRedirects(response, self.viewuser_url)
self.user.refresh_from_db()
self.assertEqual(self.user.first_name, first_name)
I tried to get assertEqual for the status code and it gitves me 200 instead of 302
also I tried to enter the form details instead of model details and it gives me an error
The get test works fine and also permission test works great.. all the models, forms and urls test works perfect.
I don't know how I can test this..
If the form isn't valid, then the form will be re-rendered with errors and you'll get a 200 response.
To debug the problem, check response.context['form'].errors in your test to see what the problem is.
response = self.client.post(self.edituser_url,
data={
'first_name': first_name,
'last_name': self.last_name,
'email': self.email,
})
print(response.context['form'].errors
Your view uses EditUserForm, but you are not posting any values for email1 or email2, so there is probably something in the errors about missing data.
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'm trying to implement a user registration page in Django. Seems like a simple task, but I'm getting an error I just don't understand. Here's my view:
def registration_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username = form.cleaned_data['username'],
password = form.cleaned_data['password1'],
email = form.cleaned_data['email1']
)
return HttpResponseRedirect('/register/success/')
else:
form = RegistrationForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('registration/registration_page.html', variables)
And here's my registration form:
class RegistrationForm(forms.Form):
username = forms.CharField(label = u'Username', max_length = 30, error_messages={'required': 'A username is required.'})
email1 = forms.EmailField(label = u'Email Address', error_messages={'required': 'An email address is required.'})
email2 = forms.EmailField(label = u'Email Address confirmation', error_messages={'required': 'A confirmed email address is required.'})
password1 = forms.CharField(label = u'Password', widget = forms.PasswordInput(), error_messages={'required': 'A password is required.'})
password2 = forms.CharField(label = u'Password confirmation', widget = forms.PasswordInput(), error_messages={'required': 'A confirmed password is required.'})
def clean_password2(self):
if 'password1' in self.cleaned_data:
password1 = self.cleaned_data['password1']
password2 = self.cleaned_data['password2']
if password1 == password2:
return password2
raise forms.ValidationError('Passwords must be identical. Remember, passwords are case-sensitive.')
def clean_email2(self):
if 'email1' in self.cleaned_data:
email1 = self.cleaned_data['email1']
email2 = self.cleaned_data['email2']
if email1 == email2:
if User.objects.get(email = email1):
raise forms.ValidationError("The email address '%s' is already associated with an account. This typically means you created an account in the past. Please use it." % email1)
else:
return email2
raise forms.ValidationError('Email addresses must be identical.')
def clean_username(self):
if 'username' in self.cleaned_data:
username = self.cleaned_data['username']
if not re.search(r'^\w+$', username):
raise forms.ValidationError('Username can only contain letters, numbers, and the underscore characters.')
try:
User.objects.get(username = username)
except User.DoesNotExist:
return username
raise forms.ValidationError("Username '%s' is already taken." % username)
The problem pops up in the form.is_valid() and clean_username(). If I add a user that does or does not exist (it doesn't matter if the user exists or not, behavior is the same) and populate the form with data I know is valid, the code should simply return username from clean_username() and form.is_valid() should be True. Instead, I get an error page that looks like this:
DoesNotExist at /register/
User matching query does not exist.
Request Method: POST
Request URL: http://127.0.0.1:8000/register/
Django Version: 1.4.1
Exception Type: DoesNotExist
Exception Value:
User matching query does not exist.
Exception Location: /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/db/models/query.py in get, line 366
Python Executable: /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
Python Version: 2.7.2
Python Path:
['/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/distribute-0.6.28-py2.7.egg',
'/Library/Python/2.7/site-packages/bpython-0.10.1-py2.7.egg',
'/Library/Python/2.7/site-packages/Pygments-1.5-py2.7.egg',
'/Library/Python/2.7/site-packages/django_registration-0.8-py2.7.egg',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/PIL',
'/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info',
'/Library/Python/2.7/site-packages']
Server time: Sun, 28 Oct 2012 00:51:58 -0400
It's got to be something small I'm missing, but I can't see it.
In your code
def clean_email2(self):
...
# here
if User.objects.get(email = email1):
raise forms.ValidationError("The email address '%s' is already associated with an account. This typically means you created an account in the past. Please use it." % email1)
...
The get() could cause DoesNotExist but is not captured. Is that the issue?
Also, please provide full traceback of the line making the issue.
Furthermore, according to the doc, its better to put the logic of validating passwords & emails to the clean() method.
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.