Create a dictionary with a HTML-form in Django - html

Is there any possible method to create a form with one or more elements like:
<form action="{% url "foo" %}" method="POST">
<div id="report_item">
<input type="text" name="dontknow">
<input type="date" name="dontknow">
</div>
<div id="report_item">
<input type="text" name="dontknow">
<input type="date" name="dontknow">
</div>
</form>
And to get a dictionary from this form as below:
{data:
{1: {
text: 'bla', date: '2014-03-02'
},
{2: {
text: 'second text', date: '2014-03-01'
}
}

Not quite, but you probably want to look at formsets. That gives you a series of repeated forms, which you can output on the template by just iterating through. Once you've validated the POST data if you really need a dictionary you can do something like:
data = {i: form.cleaned_data for i, form in enumerate(formset.forms)}

Related

Keyerror when doing a json post request on sqlalchemy

I am working on a project for class where I have to edit starter Code. I keep getting a KeyError code and I am not sure what the issue is.
line 250, in create_venue_submission
name = request.get_json()['name']
KeyError: 'name'
Code Below from new_venue.html
I added id="name", city, etc.. for all the divs . I'm not sure if that is the correct way to do it but thats the only way i figured to pull all the data from the form.
{% extends 'layouts/main.html' %}
{% block title %}New Venue{% endblock %}
{% block content %}
<div class="form-wrapper">
<form id="venueInfo" method="post" class="form">
<h3 class="form-heading">List a new venue <i class="fa fa-home pull-right"></i></h3>
<div id='name' class="form-group">
<label for="name">Name</label>
{{ form.name(class_ = 'form-control', autofocus = true) }}
</div>
<div class="form-group">
<label>City & State</label>
<div class="form-inline">
<div id='city' class="form-group">
{{ form.city(class_ = 'form-control', placeholder='City', autofocus = true) }}
</div>
<div id='state' class="form-group">
{{ form.state(class_ = 'form-control', placeholder='State', autofocus = true) }}
</div>
</div>
</div>
<div id='address' class="form-group">
<label for="address">Address</label>
{{ form.address(class_ = 'form-control', autofocus = true) }}
</div>
<div id='phone_num' class="form-group">
<label for="phone">Phone</label>
{{ form.phone(class_ = 'form-control', placeholder='xxx-xxx-xxxx', autofocus = true) }}
</div>
<div id="genres" class="form-group">
<label for="genres">Genres</label>
<small>Ctrl+Click to select multiple</small>
{{ form.genres(class_ = 'form-control', autofocus = true) }}
</div>
<div id="fb_link" class="form-group">
<label for="genres">Facebook Link</label>
{{ form.facebook_link(class_ = 'form-control', placeholder='http://', autofocus = true) }}
</div>
<input type="submit" value="Create Venue" class="btn btn-primary btn-lg btn-block">
</form>
<script type="text/javascript">
document.getElementById("venueInfo").onsubmit=function(e){
e.preventDefault();
fetch('/venues/create',{
method:'POST',
body:JSON.stringify({
'name': document.getElementById('name').value,
'city': document.getElementById('city').value,
'state': document.getElementById('state').value,
'address': document.getElementById('address').value,
'phone_num': document.getElementById('phone_num').value,
'genres': document.getElementById('genres').value,
'fb_link': document.getElementById('fb_link').value,
}),
headers: {'Content-type': 'application/json'}
})
.then(function(){
})
}
</script>
</div>
{% endblock %}
Code below is from app.py
#app.route('/venues/create', methods=['GET'])
def create_venue_form():
form = VenueForm()
return render_template('forms/new_venue.html', form=form)
#app.route('/venues/create', methods=['POST'])
def create_venue_submission():
name = request.get_json()['name']
print(name)
flash('Venue ' + request.form['name'] + ' was successfully listed!')
return render_template('pages/home.html')
Looks like you're using Flask-WTF to make those form fields, then using a JS function to grab the values from these with getElementById.
The problem is you don't set an id on the form fields. To get a better visualization of this, have a look at the rendered HTML, rather than the template code itself.
So instead of:
{{ form.name(class_ = 'form-control', autofocus = true) }}
You're looking for someting like:
{{ form.name(id='name', class_ = 'form-control', autofocus = true) }}
Then verify that renders to something like:
<input autofocus class="form-control" id="name" name="name" type="text" value="">
Note this now has an id attribute, which should allow your JS function to grab the value.
You'll need to appy this same concept to the other form fields.
I added id="name", city, etc.. for all the divs . I'm not sure if that is the correct way to do it but thats the only way i figured to pull all the data from the form.
Doing this for the divs is no use... document.getElementById('name').value takes the value of an input field, so that's what you'd have to add the id attribute to, as above.
EDIT regarding comment
In your create_venue_submission route, the request.get_json method receives the values submitted by the javascript Fetch request. On the other hand, request.form contains the values if the form is submitted without Javascript.
With the Javascript method: e.preventDefault() prevents that traditional form submission when the button is clicked, and instead submits the Fetch request, with the header {'Content-type': 'application/json'}.
Either by removing that script tag, or running in a browser with javascript disabled it will fall back to the traditional submission method.
So you should probably do some logic in your flask route to test this condition. You can also use request.is_json boolean for this. Remember to do from flask import jsonify. Something like:
if request.is_json:
# Request came in via javascript based on the header provided.
name = request.get_json()['name']
# Add data to database or something
return jsonify({'message':f'{name} was sucessfully listed.'})
else:
# handle traditional form submission
name = request.form['name']
# Add data to database or something
flash('Venue ' + request.form['name'] + ' was successfully listed!')
return render_template('pages/home.html')
Of course you'll then need to handle the returned JSON in your frontend. So insead of:
.then(function(){
})
Something like:
.then(response => response.json())
.then(data => {
console.log(data);
});
Now with the browser's dev tools Console open you should see something like this when submitted:
Object { message: "Burning Man Festival was sucessfully listed." }
Of course you could then swap that console.log line out with anything to manipulate the dom, and access for example data['message'] to get the success string itself.

How should I parse an array of objects in Node.js?

According to my research, that the best way to pass an Array of Objects through a POST method is by using the following name convention:
<!-- first student -->
<input type="text" name="students[][first]">
<input type="text" name="students[][last]">
<input type="text" name="students[][age]">
<!-- second student -->
<input type="text" name="students[][first]">
<input type="text" name="students[][last]">
<input type="text" name="students[][age]">
Body-parser setup:
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({extended: true}));
When I typed in the post route "req.body.students", I was expecting an array of objects like this:
[ { first: Fname1, last: Lname1, age: Age1} , { first: Fname2, last: Lname2, age: Age2}
Instead, my console.log shows that I got this:
[ { first : [Fname1 , Fname2], last : [Lname1, Lname2], age : [Age1, Age2] } ]
What is going wrong with it? Cheers!
This is simply how the qa library handles data in the format.
It isn't how I would expect the library to behave, and it is different to how PHP (the obvious inspiration) handles data. So you might consider this a bug and join in the discussion on this issue.
You can work around the issue by using explicit indexes for your rows:
<!-- first student -->
<input type="text" name="students[0][first]">
<input type="text" name="students[0][last]">
<input type="text" name="students[0][age]">
<!-- second student -->
<input type="text" name="students[1][first]">
<input type="text" name="students[1][last]">
<input type="text" name="students[1][age]">
A test case:
var qs = require("qs");
var data = "students[0][name]=alice&students[0][age]=11&students[1][name]=bob&students[1][age]=12";
console.log(qs.parse(data));
Outputs:
{ students: [ { name: 'alice', age: '11' }, { name: 'bob', age: '12' } ] }

How do I merge 2 JSON arrays for $http data?

I have 2 models, both JSON data. One is generated by user input in a form, the other is data provided by an API and is modified by user input in the same form.
here is the form in the view:
<form method="post" name="form" role="form" ng-controller="ContactFormController as ctrl" ng-submit="form.$valid && ctrl.sendMessage(input, ctrl.cartItems)" novalidate>
<p ng-show="success">Thanks for getting in touch!</p>
<p ng-show="error">Something went awry with your submission!, please try again.</p>
<div class="formgroup">
<legend>Express Order Form</legend>
<div id="formHeader"></div>
<fieldset>
<div class="inputItem">
<label for="name">Name:</label>
<input class="form-control" type="text" id="name" name="name" ng-model="input.name" required>
</div>
<div class="inputItem">
<label for="email">Email:</label>
<input class="form-control" type="email" id="email" name="email" ng-model="input.email" required>
</div>
<div class="inputItem">
<label for="School">School:</label>
<input class="form-control" type="text" id="school" name="school" ng-model="input.school" required>
</div>
<!-- Form Items -->
<div ng-repeat="item in ctrl.cartItems">
<div class="col-sm-6 cartItemLabel" ng-bind="item.label"></div>
<div class="col-sm-2 inputItem">
<input class="form-control" type="text" id="item.id" name="item.id" ng-change="ctrl.updateSub(item)" ng-model="item.qty">
</div>
<div class="col-sm-2 inputItem">
<input class="form-control" type="text" id="item.id" name="item.id" ng-model="item.value">
</div>
<div class="col-sm-2 inputItem">
<input class="form-control" type="text" id="item.id" name="item.id" ng-model="item.subTotal">
</div>
</div>
<!-- /Form Items -->
<div class="inputItem">
<label for="messsage">Message:</label>
<textarea class="form-control" rows="4" id="messsage" name="message" ng-model="input.message" required></textarea>
</div>
<div class="hidden">
<label for="honeypot">I promise I'm not a bot</label>
<input type="text" id="honeypot" name="honeypot" ng-model="input.honeyPot">
</div>
Here is the model that is NOT generate by the form above:
self.cartItems = [
{'id': 1, 'label': "Band-Aids (box)", 'value': 1.5, 'qty': 0, 'subTotal': 0},
{'id': 2, 'label': "Binders – 1/2”", 'value': 6.5, 'qty': 0, 'subTotal': 0},
{'id': 3, 'label': "Binders – 1”", 'value': 6.5, 'qty': 0, 'subTotal': 0},
{'id': 4, 'label': "Binders – 1 1/2”", 'value': 7.5, 'qty': 0, 'subTotal': 0},
{'id': 5, 'label': "Binders – 2”", 'value': 8.5, 'qty': 0, 'subTotal': 0}
]
I need to upload all data from both JSON arrays to an API. I tried to concatenate the 2 JSON arrays like this (the function that is called on submit in the form):
self.sendMessage = function( input, items ) {
var output = input.concat(items);
....[on to my $http call]
but I get a type error.
Forgive me if I left out info you need; I am a newbie to Angular.
You might like to take a look at angular.extend, I think it should solve your problem.
angular.extend doc
A type error usually means that a type is not what you expected. I would guess that input is not an array. Try changing your code to this:
var output = items.concat(input);
Edit: To further track down the issue, you can add a debugger; statement in your code to break in the code at runtime. Then you can verify the types are what you expect by opening the dev tools console in your browser while running that piece of code.
To all who helped me on this issue, a big thank you!
#ragingprodigy put me on the right track.
I had to change the input within the method in my controller to:
var output = [input].concat(items);
I also had to change the form elements' binding to include the controller (as ctrl)
ng-model="ctrl.input.[each form element on input]"
Thanks again to all who helped! I hope this answer helps someone else.
It would help if you could provide full code for investigation.
My guess is that you have an issue calling sendMessage, since 'input' is part of your model try calling it like this:
self.sendMessage = function(items ) {
var output = self.input.concat(items);
}
or if you have defined scope:
self.sendMessage = function(items ) {
var output = $scope.input.concat(items);
}
I usually use jquery with angular.
If this is your case, you could use $.extend (true, {}, obj1, obj2)
put your most valuable object at the end (probably the edited by the user), just in case you have the same property on both and wanna stay with only one of them.

Node.js body-parser won't parse input names with square brackets in json

I have a problem with Express body-parser in Node.js
I have inputs like this:
<input id="personEmail1" type="email" name="person[0][email]" placeholder="Email1" class="form-control">
<input id="personEmail2" type="email" name="person[1][email]" placeholder="Email2" class="form-control">
After when I submit my form I got this in my console.log:
{ 'person[0][email]': 'info#sss.sss', 'person[1][email]': 'info#ggg.ggg' }
But I want it to be parsed in json format:
{ person: [{email: 'info#sss.sss'}, {email: 'info#ggg.ggg'}] }
What I'm doing wrong?
for express version 4.x when you need to manually install body-parser and multer, and you want to get nested properties from the post, e.g. data[test] in the form of req.body.data.test, you need to set:
app.use(bodyParser.urlencoded({ extended: true }));
Even though you've used valid Javascript syntax for your keys, they are still just strings and no JSON parser will attempt to call eval on them.
However, JSON already has a concept of arrays, which should work in the way you are expecting them to.
It would be useful to show us the code in which the call to console.log happens. But I would guess that instead, you need to rethink the naming convention for your field names.
<input id="personEmail1" type="email" name="personEmail1" placeholder="Email1" class="form-control">
<input id="personEmail2" type="email" name="personEmail2" placeholder="Email2" class="form-control">
Then create the Javascript object from that data.
function handler(req, res) {
var people = [
{ email: req.body.personEmail1 },
{ email: req.body.personEmail2 }
];
console.log(people[0]); // person 1
console.log(people[1]); // person 2
console.log(people); // both people
}
You are using the Express body-parser middleware bracket notation correctly. But, as an example of what can be done...
Using this view:
<form method="post">
<label for="person_email_1">Email address 1</label>
<input id="person_email_1" name="person[0][email]" type="email" value="email1#example.com"> <br>
<label for="person_email_2">Email address 2</label>
<input id="person_email_2" name="person[1][email]" type="email" value="email2#example.com"> <br>
<button type="submit">Submit v1</button>
</form>
<br>
<form method="post">
<label for="person_email_1">Email address 1</label>
<input id="person_email_1" name="person[email][0]" type="email" value="email1#example.com"> <br>
<label for="person_email_2">Email address 2</label>
<input id="person_email_2" name="person[email][1]" type="email" value="email2#example.com"> <br>
<button type="submit">Submit v2a</button>
</form>
<br>
<form method="post">
<label for="person_email_1">Email address 1</label>
<input id="person_email_1" name="person[email]" type="email" value="email1#example.com"> <br>
<label for="person_email_2">Email address 2</label>
<input id="person_email_2" name="person[email]" type="email" value="email2#example.com"> <br>
<button type="submit">Submit v2b</button>
</form>
<br>
<form method="post">
<label for="person_email_1_address">Email address 1</label>
<input id="person_email_1_address" name="person[emailAddresses][0][address]" type="email" value="email1#example.com">
<input id="person_email_1_description" name="person[emailAddresses][0][description]" type="text" value="lorem ipsum 1"> <br>
<label for="person_email_2_address">Email address 2</label>
<input id="person_email_2_address" name="person[emailAddresses][1][address]" type="email" value="email2#example.com">
<input id="person_email_2_description" name="person[emailAddresses][1][description]" type="text" value="lorem ipsum 2"> <br>
<button type="submit">Submit v3</button>
</form>
...and this post handler:
function postHandler(req, res) {
console.log(JSON.stringify(req.body)); // show in console
res.send(req.body); // show in browser
}
Version 1 (your version, which works for me, and returns your desired result) req.body:
{
"person": [
{"email": "email1#example.com"},
{"email": "email2#example.com"}
]
}
Versions 2a & 2b (an array of strings, with/without index number) req.body:
{
"person": {
"email": [
"email1#example.com",
"email2#example.com"
]
}
}
Version 3 (an array of objects) req.body:
{
"person": {
"emailAddresses": [
{
"address": "email1#example.com",
"description": "lorem ipsum 1"
},
{
"address": "email2#example.com",
"description": "lorem ipsum 2"
}
]
}
}
I've personally used versions 2 & 3 on a node/Express/jquery/Bootstrap line of business app where a person or business account can have unlimited telephone numbers, email addresses, and URLs. The body-parser bracket notation made it stupid easy.
I used this https://github.com/macek/jquery-serialize-object to serialize my form as JSON. It parses data as I want and it's easy to use.
$.ajax({
type: 'post',
data: $form.serializeJSON(),
contentType: 'application/json',
url: $url,
success: function (result) { ... }
});
I think this is the easyest

Add form information in to URL string and submit

I need to get information from my online form added in to my URL string and get it submitted to the dialler.
I have a working URL string that submits data to our dialler ok.
I need to get the first name, last name and phone number from the form submission in to the URL string.
This is how the URL string looks;
http://domain.com/scripts/api.php?user=admin&pass=password&function=add_lead&source=MobileOp&phone_number=07000000000&phone_code=44&list_id=3002&first_name=NAME&last_name=SURNAME&rank=99&campaign_id=campaign&callback=Y&callback_datetime=NOW
This is the form I have;
<form id="contact_form" method="post" action="">
<div class="contactform">
<fieldset class="large-12 columns">
<div class="required">
<label>Your First Name:*</label>
<input name="first_name" type="text" class="cms_textfield" id="first_name" value="" size="25" maxlength="80" required="required" />
</div>
<div class="required">
<label>You Last Name:*</label>
<input name="last_name" type="text" class="cms_textfield" id="last_name" value="" size="25" maxlength="80" required="required" />
</div>
<div class="required">
<label>Phone Number:*</label>
<input name="phone_number" type:"number" id="phone_number" size="25" maxlength="11" required="required"></input>
</div>
</fieldset>
<p class="right"><strong>Call us now on 01656 837180</strong></p>
<div class="submit"><input type="submit" value="Submit" class="button small radius"></div>
</div>
</form>
I am struggling to get anywhere with this. I have a basic knowledge of PHP.
If you change your form to method="GET" and the action to your url action="http://domain.com/scripts/api.php" it will include it in the URL string. That said, showing a user's password as a query string variable is probably a bad idea in the long run.
Instead, you can process the input from the form in PHP by referring to the $_POST array in your code. For example, to get the first name you'd just use $_POST['first_name']
Change
<form id="contact_form" method="post" action="">
to
<form id="contact_form" method="GET" action="">
(notice the method 'GET'). GET sends form variables through the URL.
You can use PHP for this.
if you have an input field of name attribue 'first_name', It'll be stored in the variable $_POST['first_name'] in case of POST as method and $_GET['first_name'] in case of GET method
If you have a url
http://domain.com/scripts/api.php?user=admin&pass=password&function=add_lead&source=MobileOp&phone_number=07000000000&phone_code=44&list_id=3002&first_name=NAME&last_name=SURNAME&rank=99&campaign_id=campaign&callback=Y&callback_datetime=NOW,
notice the x=y pattern repeating in it, like user=admin. Here, the first element, x becomes the key to tha PHP array and the second becomes the value.
You can use this function. on your submission page
<script type="text/javascript">
function iter() {
var str = "";
$("#contact_form .contactform .required :input").each(function () { // Iterate over inputs
if ($(this).attr('id')) {
str += $(this).attr('id') + "=" + $(this).val() + "&"; // Add each to features object
}
});
str = str.substring(0, str.length - 1);
$.ajax({
type: "GET",
url: "http://domain.com/scripts/api.php",
data: str,
async: true,
error: function (error) {
},
success: function (data) {
}
});
}
</script>
just attach it to the submit button as shown below
$("#contact_form .submit").on("click", function () {
iter();
return false;
});