Implementing Search Form with Django - python

Hello Stackoverflow community,
I am having trouble with my form not rendering in Django.
Here's my attempt to render an empty form in views.py.
class SearchSite(forms.Form):
query = forms.CharField(label="New Item",
help_text="Search for any article located on the site.")
def search(request):
form = SearchSite()
context = {
"form": form,
"query_matches": query_matches
}
response = render(request, "encyclopedia/layout.html", context)
return response
Here's what my urls.py file looks like:
urlpatterns = [
path("", views.index, name="index"),
path("wiki/<str:page_title>", views.page, name="wiki"),
path("wiki/", views.search, name="site_search")
]
My layout.html file:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link href="{% static 'encyclopedia/styles.css' %}" rel="stylesheet">
</head>
<body>
<div class="row">
<div class="sidebar col-lg-2 col-md-3">
<h2>Wiki</h2>
<form action="{% url 'site_search' %}" method="get">
{% csrf_token %}
There should be something here
{{ form }}
<input type="submit">
</form>
<div>
Home
</div>
<div>
Create New Page
</div>
<div>
Random Page
</div>
{% block nav %}
{% endblock %}
</div>
<div class="main col-lg-10 col-md-9">
{% block body %}
{% endblock %}
</div>
</div>
</body>
</html>
I have noticed two particular problems in above screenshot. Firstly, my form does not render when inside my index.html webpage, which extends layout.html. Secondly, when I click the submit button, I get routed to a webpage that has my CSRF token in the url ... and then finally renders my form.
How can I fix this? Thanks everyone.

I have noticed two particular problems in above screenshot. Firstly,
my form does not render when inside my index.html webpage, which
extends layout.html.
Yes. You aren't passing form to index.html. Pass that in the view which renders the homepage. Even if it extends from layout.html, you need to pass it in the context for it to work.
def index(request):
# Your code.
return render(request, 'index.html', {'form': SearchSite()})
Secondly, when I click the submit button, I get routed to a webpage
that has my CSRF token in the url ... and then finally renders my
form.
That's because, in index.html, there is a blank form with a csrf_token, with an action set to /wiki, which calls search when the submit button is pressed. And search gives you layout.html, with the form, and as the form method is GET, it shows it in the url. I suggest changing it to POST if there is confidential data (and even otherwise. Why is there a csrf_token if it is not a POST request? Not needed. If you really want a GET request, then remove the csrf_token).

Here's my solution to the problem I had earlier for any future people visiting the post.
I wrote a form called SearchSite and defined a view called search in my views.py.
class SearchSite(forms.Form):
query = forms.CharField(
help_text="Search for any article located on the site.")
def search(request):
form = SearchSite()
is_substring_of_queries = []
if request.method == "GET":
form = SearchSite(request.GET)
if form.is_valid():
for entry in util.list_entries():
existsIdenticalResult = form.cleaned_data["query"].casefold() == entry.casefold()
existsResult = form.cleaned_data["query"].casefold() in entry.casefold()
if existsIdenticalResult:
return HttpResponseRedirect(reverse("wiki",
kwargs={"page_title": entry}))
elif existsResult:
is_substring_of_queries.append(entry)
context = {
"form": SearchSite(),
"is_substring_of_queries": is_substring_of_queries
}
response = render(request, "encyclopedia/search.html", context)
return response
When my view.search is requested, it will send the response of either an empty form (if accessed by index.html or if there are no results with a message saying there are no results) , an empty form and all the queries that are substrings of the markdown entries or route the client to an exact entry if the query matched.
Here's the routing down in my urls.py so far:
urlpatterns = [
path("", views.index, name="index"),
path("wiki/<str:page_title>", views.page, name="wiki"),
path("search/", views.search, name="site_search")
]
In my layout.html, I have the following form:
<form action="{% url 'site_search' %}" method="get">
{{ form }}
<input type="submit">
</form>
as well as in my search.html the queries that are substrings of the markdown entries:
{% if is_substring_of_queries %}
<h1>Search Results</h1>
{% for query in is_substring_of_queries%}
<li> {{ query }} </li>
{% endfor %}
{% else %}
<h1>No Results! Try again.</h1>
{% endif %}
If there are any mistakes, please let me know.

