I was searching for this answer but none met my expectation. So, In my template I have some content and wanted to add button (which later will add to favorites). After clicking I want to call method from my views.py and redirect to other view.
my views.py
def home(request):
//logic here
request.session['url'] = url
return render(request,'file.html')
def function_to_call(request):
///logic here
url = request.session.get('url')
return render(request,'second_file.html',url=url)
file.html
<form action="{% url 'function_to_call' %}">
<button id="submit" type="button" value="Click" />
</form>
and in my urls.py
url(r'^function_to_call/',views.function_to_call,name='function_to_call'),
Unfortunately, after clicking on button, nothing happens
unless you are submitting a form, you should use
Click
If for some reason you need to use a POST request rather than a GET this will work:
<form method="POST" action="{% url 'function_to_call' %}">
<button id="submit" type="submit" value="Click" />
</form>
Using a post can be helpful when you don't want to include data in the querystring because it's a little less secure than having the parameters in the request's body.
Related
I am currently using HTMX and Django to process button clicks within a table that adds the selected item to a list. I am trying to use the name/value HTML attributes to send to the backend with the value being dynamic based on the database information. I have the following form code:
<form action="" method="post">
{% csrf_token %}
<button hx-post="{% url 'add-analysis' %}" hx-target="#analysis-list" type="submit" name="projectChoice" value="{{project.project_title}}">Add</button>
</form>
in my Views.py I am trying to parse the data with the following code:
def add_analysis(request):
proj_name = request.POST.get("projectChoice")
print(list(request.POST.items()))
print(request.data())
return render(request, 'includes/analysis-list.html', {"selected_projects" : proj_name})
This returns None however. To debug this I tried listing all of the POST requests to the server with the following:
print(list(request.POST.items()))
However this only returns the CSRF token, what am I doing wrong here?
htmx sends the button value with the posted data when the request attribute hx-post is placed on the form itself.
<form hx-post="/form" hx-target="#result">
<button name="submit1" value="foo" type="submit">Submit 1 (foo)</button>
<button name="submit2" value="bar" type="submit">Submit 2 (bar)</button>
</form>
Here's a live example https://codepen.io/jreviews/pen/PoEJYMX
In your case you can try to do something different on the server side depending on the button that was used to submit the form.
I found several similar answers, but I could not solve the problem (maybe I searched wrong) anyway.
I'm trying to do the following:
Filter a result by getting the value of "q" where is the input containing the value to search
Add the "q" value in the url, like "/search?q=test"
Copy the url above and from there get the filtered result
My problem so far is:
The value of "q" is only added in the url after another search, ie the url contains "q" with the previous search value, which is bad
I can not access the url and have the results filtered, since when I enter this url, a new request object with no value for "q" is created, so I get a ValueError
So far what I've done is:
template
<form method="POST" action="{% url 'blog:post_search' %}?q={{ query }}" class="form-inline">
<div class="md-form my-0">
<input name="q" class="form-control form-control-sm mr-3 w-75" type="text" placeholder="Pesquisar" aria-label="Search">
</div>
<button class="btn btn-outline-white btn-sm my-0" type="submit">GO</button>
{% csrf_token %}
</form>
urls.py
urlpatterns = [
# ....
path('search', views.post_search, name='post_search'),
]
views.py
def post_search(request):
template = "blog/post_search.html"
query = request.POST.get('q')
if query:
posts = Post.objects.filter(title__icontains=query)
else:
posts = Post.objects.all()
context = {'posts': posts, 'query': query}
return render(request, template, context)
You are confusing POST and GET methods here. Query parameters in the url are typically only used with get requests.
So in your form, you can change this:
<form method="POST" action="{% url 'blog:post_search' %}?q={{ query }}" class="form-inline">
to
<form action="{% url 'blog:post_search' %}" class="form-inline">
The default method for a html form is GET, and named inputs will automatically be included in the action url by the browser when you submit the form. So you should not have a querystring in the from action attribute action='/search' is enough.
In the view you access url parameters from request.GET instead of request.POST.
def search(request):
query = request.GET.get('q')
I am using a flask framework, and can't seem to delete rows from the database. The code below gives a 405 error: "The method is not allowed for the requested URL." Any ideas?
In the py:
#app.route('/delete/<postID>', methods=['POST'])
def delete_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('delete from entries WHERE id = ?', [postID])
flash('Entry was deleted')
return redirect(url_for('show_entries', post=post))
In the html:
<h3>delete</h3>
Clicking <a href...>delete</a> will issue a GET request, and your delete_entry method only responds to POST.
You need to either 1. replace the link with a form & submit button or 2. have the link submit a hidden form with JavaScript.
Here's how to do 1:
<form action="/delete/{{ entry.id }}" method="post">
<input type="submit" value="Delete />
</form>
Here's how to do 2 (with jQuery):
$(document).ready(function() {
$("a.delete").click(function() {
var form = $('<form action="/delete/' + this.dataset.id + '" method="post"></form>');
form.submit();
});
});
...
Delete
One thing you should not do is make your delete_entry method respond to GET. GETs are meant to be idempotent (are safe to run repeatedly and don't perform destructive actions). Here's a question with some more details.
Alternatively, change POST to DELETE to get you going.
#app.route('/delete/<postID>', methods=['DELETE'])
Ideally, you should use HTTP DELETE method.
I used flaskr as a base for my Flask project (as it looks like you did as well).
In the .py:
#app.route('/delete', methods=['POST'])
def delete_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('delete from entries where id = ?', [request.form['entry_id']])
g.db.commit()
flash('Entry deleted')
return redirect(url_for('show_entries'))
In the HTML:
<form action="{{ url_for('delete_entry') }}" method=post class=delete-entry>
<input type="hidden" name="entry_id" value="{{ entry.id }}">
<input type="submit" value="Delete" />
</form>
I wanted a button, but you could easily use a link with the solution here.
A simple <a href= link in HTML submits a GET request, but your route allows only PUT requests.
<a> does not support PUT requests.
You have to submit the request with a form and/or with JavaScript code.
(See Make a link use POST instead of GET.)
I am a novice Django user trying to create a push button, which links to another page in my site when clicked. I've tried a few different examples which I've found, but none seem to be working for me...
As an example, why is this not working?
<form method="link" action="{% url 'gui' %}">
<input type="button" value="Start">
</form>
Here, 'gui' is the name I have given to my link in urls.py to the desired page.
I am also fairly new to HTML in general, so it may be my HTML that is wrong...
Thank you.
Is there any particular reason you're using both a form and a button?
Can you use an a (anchor tag)?
Click here
If you want to use a form, please post your urls.py.
To create a button that can be clicked and redirect to another link, you can wrap <button></button> around <a></a>, which worked for me:
<button type="button">
Click here!
</button>
<form method="post" action="{% url 'gui' %}">
<button type='submit'>Start</button>
</form>
In your views:
def function(request):
if request.method == 'POST'
# Do something
I added django.contrib.auth.views.login everywhere in my webpage, for that I had to load a templatetag (that returns the AuthenticationForm) in my base.html. This templatetags includes the registration/login.html template.
The login is working ok but I want it to redirect the users to the same page they are before login. Now, it redirects me to /wherever_i_am/login wich shows registration/login.html with the 'login ok' or 'login fails' messages but without the rest of base.html.
I have followed django documentation and a few SO questions like this but I cannot redirect correctly. I have modified the next variable but it doesn't seem to work (next={{ request.get_full_path }} redirects me to /wherever_i_am/login ...again)
Have you tried something similar? any ideas?
UPDATE1
Now, the question could be something like: Do I have to declare my own login view if I want to include the login form everywhere in my web page?
Thank you.
Found answer:
Change settings.LOGIN_REDIRECT_URL in your settings.py,
below code is copy from django:
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = settings.LOGIN_REDIRECT_URL
...
The below allows redirects the user to the page they were attempting to access after they log in, but without having to write a custom view. It contains all the code you need to add to make it work. (As an aside, not all the TEMPLATE_CONTEXT_PROCESSORS are needed, but if you set a value to it explicitly you overwrite the defaults so need to re-add them.)
settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.request",
"django.core.context_processors.static",
)
urls.py
from django.contrib.auth.views import login, logout
...the other imports for your app ...
urlpatterns = patterns('',
(r'^login/$', login, {'template_name':'login.html'} ),
(r'^logout/$', logout,{'template_name':'logout.html'}),
...the other urls for your app...
)
login.html
<html>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{form}}<br/>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</html>
logout.html
<html>
<p>You are logged out. To log in again, click here.</p>
</html>
views.py
#login_required(login_url="/login/")
def view1(request):
....this is a view you want to protect with security...
#login_required(login_url="/login/")
def view1(request):
....this is a view you want to protect with security...
I used something like this with default login view:
{% if form.errors %}
<p class="error">Sorry, that's not a valid username or password</p>
{% endif %}
<form action="{% url login %}" method="post">
{% csrf_token%}
<label for="username">User name:</label>
<input type="text" name="username" value="" id="username">
<label for="password">Password:</label>
<input type="password" name="password" value="" id="password">
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
</form>
# or if it's not declareв шт urls:
<form action="{% url django.contrib.auth.views.login %}?next={{ request.get_full_path }}" method="post">
everything worked fine.
PS: are you absolutely sure that "context_processors.request" is included in settings? Forgetting to include it is a common problem.
UPD: As far as I know, there are no way to make default login view to redirect on failed login (It just doesn't work that way).
Still i may be wrong
Finally I created a login view that calls django.contrib.auth.views.login internally.
I'd suggest to pass a previous url as a parameter within the url:
/accounts/login/?next=my_previous_url
and then use this value in a view
request.next
{{request.get_full_path}} gives you the current path, so is normal that the redirect points to the same place, change it for {{next}} in your registration/login.html template
Adding up to #Sean's anwer. Code for iterating over each form field in order to write field error above the miss-typed field.
So, in Sean's login.html is the existing code:
login.html
<html>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{form}}<br/> <!-- I can change! -->
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</html>
Now what you should do is replace the "I can change!" line (4th line in the above code snippet) with following code:
{% for field in form %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<span class="text-danger small"> {{ field.errors }}</span>
</div>
<label class="control-label col-sm-2">{{ field.label_tag }}</label>
<div class="col-sm-10"> {{ field }}</div>
</div>
{% endfor %}
You can use this snippet for other forms too (for example registration). :)
I stumble upon this question in my process of implementing Facebook Account Linking. The problem is the same: how do I correctly redirect django after successful login?
Remember this: your settings.py contain LOGIN_REDIRECT_URL right? So, that's the only place where you should do the logic of redirecting. To do that, first connect this signal (put this in your views.py):
def after_success_login(sender, user, request, **kwargs):
alt = request.GET.get('account_linking_token')
if alt is not None:
uri = request.GET.get('redirect_uri')
request.session['fb_redirect_uri'] = uri
user_logged_in.connect(after_success_login)
The logic above may not reflect your case, but the idea is setting up a session variable to be read in the route defined as LOGIN_REDIRECT_URL.
So, in my case:
def index(request):
if not request.user.is_authenticated():
form = SignUpForm()
return render(request, 'index.html', {'form': form})
else:
# FB ACCOUNT LINKING!
if 'fb_redirect_uri' in request.session:
redirect_uri = request.session['fb_redirect_uri']
del request.session['fb_redirect_uri']
to = '{}&authorization_code={}'.format(redirect_uri, request.user.username)
print('to', to)
return redirect(to)
That's it!
Add a decorator before the view function should be OK.
#login_required
see here for details