Looking for a easier cleaner way to save ajax POST data to a django model and avoid hardcoding for saving? - ajax

I have been working on a django project that requires a large amount of user input and processing and am sick of hardcoding the data in the view in order to save it to my models as seen below.
mymodel = TheModel.objects.get(id=model.id)
mymodel.name = request.POST.get('name')
mymodel.zip = request.POST.get('zip')
...
mymodel.save()
Except instead of two model attributes like I used above there are sometimes up to 25 that need to be saved.
I am using ajax to serialize the forms and send them to my views where they are saved. I am looking for the cleanest way possible to get around this problem. Less code the better and I am willing to reformat my models if there is a way that significantly shortens the number of lines of code I have now.
Thanks

you may want to have a look at ModelForms

This method will work, although you have to be careful to add a model field / ajax parameter at the sametime for it to work
Given:
Form1
<form method="post">
<input name="parameter1" />
<input name="parameter2" />
<input name="parameter3" />
</form>
Write javascript code so the data going across the wire looks like this JSON (form serialization
will probably not work)
{ parameter1 : "some data", "parameter2" : "some data", parameter3 : "some data" }
Then, you have a django model that looks like this
class MyModel(models.Model):
parameter1 = models.StringField()
parameter2 = models.StringField()
parameter3 = models.StringField()
You can save/update with code like this:
params = dict(request.POST)
m = MyModel.objects.create(**params)
or
m = MyModel.objects.get(id=ID)
m.update(force_update=False,**params)
If your parameters do not line up the code will fail though.

Related

How to populate select fast and only once