Related

NoReverseMatch at /search/ Reverse for 'entry' not found. 'entry' is not a valid view function or pattern name

NoReverseMatch at /search/
Reverse for 'entry' not found. 'entry' is not a valid view function or pattern name.
I´m trying to make an search engine for my website in django, via views.py, but Django always says that there's an exception in views.py
Views.py
def index(request):
return render(request, "encyclopedia/index.html", {
"entries": util.list_entries()
})
def entries(request, entry):
if entry not in util.list_entries():
raise Http404
content = util.get_entry(entry)
return render(request,"encyclopedia/entry.html",
{"title": entry, "content": Markdown().convert(content)},
)
def search(request):
query = request.GET.get("q", "")
if query is None or query == "":
return render(
request,
"encyclopedia/search.html",
{"found_entries": "", "query": query},
)
entries = util.list_entries()
found_entries = [
valid_entry
for valid_entry in entries
if query.lower() in valid_entry.lower()
]
if len(found_entries) == 1:
return redirect("entry", found_entries[0])
return render(
request,
"encyclopedia/search.html",
{"found_entries": found_entries, "query": query},
)
But Django says: "if len(found_entries) == 1:
return redirect("entry", found_entries[0])
have an "NoReverseMatch" error"
Urls.py
from django.urls import path
from . import views, util
urlpatterns = [
path("", views.index, name="index"),
path("entries/<str:entry>", views.entries, name="entries/{{ entry }}"),
path("search/", views.search, name="search"),
]
handler404 = 'encyclopedia.views.error_404_view'
Layout.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<body>
<div class="row">
<div class="sidebar col-lg-2 col-md-3">
<h2>Wiki</h2>
<form action="{% url 'search' %}">
<input class="search" type="text" name="q" placeholder="Search
Encyclopedia">
</form>
<div>
Home
</div>
<div>
Create New Page
</div>
<div>
Random Page
</div>
{% block nav %}
{% endblock %}
</div>
<div class="main col-lg-10 col-md-9">
{% block body %}
{% endblock %}
</div>
</div>
</body>
</html>
I've been trying a lot of things, but nothing fix it, how I can let my page run well?
You must change your url name in urls.py to entry:
path("entries/<str:entry>", views.entries, name="entry"),
And you should pass the argument in your views.py file as:
return redirect("entry", entry=found_entries[0])
See https://docs.djangoproject.com/en/4.0/topics/http/shortcuts/#redirect

Django search page

I'm trying to make a website that lets visitors search for books using another search engine. I have a script that takes a query, and returns some HTML with the results of the search, but I'm struggling to make a front end for this. I am using django because it seemed like the best option when I started, but now I am going in circles and I can't figure out how to make this thing - I'm just getting overwhelmed because the different tutorials and documentation that I'm reading all go into the advanced stuff before I can get the basic thing working.
Do I need separate search and results templates? Right now I'm getting the error The view book_search.views.search didn't return an HttpResponse object. It returned None instead.
How can I fix this error and/or design this whole thing better?
Here's what I have so far (the script that returns the results in html is pull.py):
The views and urls are from inside the book_search app.
views.py:
from django.shortcuts import render
from django.http import HttpResponse
from . import pull
from .forms import SearchForm
def index(request):
return HttpResponse("Welcome to the index page")
def test_search(request):
context = {'query': 'test query'}
return render(request, 'book_search/search.html', context)
def search(request):
if request.method == "GET":
form = SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['query']
results = pull.main(query)
context = {'query': query, 'form': form, 'results': results}
return render(request, 'book_search/results.html', context)
apps.py:
from django.apps import AppConfig
class BookSearchConfig(AppConfig):
name = 'book_search'
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('index', views.index, name='index'),
path('test', views.test_search, name='test_search'),
path('', views.search, name='search'),
]
forms.py:
class SearchForm(forms.Form):
query = forms.CharField(label='Search', max_length=200)
template base.html:
<html>
<head>
</head>
<body>
<form method="GET" action="/search/">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
{% block content %}{% endblock %}
</body>
</html>
template results.html:
{% block content %}
{% results %}
{% endblock content %}
Since we guessed that form isn't valid (because no POST handler - you do not send anything to the form) and wrong indentation gives None response, now you can fix reference before assignment:
def search(request):
if request.method == "GET":
form = SearchForm()
context = {'form': form}
elif request.method == "POST":
form = SearchForm(request.POST)
if form.is_valid():
query = form.cleaned_data['query']
results = pull.main(query)
context = {'query': query, 'form': form, 'results': results}
return render(request, 'book_search/results.html', context)
and render errors in results.html template by putting this:
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}

