I am using a form to post a record to my database in Django. The form looks something like this:
<form>
<div>Field 1</div>
<input/>
<div>Field 2</div>
<input/>
...
<a id='save'>Save</a>
</form>
I am using an event listener to let me know when this Save link has been clicked. When it is clicked, I would like to store the fields as a record in the database.
Here is my JS:
const savebtn = document.querySelector('#save');
savebtn.addEventListener('click', showSelected);
function showSelected(e) {
// post record to database here
window.location.replace("/"); // redirect to home page
I am hoping to use some python to post this record like shown below:
e = Entry()
e.field1 = 1
e.field2 = 2
e.save()
But I don't know how I can access a python function within JS. I'm thinking maybe a template tag could work here but can't figure out how to use it. Ideally I wouldn't have to rewrite the form as the formatting is kind of specific, is there a way to make this work?
You have to create an endpoint that will do the algorithm.
ie. some urls.py:
urlpatterns = [
path('my_save_view', my_save_view, ...),
]
views.py:
def my_save_view(request):
e = Entry()
e.field1 = 1
e.field2 = 2
e.save()
and in javascript just get/fetch that endpoint when you want to. You can pass any values to the view with either GET or POST method in a standard way if needed.
Related
I am working on a flashcards app using flask. It is similar to Anki if you know about that. When I click on a deck, I want it to go to study_session.html where it displays the first card of the selected deck. Currently I have a home screen that lists the user's decks. Each list item is a button element with an onclick attribute that calls a javascript method that takes the selected deck's name as a parameter. The method sends a post request to the flask route "/study-session" along with the name of the deck. This allows the route's method to get the selected deck and send the appropriate flashcard to the "study_session.html" page to be displayed. What's wrong is that either the javascript method or the server-side route method, or both do not seem to be executing. When I click the deck I want to study, nothing happens. I would like someone to point out my mistake and offer the appropriate fix. If I need ajax, please show me how the javascript would look like. Thank you.
Here is the html that has the button with an onclick attribute:
{% for deck in user.decks %}
<li class="list-group-item">
<button class="btn btn-link pl-0" onclick="startStudySession({{ deck.deck_name }})">{{deck.deck_name}}</button>
Here is the javascript:
// Takes deckId and sends post req to study-session route.
// Then runs study-session view
function startStudySession(deckName) {
// Specify what route to send request to
fetch("/study-session", {
method: "POST",
body: JSON.stringify({deckName: deckName})
}).then((_res) => {
window.location.href = "/study-session"
});
}
Here is the route method on the python server:
#views.route('/study-session', methods=['GET', 'POST'])
def study_session():
"""
Renders study_session page with one flashcard displayed. Handles all requests on study_session page.
"""
request_data = json.loads(request.data)
cur_deck = current_user.get_deck(request_data['deckName'])
cur_card = cur_deck.notes[0].flashcards[0]
front = cur_card.front_text
back = cur_card.back_text
print("is this working?")
return jsonify('', render_template("study_session.html", user=current_user, front=front, back=back))
Using this tutorial http://aymeric.gaurat.net/2010/how-to-develop-live-search-textbox-in-ruby-on-rails/ as a guideline I have tried to implement my own search bar which will allow a user to start to write in another user's name and see that user (if in the database) as a drop down selection option.
However, when I run my code and type something in, nothing happens.. I am very new to Ruby, Rails and jquery/ajax, so if it's something obvious I apologise.
Here is my code:
customers_search.html.erb:
<form id = "live-search-form" method = get action = "<%= search_customers_path %>">
<input id="search-box" name = "query" type="text" placeholder="find colleagues" />
</form>
<div id="search-results"></div>
my customers controller search method:
def search
#q = "%#{params[:query]}%"
#customers = Customer.where("first_name LIKE ? or last_name LIKE ? ",#q,#q)
end
and here is my application.js file:
var main = new function(){
$('#search-box').bind("keyup", function(){
var form = $("#live-search-form"); //gets form wrapping search bar
var url = "/customers/_search"; //_search action
var formData = form.serialize(); //gets data in the form
$.get(url, formData,function(html){ //perform an AJAX get
$('#search-results').html(html);
});
});
}
$(document).ready(main);
Am I missing an AJAX plugin or something down those lines? Or is it a problem with my actual search method? How can I fix this?
I was trying to match whatever the user typed to either a customer's :first_name or :last_name.
Thanks guys
I have now stopped nesting my 2 forms and placing them side by side. I have also fixed my 'main' undefined error. Still I am getting no response to my search bar.
I'm working on an open source OFFLINE pyramid application running on a raspberry pi so I want to keep everything as simple and efficient as possible. The application is happily communicating with my postgresql db using sqlalchemy. I don't want to complicate things by using Deform or other widgets or add authentication or use session cookies (as in the Pyramid Doc examples). My template (chameleon) using the twitter bootstrap contains a form (snippets):
<form action="." method="post">
<a class="btn" href="#" id="actionbtn" onclick="camCapture()">Capture</a>
<p><input type="text" class="input-small' id="projfld" value="Project Name">
<p><label class="control-label" for "select01">Brightness</label>
<div class="btn-group' data-toggle="buttons-radio" id="bright">
<button type="button" class="btn btn-small" id="btnoff">Off</button>
<button type="button" class="btn btn-small" id="btn10">10%</button>
<button type="button" class="btn btn-small" id="btn50">50%</button>
<button type="button" class="btn btn-small" id="btnfu">Full</button>
</div>
<p><label class="checkbox"><input type="checkbox" id="auto">Auto</label>
</form>
There are other buttons and fields in the form, but this is the diversity. When user clicks the actionbtn the javascript in the template:
function camCapture()
{
jQuery.ajax({
url: '/camcapture',
dataType:"json",
type: 'POST',
data: "test",
});
}
is triggering the function in views.py (trimmed code below) and adding system data to a postgresql database using sqlalchemy.
#view_config(renderer="json", name="camcapture")
def camcapture_view(self):
#
# stuff happens
#
now = datetime.datetime.utcnow()
outfile = ("/home/brian/cam/%s" % now.strftime("CAM_%Y%m%d%H%M%S") + ".jpg")
cam_event = Event('fileprefix',now,'CAM',outfile,'memo field content','project')
DBSession.add(cam_event) # data, Event arguments above, are written to database
#
# other stuff happens
#
return []
The user needs only return to the original page (no redirect) as they are likely to alter data in one or more fields and submit the form again.
I reckon this is a common application but I cannot find an example or tutorial to help new developers like me. Perhaps it is too simple and I am missing important fundamental information? My db schema is, I believe appropriately defined in models.py:
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import (
scoped_session,
sessionmaker,
)
from zope.sqlalchemy import ZopeTransactionExtension
engine = create_engine('postgresql://scan:scan#localhost:5432/scan', echo=True) # Establishes SQLAlchemy link to postgresql db
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
class ORMClass(object): # Allows grabs of database values as variables in python
#classmethod
def query(class_):
return DBSession.query(class_)
#classmethod
def get(class_, record):
return Session.query(class_).get(record)
Base = declarative_base(cls=ORMClass)
class Event(Base): #Schema description which allows automated table generation if it doesn't already exist
__tablename__ = 'event'
record = Column(Integer, Sequence('event_record_seq'), primary_key=True, nullable=False)
fileprefix = Column(String)
date = Column(DateTime, nullable=False)
eventtype = Column(String(3))
filename = Column(String)
memo = Column(String)
project = Column(String)
extend_existing = True
def __init__(self, fileprefix, date, eventtype, filename, memo, project): #constructor for new event records
self.fileprefix = fileprefix
self.date = date
self.eventtype = eventtype
self.filename = filename
self.memo = memo
self.project = project
def __repr__(self):
return "<Event('%s','%s','%s','%s','%s','%s')>" % (self.fileprefix, self.date, self.eventtype, self.filename, self.memo, self.project)
Base.metadata.create_all(engine)
Thank you. Yes, #Sergey, I'm having problems accessing the values in the JSON dict. I have gone through that tutorial prior to posting my query. In my attempts to emulate it, I am used stringify to construct the JSON body. I'm taking form information and setting the values into a variable.
jsondata= JSON.stringify({"var1name" : $(#projfld").val(), "var2name" and so on.
On my post--> data: jsondata.
In the tutorial they print the tuples. I'm trying to set each pair as variables for use in the code and can't find the syntax to do that. I want to do something like
var1 = request.json_body(index(0)), var2 = request.json_body(index(1)), and so on.
In the simplified version above I'm sending the string "test" as JSON data. I can then set a string in my Python code to that string, but how do I parse the parts of the JSON body? variablez = request.json_body is the tutorial example equivalent. Since the tuples contain name and value pairs I thought variables might already be declared in the body and I could use them in my code and send some of the values off to my database.
The reason I'm not using a simple submit button is that there are several buttons on the form (mentioned) each of which will take different pieces of information from the form and activate different pieces of code on the server side for immediate hardware action (sending serial commands in this case). Were you trying to suggest that pressing enter in a field will automatically submit the form? Not true, at least not on the system I'm using (which I have control over in this offline application).
It's not really clear what is the exact problem you're having, but I think you don't know how to access values in the JSON dict which the client sends in the POST request body, right?
The answer is request.json_body. Here's a short tutorial in Pyramid docs: Dealing With A JSON-Encoded Request Body. The method is just a thin wrapper which does return json.loads(request.body)
If you send a Javascript Object on the client side (as you seem to be doing with var jsondata = JSON.stringify({var1name: $(#projfld").val(), var2name:...});), on the Python side you'll get a dict. There's no automatic assigning values to any variables, it's just a plain normal dict:
data = request.json_body
event.fileprefix = data['fileprefix']
event.eventtype = data['eventtype']
and so on.
(Another unrelated suggestion: contrary to what Pyramid tutorials suggest, you don't need that unsightly __init__ method, SQLAlchemy generates a default constructor for your model which does exactly the same. Just remove it)
Problem Overview:
I am creating a Django-based client with the intent of returning data from a web service. The goal of this project is to return data to the user from the web service based on the values selected by the user in a form. Upon the form submit, a query string is generated, sent to the web service and the page data is returned as a string. Currently, that data is displayed to the user in the browser. I want to provide the functionality that would allow the user to click a button and download the data.
Question:
How could I return the data to the user when they click a button in their browser for download? How do I make different options of the same data available (i.e. application/json, or text/csv)?
Current (not-working) Implementation:
I am attempting, and failing, to do the following:
views.py
Returning a render_to_response object of my template. To the template I pass the form, and the data in it's various forms.
def view(request):
#Do stuff, get data as string
#Get data into needed formats (see utils.py)
jsonData = jsonToJsonFile(dataString, fileName)
return render_to_response('template.html', {'someForm' : aForm,
'regularData' : stringData,
'jsonData' : jsonData...})
utils.py
Contains functions to take the data as a string and return response objects. This part I am unsure if I am doing correctly. I call these functions in the view to get jsonData (and csvData) into their proper formats from the original data string.
def jsonToJsonFile(dataString, fileName):
#Get the data as json
theData = json.dumps(dataString)
#Prepare to return a json file
response = HttpResponse(theData, mimetype = 'application/json')
response['Content-Disposition'] = 'attachment; filename=' + str(fileName) + '.json'
#return the response
return response
template.html
I am currently passing the responses into the template. This is where I am really lost, and have not yet begun to find a good solution. I expect I will need to use javascript to return the variables (jsonData and csvData) to the user when the button is clicked. I have attempted the use of the onclick action of the anchor class, and then using javascript to return the django variable of the response - but this really isn't working.
<li class = 'button'>
<a href = "#dataButtons" onclick = "javaScript:alert('test');">
TEST
</a>
</li>
<li class = 'button'>
<a href = "#dataButtons" onclick = "javaScript: var a = '{{ jsonData }}'; return a;">
JSON
</a>
</li>
I put the test part in there to, well, test whether or no the alert would work. It does. However, when I click the button for the json data, nothing happens.
Am I approaching this completely wrong? Or is there something small that I am missing?
Solution:
After looking at the problem a little further and talking to a colleague about it, it seems as though my problem lies in trying to pass the response object to the javascript. For those interested, I solved the problem with a little careful re-routing of the data.
views.py
In my views.py I added a couple lines of code in my main view that would set variables of two extra views (one for csv one for json) to the response objects holding the data. These two extra views would then be called when their respective buttons were pressed, returning the httpresponse and prompting the user for download.
#MAIN VIEW FUNCTION
def view(request):
#Do stuff, get data as string
#Get data into needed formats
jsonData = jsonToJsonFile(dataString, fileName)
#Set values to external view ****NEW PART****
returnJSON.jsonData = jsonData
#Render main template
return render_to_response('mainTemplate.html', {'someForm' : aForm,
'regularData' : dataString})
#SECONDARY VIEW TO RETURN JSON DATA TO USER ****NEW PART****
def returnJSON(request):
#Simply return the response
return returnJSON.jsonData
template.html
Then, when the button is pressed by the user, the anchor is linked via the url to the secondary django view that will present the download option to the user.
<li class = 'button'>
<a href = "{% url client.views.returnJSON %}">
JSON
</a>
</li>
urls.py
Lastly, I just pointed my url patterns to the view.
urlpatterns = patterns('',
(r'^somesite/$', views.view),
(r'^somesite/json$', views.returnJSON),
)
So far, this method has worked great for me! If anyone has any other suggestions, or a better method, I would certainly be open to hear it.
I think you need to change your javascript to make it start a file download - see this question & answer:
starting file download with JavaScript
I'm trying to build a Django app for inputting science data, but am running into problems coming up with an elegant way to describe the possible forms.
I have two models, HCameraImage and MCameraImage that inherit from an Image model. On the site, the user can fill in HTML forms that populate either of these two models. In another words, I'm using HCameraImageForm(request.POST) to populate the model. Once I send it over to the client side, I find myself having to manually keep track of what form type is being sent to Django by appending an imagetype to the POST data:
if request.POST['imagetype'] == "HCameraImage":
form = HCameraImageForm(request.POST)
form.save()
if request.POST['imagetype'] == "MCameraImage":
form = MCameraImageForm(request.POST)
form.save()
...etc
Is there a more elegant way to deal with this? Ideally I want to have some parent base class Image that lets me do:
i = Image()
i.loadFormData(request.POST)
where loadFormData calls the correct derived function.
You could construct a string to instantiate the correct form object. Something like this should work:
import myapp.models
form_class = request.POST['imagetype'] + 'Form'
form_class = getattr(myapp.models, form_class)
form = form_class(request.POST)
form.save()