How to reach url from form in django - django

I am new to Django and I have a simple question. Here is a view :
def watchmovie(request, idmovie):
[...]
return render(request, 'movies/watch_movie.html', locals())`
and I would like to create a simple form :
an IntegerField that would redirect to the correct url :
if I submit "42" it will redirect me to the view watchmovie with the parameter 42 as idmovie.
How can I do that?
I tried something like that
<form action="{% url "movies.views.watchmovie" %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
my url.py is
from django.conf.urls import patterns, url
urlpatterns = patterns(
'movies.views',
url(r'^movie/(?P<idmovie>\d+)$', 'watchmovie'),
)
and Django says
Reverse for 'movies.views.watchmovie' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['movies/movie/(?P<idmovie>\\d+)$']
Thank you!

The reason you are getting that error is because of a mistake in your url tag usage. Your watchmovie view url definition expects an argument to be supplied for idmovie. Since you are not supplying any argument in your url tag call, it looks only for urls which do not require an argument. Since there is none, you get an error.
But that is just a symptom. The real issue is that the way you have this structured there is no view listening for a post from your form.
The easier way to structure this is to use the same view to both display the form and to play the movie. If your view is hit with a GET request, display the form. If it is hit with a POST, validate the form (which will contain the movie id) and then respond with the page that plays the movie. That way there is no need to pass idmovie within your url.. you can remove that from your url definition and also remove the need to specify the action= attribute in your tag.. it will just post right back to where it came from.

Related

Reverse for *view* with arguments '('',)' not found

I am trying to create a simple search form(search by zip code), but am struggling to pass the user's input to a view:
<form action="{% url 'search_results' query %}" method="post">
<div>
{% csrf_token %}
<input type = 'text' name = 'query' placeholder="Zip Code" />
<button type="submit" name="button">Find Jobs</button>
</div>
</form>
urls.py:
path('search_results/<str:query>', job_views.search_results, name = 'search_results'),
views.py:
def search_results(request, query):
query = request.GET['query']
return HttpResponse(query) # just trying to see if this view has access to 'query'
I'm not sure what is going on here. This returns
raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'search_results' with a
rguments '('',)' not found. 1 pattern(s) tried: ['search_results\\/(?P<que
ry>[^/]+)$']
Thanks for any help!
In your form, at the line: action="{% url 'search_results' query %}", you are trying to pass query to the url tag, which would be fine if query had a value, but in your case query isn't defined.
Instead, you're form should look like this:
<form action="{% url 'search_results' %}" method="GET">
<div>
<input type = "text" name = "zip_code" placeholder="Zip Code" />
<button type="submit" name="button">Find Jobs</button>
</div>
</form>
Notice that I removed query from your url tag, and changed the method to GET, see this question for details.
Now change your path:
path('search_results', job_views.search_results, name = 'search_results')
You don't need to add query to the path, because Django will do it for you in your view:
def search_results(request):
zip_code = request.GET.get('zip_code')
print(zip_code)
...
That's not how templates work.
Templates are used to produce a response, which gets returned to the requester. In this case, your main view produces an HTML document which will be passed to a browser. That document contains results of the url template tag, which is determined before the user even sees it. Template tags are not a way to refer to the state of DOM objects!
Instead, you should usually have a view with a stable URL, not varying with the query. Typically you'll then extract the query text from the form data representation - here, it'd be the request.POST dictionary-like object or (usually better) a Django form that you bind to the POST data, because your form uses method="post".
It would be possible to use Javascript to edit the DOM and change your form action prior to submitting it to include the query text, but that's not the usual pattern for working with forms. And template tags will never do that - they're only used to generate the response.

Django button in html template creates error that I can't solve

my code is simple but I admit I have taken it from the internet.
I want to update a model with a button click. Which model, it is chosen by the variable. So I have this html code:
<form action="{% url 'reception:update_status' slug=name %}" method="POST">
{% csrf_token %}
<input type="submit" value="has arrived">
</form>
This code is in url.py
re_path('update_status/(?P<slug>[-a-zA-Z0-9_]+)$', views.update_status, name='update_status'),
and in views.py
def update_status(request, slug):
if request.method == 'POST':
p = MyModel.objects.filter(name=slug)
p.status = 'is waiting'
p.update()
return redirect(request, 'home')
Now with the code like this it comes back with the following error when I click on the button!
Reverse for '<WSGIRequest: POST '/update_status/Name10'>' not found. '<WSGIRequest: POST '/update_status/Name10'>' is not a valid view function or pattern name.
and I have no clue what this means. Please help?
Thanks
For regex patterns in Django 2.0, you need to use re_path, instead of path for your url.
I solved this problem with pk rather than slugs.
I would recommend everyone who is new to Django to create an id field for every Model. Because that can then be used to pass parameters very easy. Slug is more complicated and makes life unnecessarily difficult!

Call function from button in django

On one of my pages I want to display a button, whenever this button is clicked I want to display the following in my display "Button clicked".
However the following message displays in my console.""GET /account/all-plan/?print_btn=Click HTTP/1.1" 200 5025"
This is my view
def print_from_button(request):
if(request.GET.get('print_btn')):
print('Button clicked')
return HttpResponse('testklik')
html
<form method="get">
<input type="submit" class="btn" value="Click" name="print_btn">
</form>
and url in urls.py
path('all-plan/print_from_button', views.print_from_button, name='print_from_button'),
Can anyone point me into the right direction, I cannot find what I am missing. Thanks a lot!
You seem to have to URLs:
/account/all-plan/
/account/all-plan/print_from_button
In the first URL you are creating a <form> which uses the GET method, but no action attribute is specified. The result is that your form submits to the same URL as the current page (first URL). This can be seen as your console print says it's using the first URL with an extra GET parameter.
In order to get your form to use the correct URL you need to specify the action attribute with the correct URL:
<form method="get" action="{% url "app_name:print_from_button" %}">
...
</form>

Django: CSRF is incorrect or missing

I have seen a number of forums and posts but still couldn't get the handle of it. Here in django doc, it says
The CSRF middleware is activated by default in the MIDDLEWARE setting. If you override that setting, remember that 'django.middleware.csrf.CsrfViewMiddleware' should come before any view > middleware that assume that CSRF attacks have been dealt with.
If you disabled it, which is not recommended, you can use csrf_protect() on particular views you want to protect (see below).
In any template that uses a POST form, use the csrf_token tag inside the > element if the form is for an internal URL, e.g.:
form action
{% csrf_token %}
Based on that, in my html template I did simply:
<form id='frm' name='frm' method="post" action="{% url 'gettip' %}" >
{% csrf_token %}
<input type="text" name="tipid" name="tipid">
<input type="submit" value="Get Tip Value"/>
</form>
I expected the CSRF_token to create the hidden element since the middleware is already loaded. I see no element in the form and I get CSRF error.
The form is not associated with any model. I haven't used forms.py either. My current view is simply to output something:
def gettip(request):
if request.POST:
return HttpResponse('You requested a tip')
#from a weblink, i was told to add the following but it made no difference
context = {}
return render_to_response('tip.html',context, context_instance=RequestContext(request))
The error I am getting is obviously CSRF missing cos the hidden element is not there at all.
I am migrating from PHP and this is giving me a hard time. Though my form is not for login purposes, I couldn't get this one to work either for the same error. I am on django 1.10 and just want to get a positive response when form is submitted.
Don't use render_to_response, it's obsolete. Use render instead.
from django.shortcuts import render
def gettip(request):
if request.POST:
return HttpResponse('You requested a tip')
context = {}
return render(request, 'tip.html', context)
If the template containing the form is rendered by another view, you'll have to fix that view as well.

Dynamic redirect url after login [duplicate]

I'm trying to build a simple website with login functionality very similar to the one here on SO.
The user should be able to browse the site as an anonymous user and there will be a login link on every page. When clicking on the login link the user will be taken to the login form. After a successful login the user should be taken back to the page from where he clicked the login link in the first place.
I'm guessing that I have to somehow pass the url of the current page to the view that handles the login form but I can't really get it to work.
EDIT:
I figured it out. I linked to the login form by passing the current page as a GET parameter and then used 'next' to redirect to that page. Thanks!
EDIT 2:
My explanation did not seem to be clear so as requested here is my code:
Lets say we are on a page foo.html and we are not logged in. Now we would like to have a link on foo.html that links to login.html. There we can login and are then redirected back to foo.html.
The link on foo.html looks like this:
<a href='/login/?next={{ request.path }}'>Login</a>
Now I wrote a custom login view that looks somewhat like this:
def login_view(request):
redirect_to = request.REQUEST.get('next', '')
if request.method=='POST':
#create login form...
if valid login credentials have been entered:
return HttpResponseRedirect(redirect_to)
#...
return render_to_response('login.html', locals())
And the important line in login.html:
<form method="post" action="./?next={{ redirect_to }}">
So yeah thats pretty much it, hope that makes it clear.
You do not need to make an extra view for this, the functionality is already built in.
First each page with a login link needs to know the current path, and the easiest way is to add the request context preprosessor to settings.py (the 4 first are default), then the request object will be available in each request:
settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.request",
)
Then add in the template you want the Login link:
base.html:
Login
This will add a GET argument to the login page that points back to the current page.
The login template can then be as simple as this:
registration/login.html:
{% block content %}
<form method="post" action="">
{{form.as_p}}
<input type="submit" value="Login">
</form>
{% endblock %}
To support full urls with param/values you'd need:
?next={{ request.get_full_path|urlencode }}
instead of just:
?next={{ request.path }}
This may not be a "best practice", but I've successfully used this before:
return HttpResponseRedirect(request.META.get('HTTP_REFERER','/'))
Django's built-in authentication works the way you want.
Their login pages include a next query string which is the page to return to after login.
Look at http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.decorators.login_required
I linked to the login form by passing the current page as a GET parameter and then used 'next' to redirect to that page. Thanks!
I encountered the same problem. This solution allows me to keep using the generic login view:
urlpatterns += patterns('django.views.generic.simple',
(r'^accounts/profile/$', 'redirect_to', {'url': 'generic_account_url'}),
)
In registration/login.html (nested within templates folder) if you insert the following line, the page will render like Django's original admin login page:
{% include "admin/login.html" %}
Note: The file should contain above lines only.
See django docs for views.login(), you supply a 'next' value (as a hidden field) on the input form to redirect to after a successful login.
You can also do this
<input type="hidden" name="text" value="{% url 'dashboard' %}" />