How to construct a condition to choose appropriate function in template

I have two apps and want them to run one function in view.py and render in template. The only difference is that each app uses different models. For example, my first app uses function called like_post_fin:
post = get_object_or_404(Post_Fin, id=request.POST.get('id'))
and my second app uses function called like_post:
post = get_object_or_404(Post, id=request.POST.get('id'))
other part of code is same for each function.
In my first app template I will use to call function:
<form action="{% url 'like_post_fin' %}" method="post">
In my second app template I will use:
<form action="{% url 'like_post' %}" method="post">
other part of html code is same for both template.
When I run codes separately everything works perfect. But I want to have just one function and template for both apps which should use condition in view function or template.
I tried to use condition in app's template by using following code
{% if title == 'my app title' %}
<form action="{% url 'like_post_fin' %}" method="post">
{% else %}
<form action="{% url 'like_post' %}" method="post">
{% endif %}
But it did not work. Template titles are given using following code:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'my app title'
In my base html
{% if title %}
<title>web - {{ title }}</title>
{% else %}
<title> web </title>
{% endif %}
So, how to make condition in template or view file function

django template form shows the result in another html file

I want my search result to be displayed in another html file. I have my template as follows:
<html>
<head></head>
<body>
<div id="div1">
{% for i in var1 %}
{{i.name}}<br>
{% endfor %}
</div>
<form action="searchresult.html" method="post">{% csrf_token %}
<input type="text" name="search1">
<input type="submit" id="click1" value="search">
</form>
</body>
</html>
views.py
def search(request):
var = ""
if request.method=='POST':
s1 = request.POST.get("search1")
var = form1.objects.all().filter(keyskills=s1)
return render(request,"search.html",{'var1':var})
models.py
class form1(models.Model):
name = models.CharField(max_length=20)
qualification = models.CharField(max_length=20)
keyskills = models.CharField(max_length=50)
def __unicode__(self):
return self.name,self.qualification,self.keyskills
My result shows search details in the same page. instead I want it to be displayed in searchresult.html. I have written the searchresult.html in action of form tag. Please give me your suggestion.
Change the action attribute of your form to the URL that is mapped to the search method.
In your search method; return the searchresult.html template: return render(request, 'searchresult.html', {'var1': var1 }).
In the searchresult.html template, add:
{% for i in var1 %}
{{i.name}}<br>
{% endfor %}
It looks like form Action repesents the Html file, rather than django method to be invoked.
What you do fundamentally is this:
1) You have a form with POST operation, and while submitting, the method that you mentioned in the 'action' tag will get invoked.
2) Once you process with POST datas, then redirect to searchresult.html.
Hope this helps.
Change your action to
<form action="{% url your_project_name.your_app_name.views.search %}" method="post">
It would help if you posted your urls.py content.

Django : CSRF verification failed even after adding {% csrf_token %}

