Django Search function - python

How do I provide a search bar in django? My code is as follows...
home.html
<form method='GET' action="">
<input type="text" name="search" placeholder="Search posts"/>
<input type="submit" value="Search"/>
</form>
views.py
def home(request):
posts = Post.objects.all()
search_term = ''
if 'search' in request.GET:
search_term = request.GET['search']
posts = posts.filter(text__icontains=search_term)
context = {
'posts': posts,
'search-term': search_term
}
return render(request, 'feed/home.html', context)

You likely need a function-based view. This is probably a duplicate or semi-related question.
from django.shortcuts import render
from django.db.models import Q
from .models import Posts #or whatever your model is
def search(request):
query = request.GET.get('q','')
#The empty string handles an empty "request"
if query:
queryset = (Q(text__icontains=query))
#I assume "text" is a field in your model
#i.e., text = model.TextField()
#Use | if searching multiple fields, i.e.,
#queryset = (Q(text__icontains=query))|(Q(other__icontains=query))
results = Posts.objects.filter(queryset).distinct()
else:
results = []
return render(request, 'home.html', {'results':results, 'query':query})
#You can also set context = {'results':results, 'query':query} after
#the else: (same indentation as return statement), and
#use render(request, 'home.html', context) if you prefer.
You should be able come up with your own error-handling or redirects as needed. Your urls.py will probably have to be something like:
from django.urls import path
from . import views
urlpatterns = [
path('feed/', views.search, name='home'),
#'feed/' being the name of desired url, 'views.search' the
#name of your func-based view, and "name='home'" the template
#you're using.
]
And your search bar would prob need to look like:
<form method='GET' action=".">
#I believe lowercase get also works
<input type="text" name="q" placeholder="Search posts"/>
<input type="submit" value="{{ query|escape }}"/>
</form>
EDIT: I forgot that you'll want to access the results and display them in your template (you can drop this in under the form for now). Something like:
{% if query %}
{% if results %}
<ul>
{% for item in results %}
<li>{{ item|escape }}</li>
{% endfor %}
</ul>
{% else %}
<p>Query returned no results.</p>
#SO is formatting "Query" in HTML for some reason. Nonetheless...
{% endif %}
{% endif %}

Related

Django - Not passing id from template to views

In my ToDoApp, I couldn't send the ID to my function. Not sure what mistake I'm making.
Seems my function is correct because when I tested the form action with "datechange/1". It worked.
Here is my code:
Index.html
{% extends 'base.html' %}
{% block content %}
<h3 style = "margin-bottom: 20px"><strong>To Do List App</strong></h3>
<form method="POST" action="datechange/{{task.id}}">
{%csrf_token%}
<ul class="list-group">
{% for task in tasklist %}
<li class="list-group-item d-flex justify-content-between align-items-center">
<input type='hidden' name = {{task.id}}>{{task.tasks}}
<span class="badge bg-primary rounded-pill">{{task.duedate}}
<input type="date" name="datepick"/>
<input type='submit' value = 'Update'>
</span>
</li>
{% endfor %}
</form>
Views.py
def index(request):
tasklist = Task.objects.all()
return render(request, 'index.html', {'tasklist':tasklist})
def datechange(request,id):
# taskid = request.POST.get(id='task.id')
# task = Task.objects.get(id=taskid)
task = Task.objects.get(id=id)
datepick = request.POST.get('datepick')
task.duedate = datepick
task.save()
return HttpResponseRedirect(reverse('index'))
Urls.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('',views.index,name='index'),
path('datechange/<int:id>',views.datechange,name='datechange'),
]
Don't use action in form like that, Django has a better behaviour for such simple forms. The view datechange is also not needed. Just put everything from that view into if request.method == "POST" like that:
def index(request):
if request.method == "POST":
task_id = request.POST.get("task_id")
task = Task.objects.get(id=task_id)
datepick = request.POST.get('datepick')
task.duedate = datepick
task.save()
tasklist = Task.objects.all()
return render(request, 'index.html', {'tasklist':tasklist})
And delete action from your form in template:
<form method="POST">
{%csrf_token%}
<input type="hidden" name="task_id" value="{{ task.id }}">
...
Submitting the form will render index again, but also will process everything in POST you have inside that view. If you just open it (GET method) it will ignore that processing opening the view in a standard way.

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 %}

Django - User Search

I'm trying to filter ListView based on post method from search bar in my basetemplate. So making it works like:
Insert name --> SearchBar-->GET Method-->SearchView class(in views.py)--> render html with usernames.
I have done this, but it wont work. Could you please tell me what I'm doing wrong?
views.py in my user app
class SearchView(ListView):
model = User
template_name = 'blog/list_of_users.html'
context_object_name = 'all_search_results'
def get_queryset(self):
result = super(SearchView, self).get_queryset()
query = self.request.GET.get('search')
if query:
postresult = User.objects.filter(username__contains=query)
result = postresult
else:
result = None
return result
urls.py in my blog app
path('users_search/?search=<str:username>', user_view.SearchView.as_view(), name='user_search'),
search form in html
<form class="example" method="GET">
<input type="search" placeholder="ユーザー検索..." name="user_name">
<button type="submit">
検索
</button>
rendered html with user names
{% for result in all_search_results %}
{{ result.username }}
{% empty %}
add something to show no results
{% endfor %}
override get_context_data method
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user_name = self.request.GET.get('user_name', '')
context['all_search_results'] = User.objects.filter(username__icontains=user_name )
return context
In your template
<form class="example" method="GET">
<input type="text" placeholder="ユーザー検索..." name="user_name">
<button type="submit">
検索
</button>
</form>
{% for result in all_search_results %}
{{ result.username }}
{% empty %}
add something to show no results
{% endfor %}
Update:
in template, <input ........... name="search">
in views, user_name = self.request.GET.get('search', '')

