Flask WTform validation - unexpected keyword argument 'extra_validators' - validation

I have simple flask user validation form in wtform:
class LoginForm(FlaskForm):
email = StringField('Email', validators=[validators.DataRequired(), Email()])
password = PasswordField('Password', validators=[validators.DataRequired(), validators.Length(min=8, max=20)])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
In my app.py file:
#app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and bcrypt.check_password_hash(user.password, form.password.data):
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check email and password', 'danger')
return render_template('prihl.html', title='Login', form=form)
Everything what should be is imported.
I receive always Type error:
TypeError: LoginForm.validate() got an unexpected keyword argument 'extra_validators'
And problem is referenced to the file
"C:\Users*******.virtualenvs\flaskGPT-yypoX3qF\lib\site-packages\flask_wtf\form.py", line 86, in validate_on_submit
Is there someone who could help me with this ?
I have tried to authenticate user. Connection to the db is correct. I have migrated models succesfully. The problem is only with loginform, not with registerform.

Related

Error TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'

I am using Django Rest Auth with Django All Auth.
When I hit an endpoint /rest-auth/password/reset/ (POST method with an email address) I receive an email includes a URL to reset a password.
And then I cleck the URL, I have an error like below:
ImproperlyConfigured at /password-reset/confirm/MzY/5l0-c1042120be1e07140e64/
TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'
In general, after clicking that URL the screen should transit to a browser of /rest-auth/password/reset/confirm/.
But in my case, it doesn't work like that...
How can I solve this error?
Here are the codes:
project/urls.py
from django.contrib import admin
from django.urls import path, include, re_path
from django.views.generic import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')),
path('rest-auth/', include('rest_auth.urls')),
path('rest-auth/registration/', include('rest_auth.registration.urls')),
path('account-confirm-email/(?P<key>[-:\w]+)/$', TemplateView.as_view(),
name="account_confirm_email"),
path('account-confirm-email/', include('allauth.urls')),
re_path(
r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
TemplateView.as_view(),
name='password_reset_confirm'),
]
serializers.py
from rest_auth.serializers import PasswordResetSerializer
class CustomPasswordResetSerializer(PasswordResetSerializer):
def get_email_options(self):
print("check override")
return {
'domain_override': 'localhost:8000',
'email_template_name': 'account/email/password_reset_key_message.txt',
}
password_reset_key_message.txt
A password reset has been requested.
Click the link below to continue.
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
settings.py
REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER': 'path.to.serializers.CustomPasswordResetSerializer',
}
python: 3.7.5
Django:2.2.2
django-allauth:0.41.0
django-rest-auth:0.9.5
djangorestframework:3.12.1
I have no idea about those additional library. You can make your own custom password_reset serializers and views like below, make sure you provide JWT token based authentication while you reset the password from user.
serializers.py:
class ResetPasswordSerializer(Serializer):
new_password = serializers.CharField(error_messages={'required':' new_password key is required', 'blank':'new password is required'})
confirm_password = serializers.CharField(error_messages={'required':' confirm_password key is required', 'blank':'confirm password is required'})
views.py:
class ForgotPasswordResetAPIView(APIView):
permission_classes = (IsAuthenticated,)
authentication_classes=[JSONWebTokenAuthentication,]
def post(self,request,*args,**kwargs):
data = request.data
user = request.user
print(user)
serializer = ResetPasswordSerializer(data=data)
if serializer.is_valid():
new_password = serializer.data.get("new_password")
confirm_password = serializer.data.get("confirm_password")
if new_password == confirm_password:
print('ok')
user.set_password(new_password)
user.save()
return Response({'success':"true",'message':'Your password have been reset successfully'},status=200)
return Response({'success':"False",'message':'Both password fields must be same'},status=400)
return Response(serializer.errors , status=400)

Django - Login - Forbidden (CSRF token missing or incorrect.):