I have over 3200 rows in a Google Sheet. I need a dropdown with each value on a web app.
I have this in Apps Script:
function doGet(e) {
var htmlOutput = HtmlService.createTemplateFromFile('CensusWebApp2');
var streets = getStreets();
var businessNames = getbusinessNames();
htmlOutput.message = '';
htmlOutput.streets = streets;
htmlOutput.businessNames = businessNames;
return htmlOutput.evaluate();
}
function getbusinessNames(){
var ss= SpreadsheetApp.getActiveSpreadsheet();
var StreetDataSheet = ss.getSheetByName("businessNames");
var getLastRow = StreetDataSheet.getLastRow();
var return_array = [];
return_array= StreetDataSheet.getRange(2,1,getLastRow-1,1).getValues();
return return_array;
}
This is the HTML code
<select type="select" name="IntestazioneTari" id="IntestazioneTari" class="form-control" >r>
<option value="" ></option>
<? for(var i = 0; i < businessNames.length; i++) { ?>
<option value="<?= businessNames[i] ?>" ><?= businessNames[i] ?></option>
<? } ?>
</select><be>
I'm creating an app similar to surveys forms, but this dropdown will be the same for every entry.
Is there a way to load this only once and not every time the form is submitted and got again for a new survey entry? (from the same operator/device)
I believe your goal as follows.
You want to use the value of businessNames retrieved from Google Spreadsheet at HTML side.
The value of businessNames is not changed. So you want to load the value only one time.
In this case, how about declaring the value in the tag of <script> as a global? When this point is reflected to your script it becomes as follows.
Modified script:
In this case, your HTML side is modified.
<select type="select" name="IntestazioneTari" id="IntestazioneTari" class="form-control" >r>
<option value="" ></option>
<? for(var i = 0; i < businessNames.length; i++) { ?>
<option value="<?= businessNames[i] ?>" ><?= businessNames[i] ?></option>
<? } ?>
</select>
<input type="button" value="ok" onclick="test()">
<script>
const value = JSON.parse(<?= JSON.stringify(businessNames) ?>); // Here, the value of "businessNames" is retrieved.
function test() {
console.log(value);
}
</script>
In this modification, when the HTML is loaded, the value of businessNames is added to the HTML by evaluate() method. At that time, businessNames is given to HTML and Javascript. By this, const value = JSON.parse(<?= JSON.stringify(businessNames) ?>); has the value of businessNames. In order to confirm this value, when you click a sample button of <input type="button" value="ok" onclick="test()">, you can see the value at the console. By this, you can use the value of businessNames at the Javascript side after HTML is loaded.
Reference:
HTML Service: Templated HTML
As the values from the spreadsheet won't change, I created a really long text row with all the options and pasted them directly in the HTML.
I made the same with the other information. Load time decreased enormously.
This is the code I use to generate the values:
return_array= "<option>" + businessNamesSheet.getRange(2,1,getLastRow-1,1).getValues().join("</option><option>")+"</option>";
We are talking about performance, and there are 3 things you need to do when doing so:
Make measurements
Make measurements again
And make some more measurements
A change in the code could have a negative impact for a reason that you didn't expect (it's hard to keep every single little detail in mind). When making a Google Apps Script web app, you have 3 reported times:
The timings in your browser. How much did it really take to load the entire page
The running time on Google Apps Script execution log.
Small timing inside your application using console.time (reference), console.timeLog (reference) and console.timeEnd (reference) (collectively called console timers).
Note that the first 2 may change without you changing a thing, probably because of the inner working in Google.
So let's start doing what I said: measuring. I'd measure:
The entire doGet function
The getStreets()
The getbusinessNames()
The template.evaluate()
How much time it takes to load the page (browser)
This will give me a rough idea on what takes most of the time. Knowing that, you can try the following ideas.
Note that I don't have your code so I can't tell how it will effect your times, so your mileage may vary. Also note that most ideas could be implemented simultaneously, this doesn't mean it's a good idea and can even slow what a single idea could have achieved.
Idea 1: Copy the generated options into the template
If you don't need to load the options from somewhere (like I suppose you are doing), you could generate the template once, copy the generated options and paste it to the HTML. This will obviously avoid the problem of having the request the list of options and evaluating them every time, but you lose flexibility.
Idea 2: Having the options in code instead of somewhere else
If the options won't change or you will be changing them, you could add them into your code:
const BUSINESS_NAMES = `
business 1
hello world
another one
and another one
`
function getbusinessNames() {
return BUSINESS_NAMES
.split('\n')
.filter(v => !!v) // remove empty string
}
It's similar to idea 1 but it's easier to change the values when needed, specially when using the V8 support for multi line strings.
Idea 3: Use a Google Apps Script cache
If what's taking time is querying the options, you could use CacheService (see reference). This would allow you to only query the options every X seconds (up to 6 hours) instead of every time.
function doGet(e) {
// [...]
const cache = CacheService.getScriptCache()
let businessNames = cache.get('businessNames')
if (businessNames == null) {
businessNames = getbusinessNames()
cache.put('businessNames', businessNames, 6*60*60)
}
// use businessNames
// [...]
}
In this case I've only done it with businessName but it can also be use in streets.
having a 6 hour cache means that it could take up to 6 hours for a change in the list to propagate. If you add the options manually you could add a function to force the reloading it:
function forcecacheRealod() {
cache.put('streets', getStreets(), 6*60*60)
cache.put('businessNames', getbusinessNames(), 6*60*60)
}
Idea 4: Improve how you load the data
Is very common for new Google Apps Script users to iterate the rows one by one getting the value. It's way more efficient to get the proper range with all the rows and columns and call getValues (reference).
Idea 5: do a fetch instead of submitting the form
If what is happening is that it takes time to load after sending the data, it might be a good idea to use google.script.run (reference) instead of making a form and submitting it, since it could prevent reloading the entire page again.
Idea 6: SPA web app
The result of doubling down on the last idea. Same benefits and you could load the necessary data in the background while the user lands on the home page.
Idea 7: Load the options dynamically
Use google.script.run (reference) to load the options once the page has already been loaded. May actually be slower but you can give faster feedback to the user.
Idea 8: Save the options in localStorage
Requires idea #7. Save the dynamically loaded options into localStorage(see reference) so the user only needs to wait once. You may need to load them once in a while to make sure they are up-to-date.
References
Console timer (MDN)
CacheService (Google Apps Script reference)
Range.getValues() (Google Apps Script reference)
Class google.script.run (Client-side API) (Google Apps Script reference)
Window.localStorage (MDN)