Django: How to have multiple "add another field" buttons in a form

I'm new to django and I'm having a lot of trouble with forms.
I'm making a calculation-based tool and I need to be able to have an arbitrary number of inputs.
As a really basic example, let's say I want to make a calculator that will sum and subtract any number of inputs. Each number to be added or subtracted is in its own number field. Both the list of "adding" fields and the list of "subtracting" fields has its own "add another field" button.
For starters, here's something that adds two inputs (since I can't figure out how to implement even 1 "add another field button" or understand the answer to it).
views.py
from __future__ import unicode_literals
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from .forms import AddForm
def _from_str(s):
try:
s = int(s)
except ValueError:
try:
s = float(s)
except ValueError:
pass
return s
#csrf_exempt
def web_adder(request):
if request.method == 'POST':
form = AddForm(request.POST)
# form = MyForm(request.POST, extra=request.POST.get('extra_field_count'))
if form.is_valid():
return web_adder_out(request, _from_str(form.cleaned_data['addend0']), _from_str(form.cleaned_data['addend1']))
else:
form = AddForm()
# form = MyForm()
return render(request, 'addercontent.html', {'form': form})
def web_adder_out(request, a, b):
return render(request, 'addercontentout.html', {'content':[a + b]})
forms.py
from django import forms
class AddForm(forms.Form):
addend0 = forms.CharField(label='first addend', max_length=100)
addend1 = forms.CharField(label='second addend', max_length=100)
addercontent.html
{% block content %}
<p>This is a web adder</p>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-default">Enter</button>
</form>
{% endblock %}
addercontentout.html
{% block content %}
{% for c in content%}
Result: {{c}}
<br>
Return
{% endfor %}
{% endblock %}
Don't use Django for the field generation. I would do all of it via HTML. Run your setup that you currently have, and you should be able to look at the page source to see how the inputs are structured. Then you can manually write the form in HTML, with JavaScript adding fields in as needed.
Something like this? (not tested, I haven't implement add button)
forms.py
class CalcForm(forms.Form)
first = forms.IntegerField()
second = forms.IntegerField()
def add(self):
first = self.cleaned_data['first']
second = self.cleaned_data['second']
return first + second
views.py
def index(request):
if request.method == "POST":
form = CalcForm(request.POST)
if form.is_valid():
result = form.add()
return render(request, 'your_result_template.html', {'result': result})
else:
form = CalcForm()
return render(request, 'your_template.html', {'form': form})
your_template.html
{% block content %}
<p>This is a web adder</p>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-default">Enter</button>
</form>
{% endblock %}
your_result_template.html
{% block content %}
<p>Sum:</p>
<h2>{{ result }}</h2>
{% endblock %}
Edit: For field generation you may need javascript.
I don't know why you want to use django for this kind of app.

Field value not turning up in request.POST querydict - python django

I'm sure I'm doing something really obviously wrong, but I can't see it.
I've made a simple form for a Django app, but it's only returning the csrf token, not the field value. The form submits fine, but there's no 'event-title' key/value pair in the QueryDict.
To be precise, when I log the QueryDict, it looks like this:
<QueryDict: {u'csrfmiddlewaretoken': [u'dpXmMHTE3WmQvdvrAUD4oFer2WfKEjWd']}>
create_event.html:
{% extends "basic-layout.html" %}
{% block maincontent %}
<h1>Create Event</h1>
{% if error_message %}<p>{{ error_message }}</p>{% endif %}
<form action="/create-event" method="post">{% csrf_token %}
<label for="event-title">Event title</label>
<input type="text" title="event-title" id="event-title" required/>
<input type="submit" value="create event"/>
</form>
{% endblock %}
urls.py
from django.conf.urls import include, url
from django.contrib import admin
from django.views.decorators.csrf import csrf_exempt
import views
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^$', views.listEvents),
url(r'^create-event', csrf_exempt(views.createEvent))
]
views.py
def createEvent(request):
if request.method == 'GET':
template = loader.get_template('create_event.html')
context = RequestContext(request, {})
return HttpResponse(template.render(context))
if request.method == 'POST':
logger = logging.getLogger('degub')
logger.info(request.POST)
event_title = request.POST.get('event-title', '')
if event_title:
event = Event(event_title)
c = {}
c.update(csrf(request))
template = loader.get_template('list_events.html')
context = RequestContext(request, c)
return HttpResponse(template.render(context))
else:
template = loader.get_template('create_event.html')
template_values = {"error_message": "Nope, didn't work"}
context = RequestContext(request, template_values)
return HttpResponse(template.render(context))
Try adding the name attribute in your input tag.
<input type="text" name="event-title" title="event-title" id="event-title" required/>

Categories