How to show haystack(whoosh) results into a separate page? - python

I have a index.html where my form is implemented:
...
{% block searchform%} {% endblock %}
...
In my search/ folder I have two files.
search.html, which is the actual form for the index.html
{% extends 'index.html' %}
{% block searchform %}
<form method="get" action="/results/">
<table>
{{ form.as_table }}
<tr>
<td> </td>
<td>
<input type="submit" value="Search">
</td>
</tr>
</table>
{% endblock %}
and the file result.html, where I want to see my search results:
{% if query %}
<h3>Results</h3>
...
{% endif %}
urls.py:
from django.conf.urls import include, patterns, url
from dev_devices.views import *
urlpatterns = patterns('',
url(r'^$', include('haystack.urls')),
url(r'^results/$', results, name = 'results')
)
view.py
def results(request):
return render_to_response('results.html')
My problem is, that there are no results showing up. When I click on the submit Button of my form, I will redirected to the correct file (results.html), but no results are showing up. This question exists already but i found no answer, so can someone help me? Thank you !

Well, you aren't passing any results to the template. Check out your views.py:
def results(request):
return render_to_response('results.html')
Your template has this logic:
{% if query %}
<h3>Results</h3>
...
{% endif %}
query is not a variable in context. You'd have to pass the query in from the view:
def results(request):
return render_to_response('results.html', {'query': 'some query'})
But more than that, you need to get the actual results from Haystack and put those in context too. I think you are confused about what's happening here. If you want to use haystacks default views for search/results, then you shouldn't define any custom views at all. The way your results view is defined, how is Django to know this page has anything to do with Haystack at all?
If you do want to define custom views, then those views need to implement Haystack search forms, creation of SearchQuerySets, pagination, (and so on), and pass that data to the template. See the Haystack documentation on creating you own views:
http://django-haystack.readthedocs.org/en/latest/views_and_forms.html#creating-your-own-view
If you just want to see your results and this particular URL structure is not important to you, then just change your form action to point at the same page action="" and move your results display logic from results.html to search.html. That should get you the basic search results that ship with Haystacks default views and forms.

Related

How to send object from detail view to another view in Django?

