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.
Related
Summary: I am trying to build a job site. On index.html the user enters a zip code into a form to see jobs in that zip code, this form is handled with the job_query view. This brings them to another page(search.html) where at first you only see jobs in that specific zip code but I am trying to add a filter that lets the user see jobs within X miles. How can I pass the zip code value entered in the from on index.html to the next page?
index.html:
<h2>Find a Job</h2>
<!--Search Bar-->
<form method = "GET" action = "{% url 'search' %}" >
<div id = "form_grid">
<input name="query" type="text" placeholder="Zip Code">
<button type="submit">Search</button>
</div>
</form>
search.html:
<form method = "GET" action = "{% url 'search' %}" >
<input class="search_bar" name="query" type="text" placeholder="Zip Code">
<button class="search_btn btn btn-outline-success " type="submit">Find Jobs</button>
</form>
<form id="within_miles_form" method = "GET" action = "{% url 'within_miles' %}" >
<input class="search_bar" name="miles" type="text" placeholder="Within X miles of Zip Code">
<button type="submit">Filter</button>
</form>
<!--code to display jobs-->
views.py:
def job_query(request):
if request.method == "GET":
query = request.GET.get('query')
jobs_matching_query = Job.objects.filter(zip_code__iexact = query) | Job.objects.filter(city__iexact=query) | Job.objects.filter(state__iexact=query)
number_of_results = 0
for job in jobs_matching_query:
number_of_results = number_of_results + 1
return render(request, 'core/search.html', {'query': query ,'jobs_matching_query': jobs_matching_query, 'number_of_results': number_of_results})
def within_miles(request):
miles = request.GET['miles']
#how can i use value of the zip code entered?
urls.py:
url(r'^search$', views.job_query, name="search"),
url(r'within_miles', views.within_miles, name="within_miles"),
I think I included all the relevant info but if I am missing something please let me know, thanks in advance for any help.
You can encode the entered ZIP in a URL, pass it through cookies, store it in the session variables, or use a (hidden) input element that forces the browser to pass it through a GET and POST request.
Encode it in the URL
In that case we can rewrite the URL to:
url(r'^within_miles/(?P<zip>[0-9]{5})/$', views.within_miles, name="within_miles"),
So now one can no longer fetch your.domain.com/within_miles, but your.domain.com/within_miles/12345. It makes it easy for a user to "manipulate" the URL, but since the user can probably provide any ZIP, there is probably not much gain to protect that.
In the form, the URL that is generated is thus:
{% url 'within_miles' zip=query %}
(you can use another variable that is more strictly a ZIP code)
You should thus ensure that query is here a five digit string (or otherwise change the expression in the url(..) part such that it allows all possible queries).
Using hidden form elements
We can also encode content in hidden form elements, for example here we can create an element in the form:
<form id="within_miles_form" method = "GET" action = "{% url 'within_miles' %}" >
<input class="search_bar" name="miles" type="text" placeholder="Within X miles of Zip Code">
<input type="hidden" name="zip_code" value="{{ query }}">
<button type="submit">Filter</button>
</form>
We thus add a form element, fill it with some data, and let the browser submit the value again to the next view. Note that again it is the browser that does this, so a user can inspect the DOM (most browsers allow that, and subsequently edit it).
Using session variables and/or cookies
You can also decide to use session variables (stored at server side, so "secure") or cookies (stored at client side, can be tampered with). A potential problem however is that these are stored in the browser, and changes to the cookies in one tab page, thus can have effect in the other tab page. Furthermore cookies and sessions will "die" after the request, and thus can create a lot of trouble in future views.
You can set a session variable in the view with:
request.session['zip_code'] = query
This will thus store an entry at the server side such that another call can retrieve that value again. The request.session acts like a dictionary that keeps some sort of state per session.
setting and obtaining session variables
In another view, you can thus query the request.session, like:
zip_code = request.session.get('zip_code')
setting and obtaining cookies
We can use a similar approach with cookies. A browser however might reject cookies, or manipulate them, so there are not that much guarantees that there is no tampering with the data (in fact there are none). You can set a cookie with:
response = render(request, 'core/search.html', {'query': query ,'jobs_matching_query': jobs_matching_query, 'number_of_results': number_of_results})
response.set_cookie('zip_code', query)
return response
Before we thus return the result of render(..), we call .set_cookie(..) on the result.
We can - for example in a later view - retrieve the content with:
zip_code = request.COOKIES.get('zip_code')
Improving the job_query view
The job_query view however looks a bit strange: it uses all kinds of "uncommon" code practices. For example the number of elements is calculated by iterating over it, instead of taking the len(..). This also looks basically like a ListView [Django-doc] and we can make the query more elengant by using Q-objects [Django-doc]. The listview then looks like:
def JobListView(ListView):
model = Job
context_object_name = 'jobs_matching_query'
template_name = 'core/search.html'
def get_context_data(self, **kwargs):
kwargs = super(JobListView, self).get_context_data(**kwargs)
kwargs.update(
number_of_results=len(kwargs['object_list'],
query = self.request.GET.get('query')
)
return kwargs
In the view, you then not pass the JobListView, but JobListView.as_view() result as a reference.
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!
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.
In Django1.6, is there a way to pass a dynamic parameter to my view or URL without parsing the URL?
Ideally I would want a urls.py that looks like:
url(r'^dash/$',
dash_view.account_modify,
{'account': **dynamic_account_identifier_here**}
name='dash_account_modiy')
And in views.py:
def account_modify(request, account,
template_name='profile.html,
change_form=AccountModifyForm):
...
:param account:
comes from model:
class Dash(models.Model):
name = models.Charfield()
account = models.IntegerField()
....
Basically, I would really like to avoid a urls.py with the account identifier as part of the string, like:
url(r'^dash/(?P<account>\w+)/$',
dash_view.account_modify,
name='dash_account_modiy')
Any suggestions on how I can pass these values from the a template to the processing view for use in the AccountModifyForm (which expects that 'account' parameter)?
url(r'^dash/$',
dash_view.account_modify,
{'account': **dynamic_account_identifier_here**}
name='dash_account_modify')
You can't dynamically evaluate anything there, because the dictionary is evaluated only once, when the URL conf is loaded.
If you want to pass information from one view to another your three options are:
in the URL, which you don't seem to want to do
as GET or POST data
store it in the session in one view and retrieve it from the session in the next
If anyone cares...figured it out...
In the template:
{% for dash in dashes %}
blah blah blah
<form action="..." method="POST">
<input type="hidden" name="id" value="{{ dash.account }}">
{{ form.as_ul }}
<input type="submit" value="Do stuff">
</form>
{% endfor %}
In the views:
if request.method == 'POST'
account = request.POST['id']
# be sure to include checks for the validity of the POST information
# e.g. confirm that the account does indeed belong to whats-his-face
form = AccountModifyForm(request.POST, account,
user=request.user)
....
I have a Django view which corresponds to a URL like this:
<my_site>/project_info/?pitched=true
I validate the form for this page the normal way:
form = ProjectInfoForm(request.POST or None)
if form.is_valid():
# handle form
...
But when the form is returned with errors it ends up at the same page without the URL (obviously):
<my_site>/project_info/
I don't really want to changed the pitched=true parameter to a captured URL parameter. What is the best way to redirect, considering I can't know if the page had this GET variable or not?
What does the HTML in your template look like? Try setting the action attribute on your form to "" (it should preserve your variables), like this:
<form action="" method="post">
The action="" trick works. If you want to be sure, you can do something like
<form action="{% if request.GET.pitched %}?pitched={{ request.GET.pitched }}{% endif %}" method="post">