views.py:
def index(request):
return render_to_response('index.html', {})
def photos(request, artist):
if not artist:
return render_to_response('photos.html', {'error' : 'no artist supplied'})
photos = get_photos_for_artist(artist)
if not photos:
logging.error('Issue while getting photos for artist')
return render_to_response('photos.html', {'error': 'no matching artist found'})
return render_to_response('photos.html', {'photos': photos})
Index.html:
<html>
<head>
<title>find artist photos </title>
</head>
<body>
{% block error %} {% endblock %}
<form action="/photos" method="POST">
{% csrf_token %}
<label for="artist">Artist : </label>
<input type="text" name="artist">
<input type="submit" value="Search">
</form>
{% block content %}{% endblock %}
</body>
</html>
photos.html:
{% extends 'index.html' %}
{% block error %}
{% if error %}
<p> {{ error}} </p>
{% endif %}
{% endblock %}
{% block content %}
{% if photos %}
{% for photo in photos %}
{{ photo }}
{% endfor %}
{% endif %}
{% endblock%}
url.py:
urlpatterns = patterns('',
(r'', index),
(r'^time/$', current_datetime),
(r'^photos/(\w+)$', photos)
)
I even tried by adding {% csrf_token %}, but no luck
Thank you
UPDATE
I see these in the logs
UserWarning: A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.
warnings.warn("A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.")
This came after adding context_instance=RequestContext(request) **to render_to_response()**
add context_instance=RequestContext(request) to every view that you will use a form inside it:
return render_to_response('index.html', {}, context_instance=RequestContext(request) )
return render_to_response('photos.html', {'photos': photos}, context_instance=RequestContext(request) )
Supposing you are using a fairly recent version of Django (1.3/1.4/dev) you should follow these steps :
In settings.py, Add the middleware django.middleware.csrf.CsrfViewMiddleware to the
MIDDLEWARE_CLASSES list.
In your template, use the {% crsf_token %} in the form.
In your view, ensure that the django.core.context_processors.csrf context processor is used either by :
use RequestContext from django.template
directly import the csrf processor from from django.core.context_processors
Examples
from django.template import RequestContext
from django.shortcuts import render_to_response
def my_view(request):
return render_to_response('my_template.html', {}, context_instance=RequestContext(request))
or
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
def my_view(request):
c = {csrf(request)}
return render_to_response('my_template.html', c)
References
csrf in Django 1.3 or csrf in Django 1.4
RequestContext in Django 1.3 or RequestContext in Django 1.4
(exhaustive post for posterity and future viewers)
A number of things to troubleshoot here:
Please load your "index" page in a web browser, do "View Source", and check if the {% csrf_token %} is being expanded. It should be replaced with an <input> tag. If that's not happening, then you have problems with your index page. If it is being replaced correctly, then you have problems with your photos page.
The POST URL in index.html doesn't match any of the patterns in urls.py. Your urls.py seems to expect the search term to be part of the URL, but it's not - you're sending it as a HTTP POST parameter. You need to access it via request.POST.
Check in the settings, if you have this middleware:
'django.middleware.csrf.CsrfViewMiddleware'
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
You may need to explicitly pass in a RequestContext instance when you use render_to_response in order to get the CSRF values for that template tag.
http://lincolnloop.com/blog/2008/may/10/getting-requestcontext-your-templates/
Try using the #csrf_protect decorator:
from django.views.decorators.csrf import csrf_protect
from django.shortcuts import render_to_response
#csrf_protect
def photos(request,artist):
if not artist:
return render_to_response('photos.html', {'error' : 'no artist supplied'})
photos = get_photos_for_artist(artist)
if not photos:
logging.error('Issue while getting photos for artist')
return render_to_response('photos.html', {'error': 'no matching artist found'})
return render_to_response('photos.html', {'photos': photos})
This worked for me:
{% csrf_token %}
In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
In views.py:
from django.template import RequestContext
...
...
...
return render_to_response("home.html", {}, context_instance=RequestContext(request))

Categories