Adding a deform form in an existing page (mako template) validator not called?

I have an existing (WIP) pyramid project, with the simplistic forms all being done by hand. As the user requirements have been steadily increasing in complexity, I wanted to integrate deform forms to simplify my own maintenance/programming tasks.
My initial test was to try for an interfield form[1], the purpose being to ensure that a certain date predates another date in the form. Here's the simplified definition for the schema and validator:-
class Schema(colander.MappingSchema):
startdate = colander.SchemaNode(colander.Date())
enddate = colander.SchemaNode(colander.Date())
def validator(form, value):
if value['enddate'] - value['startdate'] < 0:
exc = colander.Invalid(form, 'Start date must precede End date')
exc['enddate'] = 'Must be after %s' % value['startdate']
raise exc
schema = Schema(validator=validator)
form = deform.Form(schema, buttons=('submit',))
I then pass the form to my mako template and call:-
${form.render() | n}
This renders the form properly, and my date selectors work (of course, after I had to mess around with loading the correct CSS and javascripts). However clicking submit doesn't do any validation (not even the basic 'you didn't enter a value'), instead it goes right back to my view_config.
What could I be missing?
[1] - https://deformdemo.pylonsproject.org/interfield/
It turns out deform doesn't handle the validation automatically, and I have to actually call validate, something like below:-
try:
appstruct = form.validate(request.POST.items())
except deform.ValidationFailure as e:
return {'form': e.render()}

Querying HTML attributes in Dart

I have HTML in this format:
<form name="fruit_name">
<input id="fruit-name" type="hidden" name="Banana">
</form>
I have Dart querying the fruit name like this:
var fruitName = query('#fruit-name').attributes.values.last;
This works great in Chrome and Safari. But In Firefox, the attributes come back in a different order, so name is no longer last. What's the best way to grab the attribute I'm after without relying on the browser so much?
attributes is a Map, so this should work:
var fruitName = query('#fruit-name').attributes['name'];
You can use :
var fruitName = query('input#fruit-name').name;
The result of the query is in fact a InputElement and you have more member that in a simple Element.
By prepending #fruit-name with input you will tell to the analyzer that the result of query is an InputElement. Without that you would get a warning ( There is no such getter 'name' in 'Element' ).
Finally, from a performance point of view, the best way to do this is with document.getElementById(id) because getElementById is really faster than querySelector ) :
InputElement fruitNameElement = document.getElementById('fruit-name');
var fruitName = fruitNameElement.name;
Here, the first line allows to type fruitNameElement to prevent warning when calling fruitNameElement.name.

Extract part of HTML document in jQuery

