AttributeError: 'AcceptInvite' object has no attribute 'email' - django

Im doing some unit testing in my Django project, and am getting error
"AttributeError: 'SignUp' object has no attribute 'email'"
when I run this test.
def test_signup(self):
response = self.c.post('/accounts/signup/', {'email': 'test#test.com', 'password': 'test123', 'password_conf': 'test123',
'org_name': 'test org', 'org_username': 'test org username', 'invite': '4013'})
code = response.status_code
self.assertTrue(code == 200)
The view this is testing simply takes a signup form, and creates a new account with it.
def signup(request):
# """Register a new account with a new org."""
if request.method == "POST":
form = SignUp(request.POST)
if not form.email or not form.password:
raise Exception("Email and Password are required")
if form.password != form.password_conf:
raise Exception("Password does not match confirmation")
if not form.org_name or not form.org_username:
raise Exception('Organization name and username are required')
if not form.invite:
raise Exception('Invitation code is required')
if form.is_valid():
cleaned_data = form.cleaned_data
email = cleaned_data['email']
password = cleaned_data['password']
org_name = cleaned_data['org_name']
org_username = cleaned_data['org_username']
invite_token = cleaned_data['invite']
invitation = OrgInvite.objects.get(token=invite_token)
if invitation.used:
raise Exception("invitation code is invalid")
account = Account(email=email, password=password)
account.save()
org = Org(org_name=org_name, org_username=org_username)
org.save()
invitation.used = False
invitation.save()
login(request)
# Send Email
md = mandrill.Mandrill(settings.MANDRILL_API_KEY)
t = invite_token.replace(' ', '+')
url = "https://www.humanlink.co/verify/{}".format(t)
message = {
'global_merge_vars': [
{'name': 'VERIFICATION_URL', 'content': url},
],
'to': [
{'email': account.email},
],
}
message['from_name'] = message.get('from_name', 'Humanlink')
message['from_email'] = message.get('from_email', 'support#humanlink.co')
try:
md.messages.send_template(
template_name='humanlink-welcome', message=message,
template_content=[], async=True)
except mandrill.Error as e:
logging.exception(e)
raise Exception('Unknown service exception')
The Signup form has an email field, and the data in request.POST should have the email I am sending it with my Client's post method being used in my unit test, so I am really not sure why it still wouldn't have an 'email' attribute.
Form:
class SignUp(forms.Form):
email = forms.EmailField()
password = forms.CharField()
password_conf = forms.CharField()
org_name = forms.CharField()
org_username = forms.CharField()
invite = forms.CharField()

You code suffers from multiple errors. To address your question, in your view method signup you were creating a form, but you shouldn't do form.email or form.password because that's not how django handles form data.
Other related issues, first, you need to call form.is_valid() before you could get any data from form object. Even so, you should use form.cleaned_data['email'] to access the form data.
Secondly. You shouldn't do empty check like that. If you put:
email = forms.EmailField(required=True)
django will automatically verify the emptiness for you already.
Thirdly, raising Exception in views.py method doesn't get your form to return the message to the template you want. If you have custom form validation, you should do it in form class's clean method.
Please check django doc about how to use form properly.

Related

PasswordResetForm doesn't send email