I am getting the Forbidden (CSRF token missing or incorrect.) error when I try to use login page.
The scenario is as follows:
An user has two tabs open.
Both tabs are login pages.
In tab 1, user successfully logged in, and was redirected to a new page where login is required.
In tab 2, user hasn't refreshed the page, and is still in the login page. In the Django backend, user is already authenticated, but the front-end template hasn't noticed it yet.
In tab 2, when I click on login button, I get Forbidden (CSRF token missing or incorrect.) error.
I made sure that csrf_token is in the form.
This error occurs only when I'm using two tabs.
I'm using AJAX
Why is this happening? How can I fix it?
I don't know this would help, but here is my views.py for login
class Login_View(LoginView):
template_name = 'login.html'
def post(self, request, *args, **kwargs):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
response_data = {}
if user is not None:
if user.is_active:
login(request, user)
response_data['result'] = 'success'
else:
return HttpResponse("Inactive user.")
else:
response_data['result'] = 'fail'
return HttpResponse(json.dumps(response_data), content_type="application/json")
The reason is addressed in the documentation here:
For security reasons, CSRF tokens are rotated each time a user logs in. Any page with a form generated before a login will have an old, invalid CSRF token and need to be reloaded. This might happen if a user uses the back button after a login or if they log in a different browser tab.
As for fixing it, there's neither a straightforward way nor a great reason to do so. If the user encounters an error in this unlikely scenario all they have to do is reload the page. So I wouldn't bother if I were you.
if a scenario occurs like this where user opens 2 tabs and tries to login, which is not a real life scenario, still if you want you can do this, keeping in mind the user like to play like this
def post(self, request, *args, **kwargs):
username = request.POST['username']
password = request.POST['password']
if request.user.is_authenticated():
return redirect('to_some_page')
else:
user = authenticate(username=username, password=password)
response_data = {}
if user is not None:
if user.is_active:
login(request, user)
response_data['result'] = 'success'
else:
return HttpResponse("Inactive user.")
else:
response_data['result'] = 'fail'

DjangoRestFramework - Where do I raise a 404 error when `get_queryset()` cannot get an object because it does not exist?

This is my view:
class PostListByUsername(generics.ListAPIView):
serializer_class = PostSerializer
permission_classes = (IsAuthenticated, IsLikeOrOwnerDeleteOrReadOnly, IsFromSameLocation,)
def get_queryset(self):
username = self.kwargs['username']
user = User.objects.get(username=username)
return Post.objects.filter(owner__username=username).order_by('-createdAt')
This is my IsFromSameLocation permission:
class IsFromSameLocation(permissions.BasePermission):
"""
Permission.
Allow permissions to authenticated users from the same
location as the user.
"""
message = 'Not from the same location.'
def has_permission(self, request, view):
username = view.kwargs.get('username', None)
try:
userProfile = User.objects.get(username=username)
except:
return False
return request.user.userextended.location == userProfile.userextended.location
With that said, in my get_queryset() method, I do user = User.objects.get(username=username) but if the user does not exist, I want to raise a 404 error. I know that get_queryset() is supposed to return a queryset, so I'm guessing I shouldn't raise a 404 in that method. So where exactly should I do the check to see if the user exists or not? Note that in my permission, I do do a try and except to see if the user exists, but permissions should only return True or False from my understanding (not raise 404 errors).
You actually can raise an exception from a permission and it will be correctly handled by Django. For example you can do it with get_object_or_404() shortcut function:
def has_permission(self, request, view):
username = view.kwargs.get('username', None)
userProfile = get_object_or_404(User, username=username)
return request.user.userextended.location == userProfile.userextended.location
In fact, while the code that throws an exception is executed in a view, it will be handled by Django, so it should not matter where you are raising it from -- from view, serializer, permission, model etc. methods.
The easiest way is to raise a Http404 exception:
In your try/except block, raise the 404 in the exception, but its probably worth limiting the exception to just DoesNotExist errors:
try:
userProfile = User.objects.get(username=username)
except User.DoesNotExist:
from django.http import Http404
raise Http404("User does not exist")

JQuery ajax call with django session

