I have a super simple flask form for a service that I'm trying to integrate with the Stripe API:
from flask_wtf import FlaskForm
import wtforms as wtf
class ServiceForm(FlaskForm):
name = wtf.StringField('The service name')
submit_ = wtf.SubmitField('Submit >')
I'm serving this form along with my stripe key as follows:
#app.route('/index', methods=['GET'])
def index() -> str:
form = ServiceForm()
render_template('index.html', form=form, key='some stripe key',
service='some service', service_price=2500)
and the index.html file looks like this:
<form id="service-form" action="{{ url_for('pay_for_service', service=service) }}" method="POST" novalidate>
{{ form.csrf_token }}
{{ form.name }}
{{ form.submit_(class_="some-button" id="service-form-submit-button") }}
<script
src="https://checkout.stripe.com/checkout.js"
class="stripe-button"
data-key="{{ key }}"
data-amount="{{ service_price }}"
data-locale="auto">
</script>
</form>
The action of this form points to the following route:
#app.route('/pay+for+service/<string:service>', methods=['POST'])
def pay_for_service(service: str) -> str:
form = ServiceForm()
if form.validate_on_submit():
# ... do some validation
# now launch the Stripe dialog box to input credit card details.
which I'll use to validate the form submission before the Stripe payment dialog box is launched.
Basically, I want the stripe-button in the script to be embedded in the form.submit_ button, which has a custom some-button class. Then I want the Stripe payment pop-up to show after I've validated the form. How do I do this please? I think it's pretty easy but been scratching my head for ages!
Thanks for any help, and stay safe :-)
It's most easily done with an ajax call. Set the button action to call "submit_form" which will be:
function submit_form(Elem) {
$.ajax({
type: 'POST',
url: "{{ url_for('pay_for_service', service=service) }}",
data: $("#service-form").serialize(),
success: function(data) {
if (data['status'] == 'success') {
// call stripe
} else {
alert(data['data']['message'])
};
}
});
}
You won't need two view functions, a single one will do:
#app.route('/index', methods=['GET'])
def index() -> str:
form = ServiceForm()
if form.validate_on_submit():
return json.dumps({'status': 'success', 'data': {'message': 'ok'}})
elif request.method == 'POST':
message = str(form.errors)
return json.dumps({'status': 'fail', 'data': {'message': message }})
render_template('index.html', form=form, key='some stripe key',
service='some service', service_price=2500)
This is not possible with the "simple" integration approach. It's possible to run your own validation and trigger the Legacy Checkout modal programmatically using the "custom" integration approach, as seen in this example.
var handler = StripeCheckout.configure({ ... })
// later ...
handler.open({...})
However, note that this is all using an old deprecated version of Checkout. You should strongly consider using the new Checkout. Among other significant advantages, you can to any validation you like server-side before creating the checkout session.
In my Django project I create custom admin page (NOT admin from Django). I have 2 login pages. One for admin, second for other users.
Here below you can see urls.py file for admin. I test it and it works fine. After successful login Django redirect user to url which was next parameter (/administration/dashboard/).
I wrote unit test and it raise error. From error I understand that Django redirect to default url (/accounts/profile/). Why unit test dont use settings which I did in urls.py file (next parameter)?
How to fix this problem?
Right now I notice that problem disappear only if I use this code LOGIN_REDIRECT_URL = '/administration/dashboard/' in settings.py. I cant use it cause in the future I will use LOGIN_REDIRECT_URL to my other login page.
I would be grateful for any help!
urls.py:
from django.contrib.auth import views as authentication_views
urlpatterns = [
# Administration Login
url(r'^login/$',
authentication_views.login,
{
'template_name': 'administration/login.html',
'authentication_form': AdministrationAuthenticationForm,
'extra_context': {
'next': reverse_lazy('administration:dashboard'),
},
'redirect_authenticated_user': True
},
name='administration_login'),
]
tests.py:
class AdministrationViewTestCase(TestCase):
def setUp(self):
self.client = Client()
self.credentials = {'username': 'user', 'password': 'password'}
self.user = User.objects.create_user(self.credentials, is_staff=True)
self.data = dict(
self.credentials,
next=reverse("administration:dashboard")
)
def test_administration_authorization(self):
self.assertTrue(self.user)
# logged_in = self.client.login(**self.credentials)
# self.assertTrue(logged_in)
response = self.client.post(
reverse("administration:administration_login"),
self.data,
follow=True
)
# self.assertEqual(response.status_code, 302)
self.assertRedirects(
response,
reverse("administration:dashboard"),
status_code=302,
target_status_code=200
)
ERROR:
Traceback (most recent call last):
File "/home/nurzhan/CA/administration/tests.py", line 51, in test_administration_authorization
reverse("administration:dashboard"),
File "/srv/envs/py27/lib/python2.7/site-packages/django/test/testcases.py", line 271, in assertRedirects
% (response.status_code, status_code)
AssertionError: Response didn't redirect as expected: Response code was 200 (expected 302)
forms.py:
from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import ugettext_lazy as _
class AdministrationAuthenticationForm(AuthenticationForm):
"""
A custom authentication form used in the administration application.
"""
error_messages = {
'invalid_login': (
_("ERROR MESSAGE.")
),
}
required_css_class = 'required'
def confirm_login_allowed(self, user):
if not user.is_active or not user.is_staff:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={
'username': self.username_field.verbose_name
}
)
login.html:
<form action="{% url 'administration:administration_login' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
<input type="hidden" name="next" value="{{ next }}"/>
</form>
Have you tried removing the follow=True in the POST request. You are checking for a redirection but you are telling the requests modules to follow the redirection, so your response will be directly the page and not the 302 redirection HTTP response.
To be more explicit. You are sending a request with requests.post(follow=True), that will follow the 302 redirection to the destination page and your response will be a HTTP 200 with the destination page. Even if the destination page is the one you want, the test assertion will fail because is looking for a HTTP 302 code in your response and you've already followed the redirection.
Django isn't checking next in extra_context but in GET and POST params.
extra_context is used to update your template context. So if you want to pass the values for variables to your template you can set those with extra_context.
However, you can fix your test by either setting LOGIN_REDIRECT_URL in settings.py or passing next as a POST or GET param.
params = dict(**self.credentials, next=reverse("home:index"))
response = self.client.post(
url,
params,
follow=True
)
NOTE:
self.credentials should be unpacked in the dict being applied to self.data.
self.data = dict(
**self.credentials,
next=reverse("administration:dashboard")
)
If you wanted to test without making this change, I can recommend another way to test this.
You need to render your template in a browser and fill out the login form. This way your browser client will make the POST request with next passed as a parameter.
You can get this done with a headless browser using selenium webdriver.
I'd like to create and fill out a Flask WTF-Form using only python code. However, the form doesn't automatically generate a CSRF token when I create it with python code. Is there any way to do this manually?
The form in question:
from flask_wtf import Form
from wtforms import StringField
from wtforms.validators import DataRequired, URL
class URLForm(Form):
url = StringField('url', validators=[DataRequired(), URL(), Level3Url()])
the code I use to generate the form:
from forms import URLForm
form = URLForm()
if 'url' in request.args:
url = request.args.get('url')
form.url.data = url
if form.validate():
...
You'd be effectively disabling CSRF protection by generating and passing a token to the form locally. It's only effective when the user submits a previously generated token.
Since you're not using CSRF protection, disable it. You can also pass request.args as the source of data.
form = URLForm(request.args, csrf_enabled=False)
If you want to use CSRF for this form, then the form needs to send the csrf_token field, which can be rendered with {{ form.csrf_token }} or {{ form.hidden_tag() }}.
In newest version of flask_wtf (0.14.2) you can disable csrf token in this way.
form = URLForm(request.args, meta={'csrf': False})
I'm trying to make a search function in my Django project using AJAX. But the functions in views.py don't seem to work properly. And maybe I have some other mistakes. Could you please tell me what I need to correct?
This is my AJAX file:
$(document).ready( function(){
$('#suggestion').keyup(function(){
var query;
query = $(this).val();
$.get('/friends_plans/suggest_users/', {suggestion: query}, function(data){
$('#user').html(data);
});
});
});
This is part of my template:
<div>
<ul class="nav nav-list">
<li class="nav-header">Find user</li>
<form>
<li><input class="search-query span10" type="text" name="suggestion" value=" " id="suggestion" /></li>
</form>
</ul>
</div>
<div id="user">
</div>
These ara functions from views.py:
def suggest_users(request):
users_list = []
starts_with = ''
if request.method == 'GET':
starts_with = request.GET['suggestion']
users_list = get_users_list(5, starts_with)
return render(request, 'friends_plans/list.html', {'users_list': users_list})
def get_users_list(max_results=0, starts_with=''):
users_list = []
if starts_with:
users_list = Person.objects.filter(username__istartswith=starts_with)
if max_results > 0:
if len(users_list) > 0:
users_list = users_list[:max_results]
return users_list
This is from urls.py:
url(r'^suggest_users/$', views.suggest_users, name='suggest_users')
The istartswith method doesn't work properly with the variable but does with the constant, I can't understand why. And suggest_users function doesn't return users_list to the object with id user ($('#user').html(data)), nothing appears on the page. But maybe there are some other mistakes.
Django's render function renders HTML after parsing it with Jinja. If you want to write a view that acts as an endpoint for an AJAX function, you do not want that view to return render.
Instead you should use return JsonResponse. JsonResponse accepts a dictionary as an argument. And it builds a proper JSON object for you. :) Which will then be picked up by your AJAX's success function.
Here's an example of how to use JsonResponse:
from django.http import JsonResponse
def some_endpoint(request, *args, **kwargs):
data = dict()
data["foo"] = "bar"
data["username"] = User.objects.get(id=request["id"]).username
return JsonResponse(data)
This will cause your view to return a JSON Object, which is what your AJAX function is looking for.
Second suggestion I would make would be to use jQuery's $.ajax() function rather than jQuery's shortcut .get() function. The advantage of this would be learning all the parameters that go along with AJAX calls.
Here's an example of jQuery's $.ajax() function.
$(document).ready( function(){
$('#suggestion').keyup(function(){
var query = $(this).val();
$.ajax(function(){
type: "GET",
url: "/friends_plans/suggest_users/",
data: {suggestion: query},
success: function(data){
console.log("SUCCESS");
console.log(data);
},
failure: function(data){
console.log("FAIL");
console.log(data);
},
});
});
});
I have some problem for a while now, I'm experiencing CSRF Cookie not set. Please look at the code below:
views.py:
def deposit(request, account_num):
if request.method == 'POST':
account = get_object_or_404(account_info, acct_number=account_num)
form_ = AccountForm(request.POST or None, instance=account)
form = BalanceForm(request.POST)
info = str(account_info.objects.filter(acct_number=account_num))
inf = info.split()
if form.is_valid():
# cd=form.cleaned_data
now = datetime.datetime.now()
cmodel = form.save()
cmodel.acct_number = account_num
# RepresentsInt(cmodel.acct_number)
cmodel.bal_change = "%0.2f" % float(cmodel.bal_change)
cmodel.total_balance = "%0.2f" % (float(inf[1]) + float(cmodel.bal_change))
account.balance = "%0.2f" % float(cmodel.total_balance)
cmodel.total_balance = "%0.2f" % float(cmodel.total_balance)
# cmodel.bal_change=cmodel.bal_change
cmodel.issued = now.strftime("%m/%d/%y %I:%M:%S %p")
account.recent_change = cmodel.issued
cmodel.save()
account.save()
return HttpResponseRedirect("/history/" + account_num + "/")
else:
return render_to_response('history.html',
{'account_form': form},
context_instance=RequestContext(request))
Template file:
<form action="/deposit/{{ account_num }}/" method="post">
<table>
<tr>
{{ account_form.bal_change }}
<input type="submit" value="Deposit"/>
</tr>
{% csrf_token %}
</table>
</form>
I'n stuck, I already cleared the cookie, used other browser but still csrf cookie not set.
This can also occur if CSRF_COOKIE_SECURE = True is set and you are accessing the site non-securely or if CSRF_COOKIE_HTTPONLY = True is set as stated here and here
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def your_view(request):
if request.method == "POST":
# do something
return HttpResponse("Your response")
If you're using the HTML5 Fetch API to make POST requests as a logged in user and getting Forbidden (CSRF cookie not set.), it could be because by default fetch does not include session cookies, resulting in Django thinking you're a different user than the one who loaded the page.
You can include the session token by passing the option credentials: 'include' to fetch:
var csrftoken = getCookie('csrftoken');
var headers = new Headers();
headers.append('X-CSRFToken', csrftoken);
fetch('/api/upload', {
method: 'POST',
body: payload,
headers: headers,
credentials: 'include'
})
From This
You can solve it by adding the ensure_csrf_cookie decorator to your view
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def yourView(request):
#...
if this method doesn't work. you will try to comment csrf in middleware. and test again.
If you're using DRF, check if your urlpatterns are correct, maybe you forgot .as_view():
So that how mine code looked like:
urlpatterns += path('resource', ResourceView)
And that's how it should like:
urlpatterns += path('resource', ResourceView.as_view())
I came across a similar situation while working with DRF, the solution was appending .as_view() method to the view in urls.py.
try to check if your have installed in the settings.py
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',)
In the template the data are formatted with the csrf_token:
<form>{% csrf_token %}
</form>
This problem arose again recently due to a bug in Python itself.
http://bugs.python.org/issue22931
https://code.djangoproject.com/ticket/24280
Among the versions affected were 2.7.8 and 2.7.9.
The cookie was not read correctly if one of the values contained a [ character.
Updating Python (2.7.10) fixes the problem.
This also occurs when you don't set the form action.
For me, it was showing this error when the code was:
<form class="navbar-form form-inline my-2 my-lg-0" role="search" method="post">
When I corrected my code into this:
<form class="navbar-form form-inline my-2 my-lg-0" action="{% url 'someurl' %}" role="search" method="post">
my error disappeared.
In my case, the problem was that the path to the static files in nginx was incorrectly specified.
sudo tail -F /var/log/nginx/error.log
Check if there are errors in file paths.
I get this error and change this:
<form method="post">
to this:
<form method="POST">
and it's solved !
Just upper case post make the problem !
I have not any issue with this on 127.0.0.1, but when i use 192.168.x.x address this broke my forms.
Problem seems that you are not handling GET requests appropriately or directly posting the data without first getting the form.
When you first access the page, client will send GET request, in that case you should send html with appropriate form.
Later, user fills up the form and sends POST request with form data.
Your view should be:
def deposit(request,account_num):
if request.method == 'POST':
form_=AccountForm(request.POST or None, instance=account)
if form.is_valid():
#handle form data
return HttpResponseRedirect("/history/" + account_num + "/")
else:
#handle when form not valid
else:
#handle when request is GET (or not POST)
form_=AccountForm(instance=account)
return render_to_response('history.html',
{'account_form': form},
context_instance=RequestContext(request))
Check that chrome's cookies are set with default option for websites. Allow local data to be set (recommended).
I was using Django 1.10 before.So I was facing this problem.
Now I downgraded it to Django 1.9 and it is working fine.
I had the same error, in my case adding method_decorator helps:
from django.views.decorators.csrf import csrf_protect
from django.utils.decorators import method_decorator
method_decorator(csrf_protect)
def post(self, request):
...
Make sure your django session backend is configured properly in settings.py. Then try this,
class CustomMiddleware(object):
def process_request(self,request:HttpRequest):
get_token(request)
Add this middleware in settings.py under MIDDLEWARE_CLASSES or MIDDLEWARE depending on the django version
get_token - Returns the CSRF token required for a POST form. The token is an alphanumeric value. A new token is created if one is not already set.
I have just met once, the solution is to empty the cookies.
And may be changed while debugging SECRET_KEY related.
If you are not using {% csrf_token %} tag in the template you are rendering. Django won't set the csrftoken cookie.
To force django to set the csrftoken cookie, add ensure_csrf_cookie decorator in you view.
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def myview(request):
In my particular case, the problem is that I was using the Django rest_framework and forgot to add the following decorators to my function:
from rest_framework.decorators import api_view, renderer_classes
#api_view(('POST',))
#renderer_classes((JSONRenderer,))
def handle_web_request(request):
...
Just want to point out my case here as someone might cross the same fields.
Forbidden (CSRF cookie not set.): /main/staff/products/validation/create
HTTP POST /main/staff/products/validation/create 403 [0.01, 127.0.0.1:55940]
This thing was driving me insane... So, by commenting CSRF middleware
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
)
it gave me
POST Method not allowed.
That was my hint, after all.
I was sure Post method was present.
Turns out my url_patterns was leading to another view by a regex bug.
So no matter what I was doing in my view, #csrf_exempt #ensure_crsf_cookie, looking for .as_view()... I was looking at the wrong view.
So, if nothing works, make sure your are actually being sent to the right view.
You can get this error while deploing Django application with NO SSL.
If this is the case then putting an SSL reverse-proxy or SSL-configured Ingress in front of backend will solve the problem.
In my case, setting CSRF_COOKIE_SECURE to False wasn't enough but setting it to Null/ not specifying the parameter worked.
Method 1:
from django.shortcuts import render_to_response
return render_to_response(
'history.html',
RequestContext(request, {
'account_form': form,
})
Method 2:
from django.shortcuts import render
return render(request, 'history.html', {
'account_form': form,
})
Because render_to_response method may case some problem of response cookies.
Clearing my browser's cache fixed this issue for me. I had been switching between local development environments to do the django-blog-zinnia tutorial after working on another project when it happened. At first, I thought changing the order of INSTALLED_APPS to match the tutorial had caused it, but I set these back and was unable to correct it until clearing the cache.
In your view are you using the csrf decorator??
from django.views.decorators.csrf import csrf_protect
#csrf_protect
def view(request, params):
....