I'm working on a user system on an app. Everything works fine except one thing.
When I create a user, I generate a password to keep in database and send an email to the new created user to set his own password.
So the password is in the database and I use PasswordResetForm to send an email of password reseting.
Here is the code I use:
reset_password_form = PasswordResetForm(data={'email': user.email})
if reset_password_form.is_valid():
reset_password_form.save(request=request,
use_https=True,
from_email="Webmaster#mysite.com",
html_email_template_name='main/password_reset/password_reset_email.html',
subject_template_name="main/password_reset/password_reset_subject.txt")
And here is the problem, no email is sent.
So to clarify, I already use this Form in case we click on "I forgot my password" and it works very well. So There is no problems of settings for the email server.
As well the reset_password_form.is_valid() is true I can breakpoint in the if.
The user.email exists and contain a real and correct email adress.
I have the feeling that when I call save() on my form it doesn't send the message, did I do a mistake thinking it will do?
My complete view:
def adduser(request, id_user=None):
modify_user = User.objects.get(id=id_user) if id_user is not None else None
if request.method == 'POST':
if modify_user is not None:
userform = EditUserForm(request.POST, instance=modify_user)
else:
userform = AddUserForm(request.POST, instance=modify_user)
profileform = AddProfileForm(request.POST, request.FILES,
instance=modify_user.profile if modify_user is not None else None)
if userform.is_valid() and profileform.is_valid():
user = userform.save(commit=False)
profileuser = profileform.save(commit=False)
if modify_user is not None:
user.save(update_fields=['username', 'first_name', 'last_name', 'email'])
else:
reset_password_form = PasswordResetForm(data={'email': user.email})
if reset_password_form.is_valid():
reset_password_form.save(request=request,
use_https=True,
from_email="Webmaster#mantadivegiliair.com",
html_email_template_name='main/password_reset/password_reset_email.html',
subject_template_name="main/password_reset/password_reset_subject.txt")
user.set_password(User.objects.make_random_password(length=6))
user.save()
profileuser.created_by = request.user
profileuser.user = user
profileuser.save()
profileform.save_m2m()
if modify_user is not None:
messages.add_message(request, messages.SUCCESS,
"{} {} has been updated".format(user.first_name, user.last_name))
else:
messages.add_message(request, messages.SUCCESS,
"{} {} has been created".format(user.first_name, user.last_name))
return redirect('profile', user.id)
else:
for field in userform.errors:
if field == "__all__":
userform['confirm_password'].field.widget.attrs['class'] += ' error'
else:
userform[field].field.widget.attrs['class'] += ' error'
for field in profileform.errors:
profileform[field].field.widget.attrs['class'] += ' error'
else:
userform = AddUserForm(instance=modify_user)
profileform = AddProfileForm(instance=modify_user.profile if modify_user is not None else None)
return render(request, 'main/add_user.html', locals())
The user isn't saved yet when you save the PasswordResetForm. So the save() method won't do anything because it will try to fetch users in the database with the email submitted.
You just need to change the order in which you do things:
else: # user is new user
user.set_password(User.objects.make_random_password(length=6))
user.save()
reset_password_form = ...
reset_password_form.save(...)
profileuser.user = user
...

Django Custom User Email Account Verification