I have a detail view that uses a Quiz object to display data stored in that object, like title and author. I want to have a button that links to a new page that displays different data from the same object. I don't know how to pass this data/object.
I can render the view and pass it the context of a specific quiz using an id but I want the id to change to be the id of the object from the initial page.
#assessement view
def assessment(request):
context = {
'quiz':Quiz.objects.get(id=1),
}
return render(request, 'quiz_app/assessment.html', context)
#detailview template for quiz
{% extends "quiz_app/base.html" %}
{% block content %}
<article class="quiz-detail">
<h1>{{ object.title }}</h1>
<h2>{{ object.question_amount }} Questions</h2>
<a class="btn" href="{% url 'quiz-assessment' %}">Start Quiz</a>
</article>
{% endblock content %}
#assessment template
{% extends "quiz_app/base.html" %}
{% block content %}
<h2>Assessment</h2>
<h2>Title is {{ quiz.title }}</h2>
{% endblock content %}
Then you should make another view for url quiz-assessment and pass the quiz pk as you did above in your assessment view.
def quiz_assessment(request,pk):
quiz = Quiz.objects.get (pk=pk)
return render (request,'assessment_template', {'quiz':quiz}
And in your url,pass the quiz id like this:
path ('<int:pk>/quiz/assessment /',views.quiz_assessment,name='quiz_assessment')
And in your template you can give url like this:
< a class="btn" href="{% url 'quiz_assessment' object.pk %}>
As suggested in the comments by #Robin Zigmond, you can do like this.
#assessement view
def assessment(request, qid):
context = {
'quiz':Quiz.objects.get(id=qid),
}
return render(request, 'quiz_app/assessment.html', context)
In the HTML file
#detailview template for quiz
{% extends "quiz_app/base.html" %}
{% block content %}
<article class="quiz-detail">
<h1>{{ object.title }}</h1>
<h2>{{ object.question_amount }} Questions</h2>
<a class="btn" href="{% url 'quiz-assessment' qid=object.id %}">Start Quiz</a>
</article>
{% endblock content %}
and in your urls.py change as:
path('quiz_asswssment/?P<int:qid>/', views.assessment, name="quiz_assessment")
Besides, what SammyJ has suggested, You can use the django sessions library or the django cache framework. You can temporarily store the information you need for the next view and access it whenever you want to.
In what Sammy J had suggested, you will always to have make sure that the queryset is passed in the context, otherwise it will not be rendered.
def assesment(self, request, id):
q = Quiz.objects.get(pk=id)
request.session["someData"] = q.name
request.session["qAmount] = q.amount
In your template file
<p>The title is : {{request.session.title}} and the amount is {{request.session.qamount}}
Note: Django sessions do not allow you to set a queryset as a session record, for that, you can use Django Cache framework.
Example
from django.core.cache import cache
cache.set('quiz', q)
getting cache -> cache.get('quiz')
Sessions framework docs : https://docs.djangoproject.com/en/2.2/topics/http/sessions/
Cache framework docs: https://docs.djangoproject.com/en/2.2/topics/cache/

Django - Player URL not found despite being in urls.py

Over the last few days I have been working on building a Django web application for allowing users to make queries on a MLB Statistics database. I have some of the web app working fine, however I tried to implement a search function and ran into problems with the URL mapping. I tried to move on from this and instead work on getting the database to display information based on the link to the player that they click on, but I am running into the same issue with the URL's being dynamic based on the player's player_id. This portion only uses the People model which has player_id as its primary key.
Currently, I have an 'allPlayers.html' that lists every player in the database along with a link sending the player's player_id to the url as so:
allPlayers.html
{% extends "base_template.html" %}
{% block content %}
<h1>MLB Stats All Players</h1>
<p>Currently, there are : {{ num_players }} players listed in the database.</p>
<table style="width:100%">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th></th>
</tr>
{% for player in players %}
<tr>
<td>{{ player.name_first }}</td>
<td>{{ player.name_last }}</td>
<td>More on {{ player.name_first }} {{ player.name_last }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
And the corresponding view for 'allPlayers.html':
def allplayers(request):
"""View function for allPlayers page of site."""
# Generate counts of people object
num_players = People.objects.count()
players = People.objects.all()
context = {
'num_players': num_players,
'players': players,
}
# Render the HTML template allPlayers.html w/ data in context variable
return render(request, 'allPlayers.html', context=context)
With just the above I have successfully created a page on my web app that lists each player in the database along with a link, and notice that I am trying to send the player_id through <a href='players/{{ player.player_id }}>. Currently the allPlayers portion works fine. However, things go south when I add the following playerInfo.html and corresponding view:
playerInfo.html
{% extends "base_template.html" %}
{% block content %}
{% if results %}
{% for player in results %}
<p>{{ player.name_first }} {{ player.name_last }}</p>
{% endfor %}
{% endif %}
{% endblock %}
And the view:
def player(request, pk=None):
if pk is not None:
print('Hello world')
print('pk :', pk)
#instance = get_object_or_404(People, player_id=pk)
results = People.object.filter(player_id=pk)
context = {
"results": results
}
return render(request, "playerInfo.html", context)
else:
print('Hello')
return render(request, 'playerInfo.html')
My idea was that the link noted earlier containing {{ player.player_id }} would match up with the following url and place the player_id value in for pk, as follows using the <int:pk> syntax instead of regex:
polls/urls.py
urlpatterns = [
path('', views.index, name='index'),
path('allPlayers', views.allplayers, name='allplayers'),
path('allTeams', views.allteams, name='allteams'),
path('search/', views.search, name='search'),
path('player/<int:pk>/', views.player, name='player'),
]
However, once I navigate to my 'allPlayers' page and click on one of the links for the player, say Hank Aaron (who has player_id aaronha01), I get the following Page not found (404) error:
Using the URLconf defined in baseballdb.urls, Django tried these URL patterns, in this order:
polls/ [name='index']
polls/ allPlayers [name='allplayers']
polls/ allTeams [name='allteams']
polls/ search/ [name='search']
polls/ player/<int:pk>/ [name='player']
admin/
The current path, polls/player/aaronha01/, didn't match any of these.
I have been having trouble with this for quite a bit now. If anyone has any advice and can point me in the right direction of how I'm thinking about this incorrectly or has a solution for me that would be seriously greatly appreciated! Thank you.
path('player/<int:pk>/' means only valid int pk would match. If your pk is not int and something like a valid slug - use path('player/<slug:pk>/' instead.
docs: https://docs.djangoproject.com/en/2.1/topics/http/urls/#path-converters
And my suggestion is to use {{ player.get_absolute_url }} or {% url 'player' player.id %} instead of building url manually.
Missing leading slash means "from here", not from website root:
https://webmasters.stackexchange.com/questions/56840/what-is-the-purpose-of-leading-slash-in-html-urls
There is a mismatch between <int:pk> in your path(), which expects an integer, and your player_id, which is a string like 'aaronha01'.
You can either use the pk everywhere, and have urls like /player/17/:
path('player/<int:pk>/', views.player, name='player'),
def player(request, pk):
instance = get_object_or_404(People, pk=pk)
context = {
"instance": instance,
}
return render(request, "playerInfo.html", context)
# Use pk in template when constructing URL
<td>More on {{ player.name_first }} {{ player.name_last }}</td>
Or you can use player_id everywhere, and have urls like /player/aaronha01/.
path('player/<slug:player_id>/', views.player, name='player'),
def player(request, player_id):
instance = get_object_or_404(People, player_id=player_id)
context = {
"instance": instance,
}
return render(request, "playerInfo.html", context)
# Use player_id in template when constructing URL
<td>More on {{ player.name_first }} {{ player.name_last }}</td>
As a next improvement, you can start using the {% url %} tag so that you aren't hardcoding the URLs anymore.
<a href="{% url "polls:player" player.player_id %}">
In the above, I've assumed that you have app_name='polls' in your polls/urls.py, and that you have decided to use player_id instead of pk in your url pattern.

How to render data to a {% included a.html %} template in Django

I have a rank.html which is a publicly sharing template for many other templates through {% include rank.html %} method.
This template will display the 48 hours hot news base on the click number.
Here is the view.py:
def rank(self, request):
hot_news_48h = h_mostViewed(48, News, '-pv')
return render(request, "rank.html", {
'hot_news_48h': hot_news_48h,})
h_mostViewed(48, News, '-pv') is a function,that can fetch most viewed(clicked) post within 48 hours.It works.
Here is the rank.html:
<ul>
{% for hot_view in hot_news_48h %}
<li>
<a href="{% url 'news:news_detail' hot_view.pk %}" >
<img src="{{ MEDIA_URL }}{{ hot_view.image }}" >
</a>
<a href="{% url 'news:news_detail' hot_view.pk %}">
<h6>{{ hot_view.title }}</h6>
</a>
</div>
</li>
{% endfor %}
</ul>
Here is the url.py:
path('hot_news', views.rank, name="hot_news")
The problem is,I can only get the html ,but can't receive the data.
But if I give up {% include rank.html %} method and insert the rank.html's code directly inside each template which need this function, I can get the data.
Take new_detail.html template as an example:
Here is the view.py:
def newsDetailView(request, news_pk):
news = get_object_or_404(News, id=news_pk)
all_comments = NewsComments.objects.filter(news=news)
news.comment_nums = all_comments.count()
news.save()
News.objects.filter(id=news_pk).update(pv=F('pv') + 1)
hot_news_48h = h_mostViewed(48, News, '-pv')
relative_news = News.objects.filter(tag__id__in=news.tag.all()).exclude(id=news_pk)[:6]
return render(request, "news_detail.html", {
'news': news,
'all_comments': all_comments,
'hot_news_48h': hot_news_48h,
'relative_news': relative_news
})
Here is the urls.py:
path('-<int:news_pk>', views.newsDetailView, name="news_detail"),
So above,I directly inserted rank.html's code into new_detail.html and it works I can get the data.
My question is what should I do or correct,so that I can get the data in {% include rank.html %} method. Because {% include rank.html %} is simple and flexible.I don't want to repeat the same code in several same template.
Thank you so much for your patience!
How about this:
- Create a folder "templatetags" in your application and add a file "news_tags.py" or name it what you want. Then you can define the tags you need:
from django.template import Library
from your_app.models import your_model
register = Library()
#register.inclusion_tag('your_app/your_template.html')
def hot_news(num, order):
objects = News.objects.order_by(order)[:num]
result['objects'] = objects
return result
In your templates you then do the following:
{% load news_tags %}
{% hot_news 48 '-pv' %}
Then create a template as your already did and reference it in the inclusion tag. Then it should work properly.
If you want it to work for multiple models you can have a look at this: https://docs.djangoproject.com/el/2.1/ref/applications/
The apps framework allows you to fetch models from a string input.
I finally solved the issue by Creating custom context processor.https://www.youtube.com/watch?v=QTgkGBjjVYM

How to slice a list in Django template?

I'm doing some pagination in Django. I want to show the pages' number which are ahead of the current page. I am using the following code:
{% for page in blogs_page.paginator.page_range|slice:"0:{{ blogs_page.number }}" %}
But this seems useless; the result does the same as the following:
{% for page in blogs_page.paginator.page_range %}
The slice does not work here. How do I fix this?
Never use {{ }} inside of {% %}, don't do this {% {{ }} %}.
{% for page in blogs_page.paginator.page_range|slice:"0:blogs_page.number" %}
I think it won't work. If I were you I would create a custom tag and executed all the logic there. So, it will look like this:
Template:
{% custom_tag blogs_page as result %}
{% for page in result %}
templatetags/tags.py:
from django import template
register = template.Library()
#register.simple_tag
def custom_tag(bl_page):
return bl.page.paginator.page_range[0:bl_page.number]
Details: custom tags

Create Sorting Options in Django Template

I am new to Django and in need of some help. I have my database entries being displayed on a template (6 entries). I am aware that to sort your database objects, you need to write Model_Name.objects.all().order_by('Choose_Sort_Option') in the views.py. I would like to make this function a link in that same template. So far, the only thing I've been able to come up with is making a new view, new url pattern, and new template just to display that one new sort. I don't want have to make 30 pages just for sorting. I am thinking that I need to assign that line of code above to a variable(see product d and product_a below). Then i would call that variable in the template, but I'm not sure how to replace the existing list of database items. In short: How do I create clickable links to sort my database objects all within the same template. This is already done in the admin. Thank you!
my views.py:
from django.http import Http404, HttpRequest
from django.contrib import messages
from vendor_db.models import Itemo
from django.db.models import Q
from django.shortcuts import HttpResponse, HttpResponseRedirect,
render_to_response, render, redirect, get_object_or_404
def index(request):
return render(request, 'vendor_db/index.html')
def vendors(request):
items = Itemo.objects.all()
var_2 = request.GET.get("q")
product_d = Itemo.objects.all().order_by('-Product_Name')
product_a = Itemo.objects.all().order_by('Product_Name')
if var_2:
items = items.filter(Vendor_Name__icontains=var_2)
context = {'items': items,
'product_d' :product_d,
'product_a' :product_a,
}
return render(request, 'vendor_db/vendors.html', context)
my template (it is already displaying all of my database objects in an arbitrary order):
{% extends "base.html" %}
{% block content %}
{% load static %}
<h2>All Vendors</h2>
<h3>Search</h3><div>
<form method='GET' action=''>
<input type='text' name="q" placeholder='Search Vendors'/>
<input type='submit' value='Search'/>
<form action="{% url 'vendors' %}">
<input type="submit" value="Reset">
</form>
</form>
<br>
{% if items %}
{% for stuff in items %}
<a href="{% url 'vendors_detail' stuff.id %}">
{{ stuff.Product_Name|capfirst }}<div></div>
</a>
{% endfor %}
{% else %}
<p>No Results Found</p>
{% endif %}
<h3>Sort by:</h3>
Product Name
{% endblock %}
EDIT
While I am sure the 2 answers below would the do the trick, I was unable to comprehend (my own fault). I places my database entries in a table and used a javascript library to change the sort order just by clicking the column header. You can see the question I asked and the code here
In your model.py class use
Class XYZ()
...................
...................
class Meta:
ordering = [
"your_first_field_name",
"your_second_field_name",
........................
]
You could use a form with a variable as you said, pass the form back to the same view and get the sort option from that and create the sorted query for your product items dynamically or using if statements. Then rerender the page. This means only one view would be needed.
Or you may be interested in an app called django-tables2, it gives you the ability to render a nice table which can then be sorted as you wish by clicking on arrows.

Categories