I'm working on a django web app, and there's an html form which I need to do 2 things when the form is submitted: create a record in the app's database and post some of the values collected to another website (e.g. a payment site).
The problem I'm having is getting the form to do the 2 things simultaneously. I know an HTML form can only have one action, and I've read some posts here on StackOverflow about using javascript to get the form to execute 2 or more actions, but everything I've tried so far hasn't worked for this situation. They all seem to get only one action to work.
This is what my django template looks like right now:
{% extends "some other template" %}
{% block content %}
<div>
...
<form id=form1" name="trans_form" method="POST" >
...
<!--DATA TO POST TO PAYMENT SITE-->
<input type="hidden" name="transaction_id" value="some value" />
<input type="hidden" name="transaction_amount" value="some value"/>
<input type="hidden" name="customer_id" value="some value" />
<input type="hidden" name="customer_name" value="some value" />
<!--DATA TO POST TO PAYMENT SITE-->
...
<!--DATA TO POST TO APP DATABASE-->
<input type="hidden" name="user" value="{{ user.id }}">
<input type="hidden" name="type" value="CC">
<input type="hidden" name="ref_no" value="{{ ref_no }}">
Amount: <input type="text" name="amount" id="id_amount" required />
Ref ##: <span>{{ ref_no }}</span>
Date: <span>{{ cur_date|date:'d/m/Y' }}</span>
Submit
<!--DATA TO POST TO APP DATABASE-->
...
</form>
...
</div>
{% endblock %}
{% block script %}
<script>
function submitForm()
{
createRecord(document.forms["trans_form"]);
sendToPay(document.forms["trans_form"]);
}
function sendToPay(f)
{
f.action= "www.paymentsite.com";
f.target = null;
f.onsubmit = null;
f.submit();
}
function createRecord(f)
{
f.action = "url to view that creates the record in database";
f.target = "_blank";
f.onsubmit = null;
f.submit();
}
</script>
{% endblock %}
What do you think? Am I trying to achieve the impossible? If not, point me in the right direction. Thanks.
Why not simply POST to the payment site from your controller:
def handle_payment(request):
post_to_payment_site(request)
write_payment_info_to_db(request)
def post_to_payment_site(request):
data = {'transaction_id': request.form['transaction_id',
# etc.
}
requests.post('payment-provider-url', data=data)
If you cannot accept POST data intended for your payment provider then you can do one of the following things:
Send your payment provider an XHR request - this requires that your payment provider properly implement CORS for the endpoint you are posting to. When that request completes, you can submit the form normally.
Change the target attribute of your form to point at an iframe or a new tab / window. Then, when the iframe loads, remove the target attribute, switch the action back to your endpoint and submit.
I finally solved my problem. I'm not sure it is the most efficient solution, but here it is.
I made one more HTML page to act as a middleman between my app and the payment site. It's a very simple page, practically a replica of the first form page but with only the required fields to be posted to the payment site. This way, there's no need for much JavaScript. Submitting the form creates a record in the app database, sends the needed data to the "middleman", which then posts the data to the payment site. The user never actually sees the middleman throughout the process.
Like I said, this might not be the most efficient solution, but it works fine.
First, here's the code for the views.py:
def write_payment_info_to_db(request):
dt = datetime.now()
form = Form()
err = False
if request.method == 'POST':
#Collect data to send to GTPay
transaction_id = request.POST['transaction_id']
transaction_amount = request.POST['transaction_amount']
customer_id = request.POST['customer_id']
customer_name = request.POST['customer_name']
#Create record in db for "valid" form data
form = Form(request.POST)
if form.is_valid():
form.save()
return render_to_response('middleman.html', {'transaction_id': transaction_id,
'transaction_amount': transaction_amount,
'customer_id': customer_id,
'customer_name': customer_name},
context_instance=RequestContext(request))
else:
err = form.errors
return ...
else:
return ...
And here's the middleman:
<html>
<body onload="document.submit2paymentsite_form.submit()">
<form name="submit2paymentsite_form" action="payment-provider-url" target="_self" method="POST">
{% csrf_token %}
<input type="hidden" name="transaction_id" value="{{ transaction_id }}" />
<input type="hidden" name="transaction_amount" value="{{ transaction_amount }}" />
<input type="hidden" name="customer_id" value="{{ customer_id }}" />
<input type="hidden" name="customer_name" value="{{ customer_name }}" />
</form>
</body>
Related
I am doing a project in Django. In my views I send data to the HTML pages with dictionaries. I can easily access that data on HTML, but when I try to access it on Javascript I can't because Javascripts thinks it's a variable name.
For example, I have a log in page. If the credentials are wrong, I send them to the same page and tell them that the username or password are wrong. I want to keep the previously written username on the form, so the person only needs to rewrite the password. I send the username from the view to the Javascript, but I can't access it.
My view code:
def login_form(request):
assert isinstance(request, HttpRequest)
if 'username' and 'password' in request.POST:
user = request.POST['username']
passw = request.POST['password']
if rightcredentials(user,passw):
tparams = {
'message': 'login successful',
}
return render(request, 'about.html', tparams)
else:
tparams = {
'login' : 'failure1',
'username_info' : user
}
return render(request, 'login_form.html', tparams)
Javascript code:
{% if login == 'failure1' %}
<form action="." method="post">
{% csrf_token %}
<div class="program-info">
<h3 class="program-title">Sign Into Your Account</h3>
<p>Wrong Username or password</p>
<div class="form-group">
<input type="text" value="" class="form-control" name="username" id="username" placeholder="Username">
</div>
<div class="form-group">
<input type="password" class="form-control" name="password" placeholder="Password">
</div>
<button type="submit" class="btn btn-primary">Log In</button>
</div>
</form>
<script type="text/javascript">
{% autoescape off %}
var paramvalue = {{ username_info }}
{% endautoescape %}
document.getElementById("username").value = paramvalue;
</script>
{%endif%}
After inserting username: 'pedro' and the wrong password on the form I get the following error:
Uncaught ReferenceError: pedro is not defined
Is there a way to access the string username_info or a way to convert the variable name to a string?
Wrapping the {{ }} in quotation marks should do the trick
{% autoescape off %}
var paramvalue = "{{ username_info }}"
{% endautoescape %}
In my Django/Python application I have fields which are necessary or not, depending on what is selected in previous fields. Thats why those are not mandatory by default. I would like to run a script which would raise an error on the chosen field, depending on the selection. I would like to do that when 'Submit' button is pressed, via script.
My html code:
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST" id="PostForm" data-sektor-url="{% url 'ajax_load_sektors' %}" novalidate enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Report</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" id="submit" type="submit">Submit</button>
</div>
</form>
</div>
<script>
$("#submit").click(function () {
if (document.getElementById('id_res_person_1').value == '') {
HERE I WOULD RAISE AN ERROR ON THAT FIELD
}
});
</script>
You need to call a function when form is submitted, make your validation and if all good submit to the view
Here is an example:
JS
function validate(url) {
// do your validation
if(allIsOk){
// get the form
my_form.submit();
}
}
On Submit or link
<fom onsubmit="validate()">
When one or more of the checkboxes in hostform are checked, I want to trigger a form submit. This is triggered successfully with the code below. However, the page reloads on the form submit, making any box that is checked go immediately back to being unchecked. I thought the localStorage.input line would fix this, but apparently not.
Any suggestions?
HTML:
<div class="container-lg">
<!-- section for checkboxes -->
<div class="row justify-content-center">
<div class="col-6">
<h2>Host</h2>
<form id="hostform" class="form-inline" role="form" action="" method="post">
{% csrf_token %}
<input type="checkbox" class="form-control" id="one" name="hm" value="one" onchange="triggerPost('one')">
<label for="one">One</label>
<br>
<input type="checkbox" class="form-control" id="two" name="hm" value="two" onchange="triggerPost('two')">
<label for="two">Two</label>
<br>
</form>
</div>
....
</div>
jQuery:
<script>
function triggerPost(idnum) {
$('#hostform').on("submit", function () {
localStorage.input = $("#"+idnum).checked;
});
$('#hostform').submit()
};
</script>
There are two ways to solve this kind of issues, by keeping track of posted data in the Django view, or using ajax to send data to view, preventing html page and thus form to reload each time the post is sent.
1. Keeping track of posted data:
In your view, you should get checkbox states in the posted data, by getting list of checked values of your checkboxes and then return them as context data. It depends how your response is organized, but for simplicity let us assume you use TemplateResponse:
return TemplateResponse(request, 'your.html',{'checked_hms':request.POST.getlist('hm')})
Than in your html you should use condition to check if value exists in checked_hms and based on add checked value to your checkbox inputs, like this:
...
<input type="checkbox" class="form-control" id="one" name="hm" value="one" {% if 'one' in checked_hms %}checked{% endif %} onchange="triggerPost()">
<label for="one">One</label>
<br>
<input type="checkbox" class="form-control" id="two" name="hm" value="two" {% if 'two' in checked_hms %}checked{% endif %} onchange="triggerPost()">
<label for="two">Two</label>
...
2. Using ajax:
You may send form data as post, using ajax call, which will not reload form, thus keeping checkboxes states intact, something like this:
<script>
function triggerPost() {
$.ajax({
type: "POST",
url: $('#hostform').attr('action'),
data: $('#hostform').serialize()
});
};
</script>
Am working on a project which has a form and inside the form there is an anchor link tag with a dynamic id.Am using the anchor link tag to submit the form via Javascript. I want to fetch the id of the same anchor link tag (which is dynamic) and submit to the backend but cant figure out how to fetch it..
The form
<form method="POST" action="#" id="rsmForm">
<!-- Token field-->
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}">
+ ASM ({{ $a['id'] }})
</form>
In your onclick callback, get the href attribute value of the anchor:
function onClickAnchor(e) {
const currentHref = e.currentTarget.getAttribute('href');
console.log(currentHref);
// document.getElementById('rsmForm').submit(); <=== uncomment it
}
<form method="POST" action="#" id="rsmForm">
<!-- Token field-->
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}">
+ ASM ({{ $a['id'] }})
</form>
I'm integrating Adyen 3DSecure payments. I make a request to Adyen with the card details to get the URL of the bank, and then redirect to the bank with a HTTP POST request using a form. The form should be self-submitting. See the Adyen documentation here: https://docs.adyen.com/developers/risk-management/3d-secure#redirecttothecardissuer
At the top of my Angular 4 component I'm using ElementRef to get access to the form submit button:
#ViewChild('submitButton') submitButton: ElementRef;
I then make a request from my Angular 4 component:
Observable.forkJoin([
this.paymentForm.validate(),
this.orderEmailForm.validate(),
])
.catch((e) => {
isValidationError = true;
throw e;
})
.switchMap((result) => {
...
...
this.apiService.startBuyGift(userId, aPayload)
.do((paymentAuthorise: PaymentAuthorise) => {
this.paymentAuthorise = paymentAuthorise;
setTimeout(() => { // need timeout, because HTML is not there yet
console.log('submitButton is ', this.submitButton);
this.submitButton.nativeElement.click();
}, 1000);
})
.finally(() => {
})
paymentAuthorise contains the details to be used in the form to do a POST to the bank e.g. the issuerUrl to be redirected to. My template is:
<div *ngIf="paymentAuthorise">
<form method="POST" action="{{ paymentAuthorise.threeDSecure.issuerUrl }} " >
<input type="hidden" name="PaReq" value="{{ paymentAuthorise.threeDSecure.paRequest }}" />
<input type="hidden" name="MD" value="{{ paymentAuthorise.threeDSecure.md }}" />
<input type="hidden" name="TermUrl" value="{{ paymentAuthorise.threeDSecure.issuerUrl }}" />
<input type="submit" class="button" value="continue" #submitButton />
</form>
</div>
This line this.submitButton.nativeElement.click(); should do the submit and redirect to the issuerUrl. However nothing happens. Even when I actually click the submit button, again nothing happens (the form is populated correctly with the correct action etc).
It's as if a traditional form with an action and a submit button does not work in modern day Angular. Any ideas?
EDIT
After rendering, the HTML looks like this:
<form _ngcontent-c2="" method="POST" novalidate="" action="https://test.adyen.com/hpp/3d/validate.shtml " class="ng-untouched ng-pristine ng-valid">
<input _ngcontent-c2="" name="PaReq" type="hidden" value="eNpVUttygjAQ/RXrB5AQrjJrZiLMWB9QbOlzh4lbZUZAA1Tt1zfBW5unPXv2ejaQ7xRi8o6yV8ghxbYttjgqN9NxFIaOxwLb9VwaUBpOxhwy8YZHDt+o2rKpuW1RiwG5Q52u5K6oOw6FPM4WS+7ZzHGB3BBUqBYJz7HtsqoS8tiXChWQqxvqokL+IT5F/BLFe9RV5+VXF69SIAMFsunrTl24E+imdwC92vNd1x3aiJDT6WTJIXWrUy3ZVEAMD+Q5WtYbq9X1zuWGr5LtzzJfOGm+tpeJcNJEuKu1MG8KxETApuiQM2qH1GPuiAYRdSIWABn8UFRmED6fZSPftia+3vbqgYNpJK7Atw3z1wNacYW1vPCQaeqBAM+HpkYdoZd82ECeY8evRl/Zac08ZgT2/CCc0LvUA2GqlFodPXQ4lDEAiEkltyOS27G19e8T/AIBp600">
<input _ngcontent-c2="" name="MD" type="hidden" value="djIhOGZGVHMzNXVGMmNBYit4Vk1QWTVOQT09IXSU4cnFE9pTy1vmgpKOm7wF7CWsmu+z6CnBoBKAFMyo9Phpfuv9NljsAKOcpfrK98lwuFF0ZtOyg6pO366T0Hkb2hObYrn58Moq1hRoLtpZL+yBQE6I2ckKR9xErkyqqofXDJdhovfAe7lzDKzbu38jv7jzYKjh6pZGhSXUxMVr+iHJsLskllfIrghEOdkWYNe0FzmNsA43Cmceq0lQrCmlMBz9HnYP8WG5IETkEFk81qisvqqw7q7mIcSqRLcR1TBSn1ZKyaAajazFe0Hx7Y9yc67MeoSw6zNhq8UHqPOvDKytHmQlJaflk4FyhnkqH0OAtGJx">
<input _ngcontent-c2="" name="TermUrl" type="hidden" value="https://test.adyen.com/hpp/3d/validate.shtml">
<input _ngcontent-c2="" class="button" type="submit" value="continue" ng-reflect-class-base="button">
</form>
When I click this button, nothing happens. When I copy this to it's own separate HTML page, when I click, I get redirected to https://test.adyen.com/hpp/3d/validate.shtml as expected.....
Programatically submitting the form rather than clicking the button worked:
this.issuerForm.nativeElement.submit();