I have a site that uses custom authentication in Django that authenticates to a web service using a username, password, and domain. I need to store this information for every subsequent view's request object.
All seems to be going well until I try to make a jquery $.ajax call, debugging the view that gets called from the ajax request I have no session information and the user is AnonymousUser.
I need the session variables to make subsequent calls to the web service for data to display. I've tried a few things in there but nothing seems to work correctly. I want to make sure users a logged in before being able to submit these web service queries as I want to keep the username/domain to lookup the password (and not have it on the client side) in the view and ensure the user is always logged in.
views.py
def login(request):
if settings.DEBUG == True:
print "views.login:Attempting loging at views.login(request)"
if request.method == 'POST':
if settings.DEBUG:
print "views.login: method is POST"
form = LoginForm(request.POST)
if form.is_valid():
if settings.DEBUG:
print "Form is valid, attepmting to authenticate"
Login(request, form.get_user())
str = reverse('cm_base.views.index')
request.session['username']=form.get_user()
request.session['domain']=form.get_domain()
return render_to_response('cm_base/index.html',
{"DEBUG": True,
"user":request.session.get('username'),
'tabs': settings.TAB_LIST},
context_instance=RequestContext(request))
else:
# Their password / email combination must have been incorrect
pass
else:
form = LoginForm()
return render_to_response('cm_base/login.html',
{"DEBUG": True,
'form' : form
},
context_instance=RequestContext(request))
#login_required()
def index(request):
if request.method == 'POST':
print "POSTING"
if settings.DEBUG == True:
print "views.index:Opening index"
return render_to_response('cm_base/index.html',
{"DEBUG": True,
"user":"user",
'tabs': settings.TAB_LIST},
context_instance=RequestContext(request))
#login_required()
def scorecard(request):
user = CustomUser.objects.get(username=request.session.get('username'),
domain=request.session.get('domain'))
*fails on the above line with DoesNotExist: CustomUser matching query does not exist. Lookup parameters were {'username': None, 'domain': None}
base.js
$.ajax({
url : path,
data: $(this).serialize(),
headers: {'X-CSRFToken':getCookie('csrftoken')
,'sessionid':getCookie('sessionid')
},
success : function(data) {
console.log($(this));
//refresh right div
$('#contentpane').empty();
$('#contentpane').html(data.rhtml);
console.log(data.rhtml);
}
});
Looks like my implementation of the backend that was the problem.
backends.py
class CustomBackend(object)
...
def get_user(self, username):
try:
return CustomUser.objects.get(username=username)
except CustomUser.DoesNotExist:
return None
This is called during session passing and actually uses a the primary key which is a userid, not username so this was always returning no user and defaulting to anonymous user. I misinterpreted when the documentation says the userid can be anything, including username, I thought I could pass that in as such but I have both an auto generated userid as well as a username on the object.

django error: unexpected keyword argument 'widget'

I want to display a field (named 'icon') as radio button.
I created method callback in order to display DateTimeFields with JQuery. The code following should do it, however i get this error when i run my server:
Error when calling the metaclass bases
make_custom_datefield() got an unexpected keyword argument 'widget'
...
Exception Location: Virtualenvs/django/local/lib/python2.7/site-packages/django/forms/models.py in fields_for_model, line 164
forms.py:
def make_custom_datefield(f):
formfield = f.formfield()
if isinstance(f, DateTimeField):
formfield.widget.format = '%m/%d/%Y'
formfield.widget.attrs.update({'class':'datetimePicker', 'readonly':'true'})
return formfield
class FlashForm(forms.ModelForm):
formfield_callback = make_custom_datefield
class Meta:
model = Flash
exclude=('user','marker','address')
widgets = {'icon': forms.RadioSelect(), }
Can you please help me, i have really no clue how to solve this !
Thanks
Eventually, i found the answer: i had to add **kwargs parameter.
def make_custom_datefield(f,**kwargs):
formfield = f.formfield(**kwargs)
if isinstance(f, DateTimeField):
formfield.widget.format = '%m/%d/%Y'
formfield.widget.attrs.update({'class':'datetimePicker', 'readonly':'true'})
return formfield

Resources