Perhaps there is a different way of going about this problem, but I am fairly new to using Django.
I have written a custom Python script and would like to run a function or .py file when a user presses a "submit" button on the webpage.
How can I get a parameter to be passed into a Python function from a submit button using Django?
Typically what is done is you'd have your form submit a post request. You'd then intercept the request in your urls.py, where you'd call your function. So if your form looks like this:
<form action="submit" method="post">
<input type="text" name="info"><br>
<input type="submit" value="Submit">
</form>
your urls.py would have something like this:
url(r'^submit', views.submit)
and your views.py would have the function that would get the parameters that were passed through the post:
def submit(request):
info=request.POST['info']
# do something with info
This link gives a more in depth explanation.
After googling for a couple of days, i managed to piece together some solution on this question, which i needed for my project.
SwankSwashbucklers gave the general approach and i just wanted to add to it to complete the circle. This might not be the only solution so i am just giving one working example. So.. your template should contain the following code (as above with some extras):
your_template.html
{% extends base.html %}
{% block main_content %}
<form action="your_view_url" method="post">{% csrf_token %}
{{ form.as_table }}
// <input type="text" name="info_name" value="info_value">
<input type="submit" value="Submit">
</form>
<p> Post Data: {{ info }} </p>
<p> Result: {{ output }} </p>
{% endblock main_content %}
If you defined your form in forms.py and/or using your models for form rendering, then examine the rendered HTML to find out what was given to the "value" attributes rendered by Django in the form. "value" is what will be submitted in your POST request.
Your defined view will display the form, and also will process it once submited, so you will have 2 sections in it with an 'if' statement.
Django uses "GET" to open views, so the initial rendering display blank form
views.py
import subprocess
def your_view_name(request):
if request.method == 'GET':
form = your_form_name()
else:
if form.is_valid():
info = request.POST['info_name']
output = script_function(info)
// Here you are calling script_function,
// passing the POST data for 'info' to it;
return render(request, 'your_app/your_template.html', {
'info': info,
'output': output,
})
return render(request, 'your_app/your_template.html', {
'form': form,
})
def script_function( post_from_form )
print post_from_form //optional,check what the function received from the submit;
return subprocess.check_call(['/path/to/your/script.py', post_from_form])
forms.py
class your_form_name(forms.Form):
error_css_class = 'error' //custom css for form errors - ".error";
required_css_class = 'required' //custom css for required fields - ".required";
info_text = forms.CharField()
The "info_text" is what will be rendered in the template as in "input" field when you call form = your_form_name() . More on Django forms is here https://docs.djangoproject.com/en/1.9/ref/forms/fields/
When you press submit, the form will submit the data back to itself, so your view will pick that its a POST and will run is_valid , and then the value of output will be the error code returned by subprocess.check_call . If your script run OK, the value of "output" will be "0".
This is for "Django 1.4", and "Python 2.6". Latest versions have subprocess.check_output which can actually return the output from the script so you can render it back on the template.
Hope this helps:)
In django 1.11 and python 3.6, I had to use
return subprocess.run(['python', 'path_to_script//prog17.py', post_from_form], shell=False, timeout=1800)
The rest of #kossta's code worked fine.
What #SwankSwashbucklers says is the way to go.
If you also want to maintain your script separate from the view, you can also use a custom management command and use call_command to call it in the view. This way you can run the script from the command line as well with manage.py mycommand [myargument].
Except for the last part(calling the python script), #kosstas's code ran perfectly in my system. I am sharing what I had different. I am using Python 3.8 and Django 3.1. I had to use -
return subprocess.run(['python', 'windows_path_like_D:\\path_to_script\\prog17.py', post_from_form], shell=False, timeout=1800)
This code did not display the output in my system. To see the output, I had to put "stdout=PIPE" in place of "timeout=1800". That is:
output = run([sys.executable,'windows_path_like_D:\\path_to_script\\prog17.py', model_name], shell=False, stdout=PIPE)
For this at the top, I had to include:
from subprocess import run, PIPE
This was a great relief to me to see the output in my own eyes instantly!
python manage.py <your_script_name>
Here script name is ur python file .No need to mention .py extenstion.
Related
I have a Post model that requires a certain category before being added to the database, and I want the category to be generated automatically. Clicking the addPost button takes you to a different page and so the category will be determined by taking a part of the previous page URL.
Is there a way to get the previous page URL as a string?
I have added my AddPost button here.
<aside class="addPost">
<article>
<form action="/Forum/addPost">
<input type="submit" name="submit" value="Add Post"/>
</form>
</article>
</aside>
You can do that by using request.META['HTTP_REFERER'], but it will exist if only your tab previous page was from your website, else there will be no HTTP_REFERER in META dict. So be careful and make sure that you are using .get() notation instead.
# Returns None if user came from another website
request.META.get('HTTP_REFERER')
Note: I gave this answer when Django 1.10 was an actual release. I'm not working with Django anymore, so I can't tell if this applies to Django 2
You can get the referring URL by using request.META.HTTP_REFERER
More info here: https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
I can't answer #tryingtolearn comment, but for future people, you can use request.META['HTTP_REFERER']
Instead of adding it to your context, then passing it to the template, you can place it in your template directly with:
Return
A much more reliable method would be to explicitly pass the category in the URL of the Add Post button.
You can get the previous url in "views.py" as shown below:
# "views.py"
from django.shortcuts import render
def test(request):
pre_url = request.META.get('HTTP_REFERER') # Here
return render(request, 'test/index.html')
You can also get the previous url in Django Template as shown below:
# "index.html"
{{ request.META.HTTP_REFERER }}
I am creating quiz-like web application for learning languages using Flask, Jinja, WTForms, SqlAlchemy etc. Once an user completes such a language course by successfully going through all levels stored in JSON file I want the app offer him a practice mode, where the user will answer randomly selected levels.
When I run the app, I can see radio buttons generated with values from random level as I want, but when I choose any answer and submit it, form.validate_on_submit() returns False and form.errors returns {'practiceform': [u'Not a valid choice']}. When I hard-code value to currentLevel variable, it works properly.
views.py
#user_blueprint.route('/courses/<course>/quiz/practice',methods=['GET','POST'])
#login_required
def practice(course):
courseClass = class_for_name("project.models", course.capitalize())
courses = courseClass.query.filter_by(email=current_user.email).first()
maxLevel = courseClass.query.filter_by(email=current_user.email).first().get_maxLevel()
currentLevel = randint(0, maxLevel-1) # If this value is hard-coded or fetched from db, it works correctly
dic = generateQuestion(course, currentLevel)
display = dic["display"]
correct = dic["correct"]
options = dic["options"]
form = PracticeForm(request.form)
form.practiceform.choices = [(option, option) for option in options]
if form.validate_on_submit():
practiceForm = form.practiceform.data
if ((practiceForm == correct) and courses):
# Do something
flash("Nice job", 'success')
return redirect(url_for('user.practice', course=course))
else:
# Do something else
flash("Wrong answer", 'danger')
return redirect(url_for('user.practice', course=course))
return render_template('courses/practice.html', form=form, display=display)
forms.py
class PracticeForm(Form):
practiceform = RadioField('practice')
practice.html
{% extends "_base.html" %}
{% block content %}
<form action='' method='POST' role='form'>
<p>
<!-- Tried put form.csrf, form.csrf_token, form.hidden_tag() here -->
{{ form.practiceform() }}
</p>
<input type="submit" value="submit" />
</form>
{% endblock %}
So what am I missing there? What makes difference between lets say hardcoded level 25, which works properly or if the number 25 is randomly generated within randint?
My guess is that option is a int, bug WTForms get a str from request.form.
When data comes back from requests it is treated as a string by WTForms unless you specify a type explicitly with the coerce kwarg of the wtforms.fields.*Field constructor:
practiceform = RadioField('practice', coerce=int)
So I found that randint() caused the problem because the practice(course) method was called on both GET and POST actions which led to having two different integers -> two different forms most of the time. So I refactored the code. kept the practice(course) method for GET action and created a new method which handles POST action and this solved the problem.
I have a Post model that requires a certain category before being added to the database, and I want the category to be generated automatically. Clicking the addPost button takes you to a different page and so the category will be determined by taking a part of the previous page URL.
Is there a way to get the previous page URL as a string?
I have added my AddPost button here.
<aside class="addPost">
<article>
<form action="/Forum/addPost">
<input type="submit" name="submit" value="Add Post"/>
</form>
</article>
</aside>
You can do that by using request.META['HTTP_REFERER'], but it will exist if only your tab previous page was from your website, else there will be no HTTP_REFERER in META dict. So be careful and make sure that you are using .get() notation instead.
# Returns None if user came from another website
request.META.get('HTTP_REFERER')
Note: I gave this answer when Django 1.10 was an actual release. I'm not working with Django anymore, so I can't tell if this applies to Django 2
You can get the referring URL by using request.META.HTTP_REFERER
More info here: https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
I can't answer #tryingtolearn comment, but for future people, you can use request.META['HTTP_REFERER']
Instead of adding it to your context, then passing it to the template, you can place it in your template directly with:
Return
A much more reliable method would be to explicitly pass the category in the URL of the Add Post button.
You can get the previous url in "views.py" as shown below:
# "views.py"
from django.shortcuts import render
def test(request):
pre_url = request.META.get('HTTP_REFERER') # Here
return render(request, 'test/index.html')
You can also get the previous url in Django Template as shown below:
# "index.html"
{{ request.META.HTTP_REFERER }}
I am currently making a website using django. Now I want to execute a python script from my template/view with a button on the website. It should be possible but to be honest I don't know how.
An example would be best.
Thanks for any help at all.
Well got it working now and just thought I would write my answer:
view.py:
import script as gh
def get_hostname(request):
gh.main()
return HttpResponseRedirect('/')
urls.py:
...
url(r'^get_hostname/$', 'thinco.views.get_hostname'),
...
somewhere in template:
...
<form action="/get_hostname/" method="GET">
<input type="submit" value="Liste der Thin Clients laden">
</form>
...
If you are using Django - best way, IMHO, is just to create the view that will handle your python code, and then access it at onclick event via ajax request.
yourapp/views.py
def your_python_script(request):
if request.is_ajax:
# do your stuff here
...
else:
return HttpRequest(status=400)
If you are using django also you should have jQuery, and in your template add javascript code, something like this:
$("#<your_button_id>").click( function() {
$.post("your_python_script_url", {<dict with any args you need>}, function () {
// What to do when request successfully completed
});
});
And not to forget about CRSF token if your are using it. How to handle it you can find in offical django documentation.
UPDATE
You can add csrf token to page template like:
<script>
var csrf_token = '{% csrf_token %}';
...
</script>
Next, you need to bind to the global jquery ajaxSend event, and add the token to any POST request.
$("body").bind("ajaxSend", function(elm, xhr, s) {
if (s.type == "POST") {
xhr.setRequestHeader('X-CSRF-Token', csrf_token);
}
});
Something like this should work.
Create a view function and do an #dajaxice_register decorator for it:
A silly example follows:
models.py:
class Funkyness(models.Model):
funktasm = models.CharField(max_length=128)
funfasticness = models.TextField()
urls.py:
url(r'^funk$', 'views.myFunkyView'),
views.py:
def myFunkyView(request)
render_to_request('index.htm', {'where': 'home'}, context_instance=RequestContext(request))
index.htm:
{% if where %}
You are {{ where }}
{% endif %}
When you go to http://yoursite.com/funk, you will get index.htm rendered and you will get a page that says "You are home."
Now, the dynamic part...
Write a view method as such:
from django.utils import simplejson
def getHowFunky(request, v):
html = """
<div class="my_message">
This is really funky, almost stinky...
<br />
""" + str(v) + """
</div>
"""
return simplejson.dumps({'message': html})
back in index.htm:
<script type="text/javascript>
/* first argument is return JS function, the second is the dictionary of data to sent to the python method. */
function init(){
Dajaxice.package.getHowFunky(showFunky, {'v': "Your's Truly... Fubar"});
}
function showFunky(data){
/* This is the data returned back from the AJAX (Dajaxice) call. */
document.write(data.message)
}
</script>
So, you build a python method that takes inputs and returns something. You register it with Dajaxice, you call it, passing a callback method. It runs and when it succeeds, it send the python return (possibly JSON object) to the callback method as an argument. That method then writes to the screen what it got from the Dajaxice call.
For more info on Dajaxice, go to: http://dajaxproject.com/
Props to Jorge Bastida, the sole developer of Dajax/Dajaxice!
What I could think of for an answer is to use:
Django + Dajax
Django link:
https://docs.djangoproject.com/en/1.4/
Dajax is actually ajax for django:
Visit their website and refer to their examples for quick start
http://www.dajaxproject.com/
You can create your button in django view and upon triggering your button, you can use run a python snippet, not script.
If you want to run a standalone script, you could probably check out djcelery.
http://pypi.python.org/pypi/django-celery
I'm writing a Django admin action to mass e-mail contacts. The action is defined as follows:
def email_selected(self,request,queryset):
rep_list = []
for each in queryset:
reps = CorporatePerson.objects.filter(company_id = Company.objects.get(name=each.name))
contact_reps = reps.filter(is_contact=True)
for rep in contact_reps:
rep_list.append(rep)
return email_form(request,queryset,rep_list)
email_form exists as a view and fills a template with this code:
def email_form(request,queryset,rep_list):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('email','noreply#localboast'),['redacted#email.com'],
)
return HttpResponseRedirect('thanks')
else:
form = EmailForm()
return render_to_response('corpware/admin/email-form.html',{'form':form,})
and the template exists as follows:
<body>
<form action="/process_mail/" method="post">
<table>
{{ form.as_table }}
</table>
<input type = "submit" value = "Submit">
</form>
</body>
/process_mail/ is hardlinked to another view in urls.py - which is a problem. I'd really like it so that I don't have to use <form action="/process_mail/" method="post"> but unfortunately I can't seem to POST the user inputs to the view handler without the admin interface for the model being reloaded in it's place (When I hit the submit button with , the administration interface appears, which I don't want.)
Is there a way that I could make the form POST to itself (<form action="" method="post">) so that I can handle inputs received in email_form? Trying to handle inputs with extraneous URLs and unneeded functions bothers me, as I'm hardcoding URLs to work with the code.
You can use django's inbuilt url tag to avoid hardcoding links. see...
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#url
Chances are you'd be better off setting up a mass mailer to be triggered off by a cron job rather than on the post.
Check out the answer I posted here
Django scheduled jobs
Also if you insist on triggering the email_send function on a view update perhaps look at
http://docs.djangoproject.com/en/dev/topics/signals/