I want to make an AJAX call to an HTML-returning page, extract part of the HTML (using jQuery selectors), and then use that part in my jQuery-based JavaScript.
The AJAX retrieval is pretty simple. This gives me the entire HTML document in the "data" parameter of the callback function.
What I don't understand is how to handle that data in a useful way. I'd like to wrap it in a new jQuery object and then use a selector (via find() I believe) to get just the part I want. Once I have that I'll be passing it off to another JavaScript object for insertion into my document. (This delegation is why I'm not using jQuery.load() in the first place).
The get() examples I see all seem to be variations on this:
$('.result').html(data);
...which, if I understand it correctly, inserts the entire returned document into the selected element. Not only is that suspicious (doesn't this insert the <head> etc?) but it's too coarse for what I want.
Suggestions on alternate ways to do this are most welcome.
You can use your standard selector syntax, and pass in the data as the context for the selector. The second parameter, data in this case, is our context.
$.post("getstuff.php", function(data){
var mainDiv = $("#mainDiv", data); // finds <div id='mainDiv'>...</div>
}, "html");
This is equivalent to doing:
$(data).find("#mainDiv");
Depending on how you're planning on using this, $.load() may be a better route to take, as it allows both a URL and a selector to filter the resulting data, which is passed directly into the element the method was called on:
$("#mylocaldiv").load("getstuff.php #mainDiv");
This would load the contents of <div id='mainDiv'>...</div> in getstuff.php into our local page element <div id='mylocaldiv'>...</div>.
You could create a div and then put the HTML in that, like this…
var div = $("<div>").html(data);
...and then filter the data like this…
var content = $("#content", div.get(0));
…and then use that.
This may look dangerous as you're creating an element and putting arbitrary HTML into it, but it's not: anything dangerous (like a script tag) will only be executed when it's inserted into the document. Here, we insert the data into an element, but that element is never put into the document; only if we insert content into the document would anything be inserted, and even then, only anything in content would be inserted.
You can use load on a new element, and pass that to a function:
function handle(element){
$(element).appendTo('body');
}
$(function(){
var div = $('<div/>');
div.load('/help a', function(){handle(div);});
});
Example: http://jsbin.com/ubeyu/2
You may want to look at the dataFilter() parameter of the $.ajax method. It lets you do operations on the results before they are passed out.
jQuery.ajax
You can do the thing this way
$.get(
url,
{
data : data
},
function (response) {
var page_content = $('.page-content',response).get(0);
console.log(page_content);
}
)
Here in the console.log you will see the inner HTML of the expected/desired portion from the response. Then you can use it as your wish

codeigniter associative array in post

I normally name my db specific fields in my forms like this "objectname[columnname]", I tseems CI cant access these values using $this->input->post('objectname[columnname]'), what do I do? there is not a chance in hell im renaming 100+ form fields.. I am actually disliking CI, it really is getting in the way of progress by changing the de facto PHP norms...
And were you using $_POST['objectname[columnname]'] or $_POST['objectname']['columnname'] ?
Have you tried the equivalent for the latter
$obj = $this->input->post('objectname');
echo $obj['columnname'];
?
If it works, you can write you own helper to retreive that like post_val('objectname[columnname]').
I saw this post whilst looking for a similar issue, but worked out a CI way to do it, sorry if I'm resurrecting it, but it does appear fairly high on the Google results.
// Load the 'array' helper
$this->load->helper('array');
// Use the 'element' function to return an element from the array
echo element('ColumnName', $this->input->post('ObjectName'));
Hope this helps anyone who comes here in future.
HTML code:
<input type="text" value="" name="myPostArrayName[]">
<input type="text" value="" name="myPostArrayName[]">
Handling form with codeigniter:
$data = $this->input->post('myPostArrayName', TRUE);
You can access data in order like this
echo 'Value of the first element in the form array is '.$data[0];
echo 'Value of the second element in the form array is '.$data[1];
I think someone who has access to codeigniter documentation, had better to add a simple html post array handling example.
I seems I can rely on the $_POST var, but I thought this was reset?
You can cast the post array as an object and use method chaining to return sub-arrays (now properties) using PHP 5.3's method chaining all on one line.
Extend the input class by making a class called MY_Input and put the extended class in the application/core folder. CI 2.0 will automatically use the extended class with the MY_ prefix, and you can add methods to this new class. Extending the input class is cleaner than making helpers.
This method casts the post array, or a nested array (a sub array below the parent), as an object.
/* Cast an array from CI post as an object and return the object */
public function post_obj($key = null){
$post_return = $this->post($key);
if (false === $post_return)
return false;
return (object)$post_return;
}
Now I can retrieve nested values in one line of code using PHP 5.3's method chaining for objects.
$active = $this->input->post_obj('user')->active;
I just went with the $_POST['objectname']['colname'] option as i usually do even though this is probably not the CI way..

Resources