I am looking to add email account verification in Django. I have attempted using the django-registration app to do so, but it doesn't appear that it has been updated to be fully compatible with custom user models which causes too many problems. Is there another reliable and well-documented app out there which will allow me to send a verification email on user registration in django?
How I handle the email registration personally:
First of all, my Profile extending Django Users (models.py):
class Profile(models.Model):
user = models.OneToOneField(User, related_name='profile') #1 to 1 link with Django User
activation_key = models.CharField(max_length=40)
key_expires = models.DateTimeField()
In forms.py, the Registration class :
class RegistrationForm(forms.Form):
username = forms.CharField(label="",widget=forms.TextInput(attrs={'placeholder': 'Nom d\'utilisateur','class':'form-control input-perso'}),max_length=30,min_length=3,validators=[isValidUsername, validators.validate_slug])
email = forms.EmailField(label="",widget=forms.EmailInput(attrs={'placeholder': 'Email','class':'form-control input-perso'}),max_length=100,error_messages={'invalid': ("Email invalide.")},validators=[isValidEmail])
password1 = forms.CharField(label="",max_length=50,min_length=6,
widget=forms.PasswordInput(attrs={'placeholder': 'Mot de passe','class':'form-control input-perso'}))
password2 = forms.CharField(label="",max_length=50,min_length=6,
widget=forms.PasswordInput(attrs={'placeholder': 'Confirmer mot de passe','class':'form-control input-perso'}))
#recaptcha = ReCaptchaField()
#Override clean method to check password match
def clean(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password1 != password2:
self._errors['password2'] = ErrorList([u"Le mot de passe ne correspond pas."])
return self.cleaned_data
#Override of save method for saving both User and Profile objects
def save(self, datas):
u = User.objects.create_user(datas['username'],
datas['email'],
datas['password1'])
u.is_active = False
u.save()
profile=Profile()
profile.user=u
profile.activation_key=datas['activation_key']
profile.key_expires=datetime.datetime.strftime(datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
profile.save()
return u
#Sending activation email ------>>>!! Warning : Domain name is hardcoded below !!<<<------
#The email is written in a text file (it contains templatetags which are populated by the method below)
def sendEmail(self, datas):
link="http://yourdomain.com/activate/"+datas['activation_key']
c=Context({'activation_link':link,'username':datas['username']})
f = open(MEDIA_ROOT+datas['email_path'], 'r')
t = Template(f.read())
f.close()
message=t.render(c)
#print unicode(message).encode('utf8')
send_mail(datas['email_subject'], message, 'yourdomain <no-reply#yourdomain.com>', [datas['email']], fail_silently=False)
Now, in views.py, we need to handle all that, let's go :
The register view:
def register(request):
if request.user.is_authenticated():
return redirect(home)
registration_form = RegistrationForm()
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
datas={}
datas['username']=form.cleaned_data['username']
datas['email']=form.cleaned_data['email']
datas['password1']=form.cleaned_data['password1']
#We generate a random activation key
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
usernamesalt = datas['username']
if isinstance(usernamesalt, unicode):
usernamesalt = usernamesalt.encode('utf8')
datas['activation_key']= hashlib.sha1(salt+usernamesalt).hexdigest()
datas['email_path']="/ActivationEmail.txt"
datas['email_subject']="Activation de votre compte yourdomain"
form.sendEmail(datas)
form.save(datas) #Save the user and his profile
request.session['registered']=True #For display purposes
return redirect(home)
else:
registration_form = form #Display form with error messages (incorrect fields, etc)
return render(request, 'siteApp/register.html', locals())
The activation views :
#View called from activation email. Activate user if link didn't expire (48h default), or offer to
#send a second link if the first expired.
def activation(request, key):
activation_expired = False
already_active = False
profile = get_object_or_404(Profile, activation_key=key)
if profile.user.is_active == False:
if timezone.now() > profile.key_expires:
activation_expired = True #Display: offer the user to send a new activation link
id_user = profile.user.id
else: #Activation successful
profile.user.is_active = True
profile.user.save()
#If user is already active, simply display error message
else:
already_active = True #Display : error message
return render(request, 'siteApp/activation.html', locals())
def new_activation_link(request, user_id):
form = RegistrationForm()
datas={}
user = User.objects.get(id=user_id)
if user is not None and not user.is_active:
datas['username']=user.username
datas['email']=user.email
datas['email_path']="/ResendEmail.txt"
datas['email_subject']="Nouveau lien d'activation yourdomain"
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
usernamesalt = datas['username']
if isinstance(usernamesalt, unicode):
usernamesalt = usernamesalt.encode('utf8')
datas['activation_key']= hashlib.sha1(salt+usernamesalt).hexdigest()
profile = Profile.objects.get(user=user)
profile.activation_key = datas['activation_key']
profile.key_expires = datetime.datetime.strftime(datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
profile.save()
form.sendEmail(datas)
request.session['new_link']=True #Display: new link sent
return redirect(home)
Finally, in urls.py:
url(r'^register/$', 'register'),
url(r'^activate/(?P<key>.+)$', 'activation'),
url(r'^new-activation-link/(?P<user_id>\d+)/$', 'new_activation_link'),
With all that you should have something to start with, use the appropriate templatetags in the .txt emails and HTML and it should work.
NB: This code isn't perfect, there is duplication (for instance, the generation of the random key could be defined in a function), but it does the job. Also: the activation key is not generated using proper cryptographic functions. An alternative is to use a function like the following to generate the keys:
from django.utils.crypto import get_random_string
def generate_activation_key(username):
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!##$%^&*(-_=+)'
secret_key = get_random_string(20, chars)
return hashlib.sha256((secret_key + username).encode('utf-8')).hexdigest()
NB2: Django send_mail doesn't provide any tools to authenticate your emails. If you want to authenticate your emails (DKIM, SPF), I advise you to look into this: https://djangosnippets.org/snippets/1995/
NB3: There is a security issue with the view new_activation_link: it should check if the user requesting the re-send is the right one and also if he isn't already authenticated. I let you correct that.
You may also be interested in the simple but powerful django-verified-email-field.
Simply use VerifiedEmailField in Your forms:
from django import forms
from verified_email_field.forms import VerifiedEmailField
class RegistrationForm(forms.ModelForm):
email = VerifiedEmailField(label='email', required=True)
Or in Your models:
from django.db import models
from verified_email_field.models import VerifiedEmailField
class User(models.Model):
email = VerifiedEmailField('e-mail')
It renders two input fields: e-mail and verification code. The verification code is sent to the e-mail address using AJAX or during field's clean if there is no valid code for given e-mail, so it works even without javascript.

Django: errors not being shown when form is submitted

clean is not showing errors on my login form regardless of what my input is and regardless of what method of displaying errors I try in clean.
In my CustomUserCreationForm error displaying works perfectly. The only difference between the two is login extends forms.Form while Custom extends UserCreationForm
Also I'm using django-crispy-forms to render my forms
class LoginForm(forms.Form):
username = forms.CharField(label=('UserName'),
widget = forms.TextInput(attrs={'placeholder': _('Username')})
)
password = forms.CharField(label=('Password'),
widget=forms.PasswordInput(attrs={'placeholder' : _('Password') }),
)
def helper(self):
helper = FormHelper()
helper.form_id = "Login"
helper.form_method = "POST"
helper.layout = Layout(Div(
Field('username', css_class='input-box-rounded'),
Field('password', css_class='input-box-rounded'),
Submit('Login', 'Login', css_class='col-md-6 col-md-offset-3 rounded'),
css_class='col-md-4 col-md-offset-4 centered-div'))
return helper
def clean(self):
cleaned_data = super(LoginForm, self).clean()
if 'username' not in cleaned_data:
msg = _("Please enter a username")
self._errors['username'] = self.error_class([msg])
if 'password' not in cleaned_data:
msg = _("Please enter a password")
raise forms.ValidationError(msg)
u =authenticate(username = cleaned_data['username'], password = cleaned_data['password'])
if u == None:
msg = _("Username or Password is incorrect")
self.add_error('username', msg)
return cleaned_data
Can you post your view and template code? Without seeing either of those, I assume either your template needs to display the errors or your view is not handling the form, though I haven't used Django Crispy Forms.
{{ form.non_field_errors }}
{{ form.username.errors }}
FYI, the preferred way to handle error checking is to create a clean function for each field and have it raise a ValidationError when there is a problem. This will then be a field error (second line above).
def clean_password(self):
data = self.cleaned_data.get('password')
if not data:
raise ValidationError(_("Please enter a password"))
Also, since you're just checking that a field is there, so you could set required=True for each required field and skip the manual validation.
class LoginForm(forms.Form):
username = forms.CharField(label=('UserName'), required=True,
widget = forms.TextInput(attrs={'placeholder': _('Username')})
)
password = forms.CharField(label=('Password'), required=True,
widget=forms.PasswordInput(attrs={'placeholder' : _('Password') }),
)
See the documentation for more info: https://docs.djangoproject.com/en/dev/topics/forms/#customizing-the-form-template

Raising validation error in django form for the api

I am new to django and python development and am naive in my understanding of how to handle exceptions.
I am registering a user through an api call by calling the method register, and would like to push the success status or the error messages while registration.
def register(self,request, **kwargs):
try:
data = self.deserialize(request, request.raw_post_data, format=request.META.get('CONTENT_TYPE', 'application/json'))
email = data['email']
password = data['password']
firstname = data['firstname']
lastname = data['lastname']
newdata = {'email' : email , 'password1': password , 'password2':password, 'firstname':'firstname' , 'lastname':lastname }
registrationform = UserEmailRegistrationForm(newdata)
print registrationform.errors.as_text
print registrationform.cleaned_data
cleaned_data = registrationform.cleaned_data
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
new_user = RegistrationProfile.objects.create_inactive_user(cleaned_data['username'],cleaned_data['email'],cleaned_data['password1'], site)
signals.user_registered.send(sender=self.__class__,
user=new_user,
request=request,**cleaned_data)
registerUser = collections.OrderedDict()
registerUser['return']='0'
registerUser['code']='0'
registerUser['message']='registered user'
return HttpResponse(registerUser, content_type="application/json")
except Exception, e:
logging.exception(e)
registerUser = collections.OrderedDict()
registerUser['return']='0'
registerUser['code']='0'
registerUser['message']='registered user'
return HttpResponse(registerUser, content_type="application/json")
When I execute this, for example with an already registered email, I get the following in registrationform.errors.as_text
bound method ErrorDict.as_text of {'email': [u'A user with that email already exists.']}>
What would be the right way to code exceptions so that I can pass the success message if the form was validated and user was registered, and the error message if there was a validation error?
Any help is much appreciated!
You might want to have a look in the form's is_valid() method: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.is_valid
For example
if registrationform.is_valid():
//do your stuff
....
register['error'] = False
else:
//return the errors
registerUser['message'] = _('Oops! Please fix the following errors')
register['error'] = True
register['errors'] = registrationform.errors
....

Problem with pymongo and django unique value

I am writing django app that as a beckend is using mongodb. I am curently writing register part. Here is how I connecto to database in settings.py
if socket.gethostname() == "Production server":
CON = Connection()
DB = CON.fish
else:
CON = Connection()
DB = CON.test
DB.user.ensure_index([("username", ASCENDING),("email",ASCENDING)],unique = True)#,drop_dups=True
Here is mye register view:
def register(request):
"""
handle user registration
code variable is for testing purposes
"""
if request.method== 'GET':
form = RegisterForm(auto_id=False)
code = 1
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
elif request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
password = form.cleaned_data['password']
password_confirmation = form.cleaned_data['password_confirmation']
if password == password_confirmation:
login = form.cleaned_data['login']
email = form.cleaned_data['email']
newsletter = form.cleaned_data['newsletter']
key = register_user(login,email,password,newsletter)
if key:
#send email
send_mail("Dziękujemy za rejestrację"," Klucz aktywacyjny to " + key,settings.EMAIL_HOST_USER,[email])
request.session['email'] = email
return redirect(register_success)
else:
code = 4
error = "Login/email taken"
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
else:
code = 3
error = "invalid password"
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
else:
code = 2
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
Here is my function I use to register user:
def register_user(login,email,password,newsletter):
"""
This function will return activation key for this user if user was added successfully or none otherwise
"""
key = generate_activation_key()
user = {
"username":login,
"email":email,
"password":crypt_password(password),
"date_join": datetime.now(),
"key": key
}
if newsletter:
user['newsletter'] = True
try:
settings.DB.user.insert(user,safe = True)
except DuplicateKeyError, error:
logging.debug("error raise during saving user")
return None
except OperationFailure, error:
logging.critical("Cannot save to database")
logging.critical(error)
else:
#we have no errors users is registred
return key
And when I test it in the browser it seems to be working. But I write test for it and it isn't working anymore. Here is code for test:
def test_valid_credentials(self):
#now try to register valid user
data = {'login':'test','password':'zaq12wsx','password_confirmation':'zaq12wsx','terms':True,'newsletter':True,'email':'test#test.com'}
response = self.c.post(reverse('register'),data)
#our user should be registred
self.assertEquals(302, response.status_code,'We dont have benn redirected')
self.assertEqual(len(mail.outbox), 1,'No activation email was sent')
#clen email box
mail.outbox = []
#now try to add another user with the same data
response = self.c.post(reverse('register'),data)
#template should be rendered with error message about used login and email
self.assertEquals(200, response.status_code)#this fails
And here is error that i get.
self.assertEquals(200, response.status_code)
AssertionError: 200 != 302
So user was registred with the same username and email which shoudn't happen. Any sugestions? Thanks in advance
Why don't you use https://github.com/django-mongodb-engine/mongodb-engine it works almost perfect with Django ORM. Works like a charm for me.