I'm trying to run a redirect after I check to see if the user_settings exist for a user (if they don't exist - the user is taken to the form to input and save them).
I want to redirect the user to the appropriate form and give them the message that they have to 'save their settings', so they know why they are being redirected.
The function looks like this:
def trip_email(request):
try:
user_settings = Settings.objects.get(user_id=request.user.id)
except Exception as e:
messages.error(request, 'Please save your settings before you print mileage!')
return redirect('user_settings')
This function checks user settings and redirects me appropriately - but the message never appears at the top of the template.
You may first think: "Are messages setup in your Django correctly?"
I have other functions where I use messages that are not redirects, the messages display as expected in the template there without issue. Messages are integrated into my template appropriately and work.
Only when I use redirect do I not see the messages I am sending.
If I use render like the following, I see the message (but of course, the URL doesn't change - which I would like to happen).
def trip_email(request):
try:
user_settings = Settings.objects.get(user_id=request.user.id)
except Exception as e:
messages.error(request, 'Please save your settings before you print mileage!')
form = UserSettingsForm(request.POST or None)
return render(request, 'user/settings.html', {'form': form})
I have a few other spots where I need to use a redirect because it functionally makes sense to do so - but I also want to pass messages to those redirects.
The user_settings function looks like this:
def user_settings(request):
try:
user_settings = Settings.objects.get(user_id=request.user.id)
form = UserSettingsForm(request.POST or None, instance=user_settings)
except Settings.DoesNotExist:
form = UserSettingsForm(request.POST or None)
if request.method == 'POST':
settings = form.save(commit=False)
settings.user = request.user
settings.save()
messages.warning(request, 'Your settings have been saved!')
return render(request, 'user/settings.html', {'form': form})
I can't find anything in the documentation saying that you can't send messages with redirects... but I can't figure out how to get them to show.
Edit:
This is how I render the messages in the template:
{% for message in messages %}
<div class="alert {{ message.tags }} alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{ message }}
</div>
{% endfor %}
I'm not sure if it matters - but it looks like it's almost calling 'GET' twice from somewhere.
the URL section looks like this for these two URLS:
# ex: /trips/email
url(r'^trips/email/$', views.trip_email, name='trip_email'),
# ex: /user/settings
url(r'^user/settings/$', views.user_settings, name='user_settings'),
Nope I think the best way is to use your sessions.
from django.contrib import messages << this is optional if you choose to use django messaging
Handle your form
def handle_form(request):
...
request.session['form_message'] = "success or fail message here"
redirect('/destination/', {})
def page_to_render_errors():
...
message = None
if( 'form_message' in request.session ):
message = request.session['form_message']
del request.session['form_message']
messages.success(request, message ) #<< rememeber messages.success() along with messages.info() etc... are method calls if you choose to pass it through django's messaging
return render(request,'template_name.html', {'message_disp':message })
You cannot view the error message, because it is not getting rendered, and hence there is nowhere for the message to go. The best way to do something like this would be to render an intermediate page, which would display the error message, and redirect after timeout, something of the sort used by payment gateways while redirecting you to your bank.
You can do something like this:
def trip_email(request):
try:
user_settings = Settings.objects.get(user_id=request.user.id)
except Exception as e:
return render(request, 'no_setting_error.html', {'form': form})
and on the no_setting_error html page, insert Javscript to automatically redirect you after a timeout like:
<script>
function redirect(){
window.location.href = "/user_settings";
}
setTimeout(redirect, 3000);
</script>
This will redirect you to from the error page to the user_settings page automatically after 3 seconds.
Related
I am on Django Framework and implemented an interface where a user can do Insert and Update operations on a MySQL table.
MySQL student table does not allow duplicate entries, so if a user enters a duplicate key an Exception message is displayed. If, a user re-submits a successful query to the database the error message disappears.
What is the current issue:
If a user tries to reload the page after a bad query was sent to the database the error message is still displayed. On a page reload it returns a POST request, so it's submitting the duplicate entry again triggering the Exception
How can I stop this request from being submitting again on a page reload, since it seems on a page reload it re-submits the duplicate entry?
views.py
def generate_student_info(request):
# Retrieve inputted value
current_student_name = request.POST.get('student_name')
current_student_id = request.POST.get('student_id')
# Read the table from the database
connection = db.open_connection(read_only=False)
sql_str = f"""SELECT * FROM {students_table};"""
df = db.read(sql_statement=sql_str, connection=connection)
# creating a formset
insert_formset = formset_factory(StudentInfoForm, extra=0)
formset = insert_formset(request.POST or None, initial=[{'student_id': df['student_id'][i], 'student_name': df['student_name'][i]} for i in range(len(df))])
context = {'formset': formset, 'db_error_message': '', 'display_error_message': False}
if request.method == 'POST':
# Insert MySQL query
if 'student_name' in request.POST:
try:
insert_query = f"""INSERT INTO {students_table} (student_id, student_name) VALUES ({current_student_id},'{current_student_name}');"""
db.write(sql_statement=insert_query, connection=connection)
return HttpResponseRedirect("/student_info/")
except Exception as e:
context['db_error_message'] = e
context['display_error_message'] = True
print(e)
return render(request, 'student_info_table.html', context)
# Update MySQL query
else:
if formset.is_valid():
for form in formset:
if form.has_changed():
print(form.cleaned_data)
try:
update_query = f"""UPDATE {students_table} SET student_name='{form.cleaned_data['student_name']}' WHERE student_id='{form.cleaned_data['student_id']}';"""
db.write(sql_statement=update_query, connection=connection)
return HttpResponseRedirect("/student_info/")
except Exception as e:
context['db_error_message'] = e
context['display_error_message'] = True
print(e)
return render(request, 'student_info_table.html', context)
else:
print('formset is not valid')
context = {'formset': formset, 'db_error_message': '', 'display_error_message': False}
return render(request, 'student_info_table.html', context)
student_info_table.html
<form action='' method='post'>
{% csrf_token %}
<div>
<input type="button" value="Insert" id="id_insert" class="generate">
</div>
</form>
{% if display_error_message %}
<h4>{{db_error_message}}</h4>
{% endif %}
<form method="POST" enctype="multipart/form-data" id="insertion-formset">
<table>...</table>
</form>
Instead of sending error message through context use django messages framework. It was built to achieve what you want. To use this you send message from views as
messages.error(request, 'Display error message.')
You can also add multiple message for the request. It is also removed when page is reloaded. Check out document here.
I know this question was asked before, but none worked for me. I have this code that I want it to be executed when a button is clicked and a message is passed
import time
from sinchsms import SinchSMS
number = '+yourmobilenumber'
message = 'I love SMS!'
client = SinchSMS(your_app_key, your_app_secret)
print("Sending '%s' to %s" % (message, number))
response = client.send_message(number, message)
message_id = response['messageId']
response = client.check_status(message_id)
while response['status'] != 'Successful':
print(response['status'])
time.sleep(1)
response = client.check_status(message_id)
print(response['status'])
Basically, what I need is to add an input in a template "HTML File", this input get passed to the message variable in the code above, same with the number. I can easily do that with instances, but how can the below get executed when a button is clicked from the form in the template?
I'm kinda newbie in Django and still finding my way
Here is the tutorial that explains how to make the python file, but execute it from the shell, not a django application.
I hope I was clear describing my problem and any help would be appreciated!
All you need is a form with a message field. In a view, you want to show that form and when the user press submit, you want to execute your script.
Here is some pseudo-code:
urls.py
url('^my-page/' my_views.my_view, name='my-page'),
forms.py
SmsForm(forms.Form):
message = fields.CharField(...)
my_views.py
def my_view(request):
form = SmsForm(data=request.POST or None)
if request.method == 'POST':
if form.is_valid():
send_sms(form.cleaned_data['message']) # do this last
messages.success(request, "Success")
return HttpResponseRedirect(request.path)
else:
messages.warning(request, "Failure")
return render(request, 'my_template.html', {'form': form})
Check the Django documentation about urls, views, forms and messages and proceed step by step:
get the page to load
get the form to load
get the form submission to work and simply show "Success" or "Failure"
finally, write the send_sms function (you've almost done it)
Lets start from the dust cloud.
What you are asking is mostly about how the web pages work. You need to know how to pass parameters using HTML. There are lots of ways to do it. But with django there is a pattern.
You need a url, and a view to catch any requests. Then you need to create a template and a form inside it. With this form you could create some requests to send data to your view.
To create you need to edit urls.py inside your project add an url:
urls.py
from django.conf.urls import url
from my_app.views import my_view
urlpatterns = [
...
url(r'^my_url$', my_view, name='my_view')
...
]
For more about urls please look at URL dispatcher page at documentation.
Then create your view inside your app which is my_app in my example. Edit my_app/views.py
my_app/views.py
from django.http import HttpResponse
def my_view(request):
return HttpResponse('IT WORKS!')
This way you get a working view which could be accessed with path /my_url. If you run ./manage.py runserver you could access your view from http://localhost:8000/my_url.
To create a form you need to create a template. By default django searches app directories for templates. Create a templates directory in your app, in our case my_app/templates and create an HTML file inside. For example my_app/templates/my_form.html. But i advice to create one more directory inside templates directory. my_app/templates/my_app/my_form.html. This will prevent template conflicts. You can check Templates page at documentation for more.
my_app/templates/my_app/my_form.html
<html>
<body>
<form action="/my_url" method="POST">
{% csrf_token %}
<input type="text" name="number">
<input type="text" name="message">
<input type="submit" value="Run My Code">
</form>
</body>
</html>
This is the one of the ways of creating your form. But I do not recommend it. I will make it prettier. But first lets "Make it work", edit your views.py:
csrf_token is a django builtin template tag, to put CSRF token into your form. By default django requires CSRF tokens at every post
request.
my_app/views.py
from django.http import HttpResponse
from django.shortcuts import render
def my_view(request):
if request.method == 'GET':
return render('my_app/my_form.html')
elif request.method == 'POST':
# get post parameters or None as default value
number = request.POST.get('number', None)
message = request.POST.get('message', None)
# check if parameters are None or not
if number is None or message is None:
return HttpResponse('Number and Message should be passed')
# your code goes here
...
return HttpResponse('Your code result')
Till this point the purpose of this answer was "Making it work". Lets convert it nice and clean. First of all we would create Form. Forms are like models, which helps you create forms as objects. It also handles form validations. Forms are saved inside forms directory generally. Create my_app/forms.py and edit it:
my_app/forms.py
from django import forms
class MyForm(forms.Form):
number = forms.CharField(max_length=15, required=True)
message = forms.CharField(max_length=160, required=True)
Put your form inside your template:
my_app/templates/my_app/my_form.html
<html>
<body>
<form action="{% url 'my_view' %}" method="POST">
{% csrf_token %}
{{ form }}
</form>
</body>
</html>
Besides the form, the action of the HTML form tag is also changed.
url template tag is used to get url form url name specified in urls.py.
Instead of url tag, {{ request.path }} could have been used.
Create a form instance and pass it to the template rendering:
my_app/views.py
from django.http import HttpResponse
from django.shortcuts import render
from .forms import MyForm
def my_view(request):
if request.method == 'GET':
form = MyForm()
return render('my_app/my_form.html', {'form': form})
elif request.method == 'POST':
form = MyForm(request.POST)
# check if for is not valid
if not form.is_valid():
# return same template with the form
# form will show errors on it.
return render('my_app/my_form.html', {'form': form})
# your code goes here
...
return HttpResponse('Your code result')
You can use class based vies to write your view, but it's not necessary. I hope it helps.
You can create a view that takes up query parameters from the url and use it for further implementation. Then you can create a link/button in the html template which can redirect you to that url. For example:
in urls.py:
url(r'^run_a/(?P<msg>\w{0,25})/(?P<num>\w{0,25})/$', yourcode, name='get_msg'),
in template:
submit
in views.py:
def get_msg(request,msg,num):
message=msg
number=num
#rest of the code
Hope this helps :)
I am just building a simple HTML form with POST method and unfortunately I am finding CSRF verification error.
This is just a simple html form using POST method on localhost. There are no cross sites involved. I could definitely fix it by using csrf_token but I still don't understand why django is asking me for that..
There are no re-directions/ iframes involved here...
So, why this is happening?? is this normal to all ??
# Also tried using RequestContext(request) but there isn't any change in the error
#settings.py
'django.middleware.csrf.CsrfViewMiddleware' in MIDDLEWARE_CLASSES
#views.py
# url for home page is "" i.e, http://127.0.0.1:8000/
def HomePage (request):
if request.method == "POST":
form = myForm(request.POST)
if form.is_valid():
data = form.cleaned_data
context = { "myForm" : myForm(choices),
"values" : data,
}
return render_to_response("home.html", context)
else:
form = myForm(choices)
context = {"myForm" : form}
return render_to_response("home.html", context)
# home.html
<div id="pingmeeForm">
<form action="" method="post">
<table>
{{myForm.as_table}}
</table>
<input name="enter" type="submit" value="enter"/>
</form>
{{values}}
</div>
# forms.py
class myForm (forms.Form):
def __init__(self, my_choices,*args, **kwargs):
super(myForm, self).__init__(*args, **kwargs)
self.fields['Friends'] = forms.ChoiceField(choices=my_choices)
message = forms.CharField()
If you do a post request, you typically change the state of the server. If you change the state of the server, you don't want to allow other sites to do so. To protect against other sites issueing post-requests on your server, you add csrf protection. Therefore the solution should (imho) never be to remove the Csrf protection. Depending on the situation, either of the following two is the case:
Your post request does not change the state. In that case, make it a get request.
Your post request changes the state. You need CSRF.
The error message you got but didn't show explains exactly what you are doing wrong: you should ensure that
the view function uses RequestContext
for the template, instead of Context.
I'm submitting a form and instead of redirecting to a success url I would like to just show "Form has been submitted" in text on the page when the form has been submitted. Does anyone know how I can do so?
In your view:
if request.POST:
# validate form, do what you need
if form_is_valid():
message = 'Form has been submitted'
return render_to_response('path/to/template.html', {'message': message})
And then use code in your template like:
{% if message %}
<h4>{{ message }}</h4>
{% endif %}
Honestly, this isn't a Django-specific issue. The problem is whether you are doing a normal form submission or using AJAX.
The basic idea is to POST to your form submission endpoint using AJAX and the form data, and in the Django view, merely update your models and return either an empty 200 response or some data (in XML, JSON, small HTML, whatever you need). Then the AJAX call can populate a success message div on success, or display a failure message if it gets back a non-200 response.
Modify your view to return an HttpResponse object with the text you want as its parameter, after you have validated the request. See the example below.
from django.http import HttpResponse
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponse('Form has been submitted.')
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/