How to use Django Haystack whoosh search field in multiple application templates - python

I have implemented a working Django 1.6 haystack/whoosh search field following Mike Hibbert's tutorial:https://www.youtube.com/watch?v=B-n6_m66TmA. The implementation works in a twitter bootstrap search field in the navbar in the url 'search'. I'd like to use the implementation in several pages in my application but it doesn't work.
I've tried with implementing the search.html code in other pages with search bars but it changes the search url prefix from 'search/...' to the page url, e.g. to 'front/...' on the frontpage. I've also tried to include the search.html in other pages, both as the block content and with an include tag in the template but it hasn't worked.
# in app/templates/search/indexes/search.html
{% extends 'base_searches.html' %}
{% block content %}
<form method="get" action=".">
<table>
<tr><th><label for="id_q"></label></th><td><input type="text" id="id_q"
name="q" placeholder="Search..." class="form-control" /><button
class="btn btn-success" type="submit" value="Search"><spaclass="glyphicon
glyphicon-search"></span></button></td></tr>
<tr>
<td> </td>
{% if query %}
{% for result in page.object_list %}
<div class="alert alert-success" role="alert">
Hi, {{ user }}, here's the found database match for your search
<a href="{{ result.object.get_absolute_url }}">
{{result.object.title }}.</a></div>
{% empty %}
<div class="alert alert-info" role="alert">
<strong>Hi {{ user }}, there's no match found in the
database for your search !.</strong>
</div>
{% endfor %}
{% endif %}
<div class="col-xs-6 col-sm-3 sidebar-offcanvas" id="sidebar">
<!--/.sidebar-offcanvas-->
<!--/row-->
<hr>
<td>
</td>
</tr>
</table>
</form>
</body>
</html>
{% endblock %}
# in models.py for each indexed model field
def get_absolute_url(self):
return '%s' % self.id
# in app/templates/base_searches.html
<form class="navbar-form navbar-right" id="search">{% csrf_token %}
<center><h> <td colspan="1" id="content">{% block content %}}
{% endblock %} </center></td></h>
<ul id="search-results">
</ul>
</form>
in urls.py
from haystack.query import SearchQuerySet
url(r'^search/',include('haystack.urls'),name='haystack_search'),
# in search_indexes.py
from haystack import indexes
from app.models import Article
class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
text= indexes.CharField(document=True,use_template=True)
content = indexes.CharField(model_attr='description')
content_auto = indexes.EdgeNgramField(model_attr='title')
def get_model(self):
return Article
def index_queryset(self, using=None):
"""used when the entire index for model is updated """
return self.get_model().objects.all()
# in app/templates/search/indexes/appname/article_text.txt
{{ object.title }}
{{ object.content}}
How do I include the search.html in other pages, as I've included it in the navbar in base_searches.html, and maintain the prefix search/..searchresult for the get_absolut_url function for the search result object and not the url prefix for other pages that I'm trying to include it in, e.g. front/..searchresult ? when trying to implement it in the frontpage with url front/, or are there better ways to use the haystack whoosh search in search fields in multiple application pages ?

I have solved this problem by adding http://{{ request.get_host }}/search" to the search form action.
`<form method="get" action="http://{{ request.get_host }}/search"`
The action="." will reference to your current page and the request will be redirected to your current view. With adding the host URL to your action it will redirect your query to the search view no matter on which page you are.

Related

How to switch wagtail homepage depending on user logged in status

I have previously been using path("", include(wagtail_urls)) for my home_page url which displays the template at home/home_page.html correctly
I wish to however display different pages depending on whether a user is logged in or not so have changed this to:
def logged_in_switch_view(logged_in_view, logged_out_view):
'''switches views dependedn on logon status'''
def inner_view(request, *args, **kwargs):
if request.user.is_authenticated:
return logged_in_view(request, *args, **kwargs)
return logged_out_view(request, *args, **kwargs)
return inner_view
urlpatterns = [
path("",
logged_in_switch_view(
TemplateView.as_view(template_name="home/home_page.html")),
TemplateView.as_view(template_name="home/not_authenticated.html")),
name="home"),
]
With this approach (directly specifying the template rather than using wagtail_urls) the home page does not display correctly when logged in, in that all the wagtail tags in the html e.g. the blog posts are not displaying
home_page.html
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags %}
{% block content %}
<main class="container">
{% for post in page.blogs %}
{% with post=post.specific %}
<div class="col-md-8 mx-auto px-auto">
<div class="row border rounded overflow-auto flex-md-row mb-4 shadow-sm position-relative ">
<div class="col p-4 d-flex flex-column position-static">
<strong class="d-inline-block mb-2 text-primary">{{ post.category }}</strong>
<div class="mb-1 text-muted">{{ post.date }}</div>
<h3 class="mb-0">{{ post.title }}</h3>
<p>{{post.intro}}</p>
</div>
<div class="col-auto my-auto py-2 px-2 d-none d-lg-block">
<a href="{% pageurl post %}" class="stretched-link">
{% with post.main_image as main_image %}{% if main_image %}
{% image main_image min-250x250 max-350x350%}
{% endif %}{% endwith %}
</a>
</div>
</div>
</div>
{% endwith %}
{% endfor %}
</main>
{% endblock %}
How should I specify the home_page in the logged_in_switch_view function?
The include(wagtail_urls) pulls in Wagtail's page handling logic for selecting which page should be returned for a given URL. If you swap that line out with your own code, you're effectively swapping out all of Wagtail...
First, consider whether you're reinventing the page privacy feature that Wagtail already provides. If you want the site as a whole to require a login, and non-logged-in users to be given a blanket generic login form, you can enable this using the Privacy control under the Settings tab (or in the top right corner on older Wagtail releases) when editing the homepage, and set WAGTAIL_FRONTEND_LOGIN_TEMPLATE = "home/not_authenticated.html" in your project settings file.
If you just want to swap the template for the homepage specifically, you can do that by defining a get_template method on your HomePage model:
class HomePage(Page):
# ...
def get_template(self, request, *args, **kwargs):
if request.user.is_authenticated:
return "home/home_page.html"
else:
return "home/not_authenticated.html"
This way, your homepage will select one template or the other, but still keep the page object available on the template.

Django form action attribute is not working propely

I am working on app in django 1.11, on search feature. I installed elasticsearch - here all things are working.
In base.html and under url 127.0.0.1:8000 - I have form to search and I would like to keep this form here. On another hand I have search app with view, url, template - under url 127.0.0.1:8000/search/ - search is working here.
To solve this problem - search on main page and redirect on site with results I was trying to use action attribute in django form.
form in base.html
<form action="{% url 'search:search' %}" method="post">
{% csrf_token %}
<div class="input-group">
<input type="text" class="form-control" id="q" {% if request.GET.q %}value="{{ request.GET.q }}"{% endif %} name="q" placeholder="Search">
<div class="input-group-append">
<button class="btn btn-outline-primary" type="button">GO</button>
</div>
</div>
</form>
view in search app
def search(request):
q = request.GET.get('q')
if q:
posts = PostDocument.search().query('match', title=q)
else:
posts = ''
return render(request, 'search/search.html', {'posts': posts})
template with results
{% extends 'base.html' %}
{% block content %}
{% for p in posts %}
{{ p.title }}
{% endfor %}
{% endblock content %}
{% block sidebar %}{% endblock sidebar %}
You here mix up GET and POST. If the method is method="post", then the data is passed in the request, and thus ends up in the request.POST query dictionary.
If on the other hand the method is method="get", then the data ends up in the querystring of the URL. In that case, you can indeed use request.GET.
Often (not always), search queries are done with querystrings, since then a person can copy the URL and send it to another person, and that person thus can see the search results.
You can thus change the form to:
<form action="{% url 'search:search' %}" method="get">
{% csrf_token %}
<div class="input-group">
<input type="text" class="form-control" id="q" {% if request.GET.q %}value="{{ request.GET.q }}"{% endif %} name="q" placeholder="Search">
<div class="input-group-append">
<button class="btn btn-outline-primary" type="button">GO</button>
</div>
</div>
</form>

Inserting ListView Template in a section of the Homepage using Django 1.11

I have a ListView Template in Django 1.11 that renders to group_list.html. However I want the same view to show in a column in the home page
I also have screenshots below if that helps understanding what I am trying to achieve
I have tried different ways to use {% include 'some.html' %}
1) I tried to make a html page and include it in homepage. but it keeps giving errors
2) I tried to change the group_list.html page into a (insert template) that and tried to insert that, in both the index page and created another template view for groups. but that is giving errors too
Below are my views and templates
Views.py
class GroupCreate(LoginRequiredMixin, CreateView):
model = Group
fields = ('name', 'description')
class GroupList(ListView):
model = Group
class GroupDetail(DetailView):
model = Group
Below is my Index.html and group_list.html
INDEX.HTML
{% extends 'base.html' %}
{% block body %}
<div class="col-md-8" style="background-color:white; border-right:1px solid grey;">
<h4>Groups</h4>
<p>Join a group or make a new Group.</p>
<a style="float:left" class="btn btn-success" href="{% url 'groups:new' %}">Start a Group</a>
<div class="content">
<!--{ % include 'groups/some.html' % } I am trying to insert my -->
</div>
</div>
<div class="col-md-4" style=background-color:white">
<h4>Second Coloumn</h4>
</div>
{% endblock %}
Below is my group_list.html
{% extends 'groups/group_base.html' %}
{% block pre_group %}
<div class="col-md-4">
<div>
<h2>Welcome Back
{{group.user.username}}
</h2>
<h3>Groups</h3>
<p>Welcome to the groups page! Select the Group with the shared interest</p>
</div>
<div>
{% if user.is_authenticated %}
<a class="btn btn-warning" href="{% url 'groups:new' %}"><span class="glyphicon glyphicon-plus-sign"></span> Create a New Group</a>
{% endif %}
</div>
</div>
{% endblock %}
{% block content %}
<div class="col-md-8">
<div class="list-group">
{% for group in object_list %}
<a class="list-group-item" href="{% url 'groups:single' slug=group.slug %}">
<h3 class="list-group-item-heading">{{group.name}}</h3>
<div class="list-group-item-text container-fluid">
{{group.description_html|safe}}
<div class="row">
<div class="col-md-4">
<span class="badge">{{group.members.count}}</span> member{{group.members.count|pluralize}}
</div>
<div class="col-md-4">
<span class="badge">{{group.posts.count}}</span> post{{group.posts.count|pluralize}}
</div>
</div>
</div>
</a>
{% endfor %}
</div>
</div>
{% endblock %}
The HomePage View comes from the root Views they are below
View.py(root directory)
from django.views.generic import TemplateView
class Homepage(TemplateView):
template_name = 'index.html'
class LoggedIn(TemplateView):
template_name = 'test.html'
class LoggedOut(TemplateView):
template_name = 'thanks.html'
I am still learning Django while working on my 1st independent project. I did a some research but could not solve this
I want a list view like this to show on a section of the HomePage
Currently the view works perfectly well on the groups page
In your homepage view you can override get_context_data and add the groups to the context.
class Homepage(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super(Homepage, self).get_context_data(**kwargs)
context['object_list'] = Group.objects.all()
return context
In the index.html, you'll have to copy the code from your group_list.html template. To prevent duplication, you could move the code from the content block into a include_groups.html template, then use {% include 'include_groups.html' %} in yourindex.htmlandgroup_list.html` templates.
In this case, since your Homepage view is just a template view, you could use a ListView instead.
class Homepage(ListView):
model = Group
template = 'index.html'
Whichever option you go for, it's useful to understand how to override get_context_data in class based views.

Django: redirect user two pages back

I have a list of multiple objects from my database (named "plp's"), arranged in a table. Next to each "plp" element I have a button "Edit" to modify that particular entry.
Next, I redirect the user to a new url, where I pass the id of that "plp", and show the form to edit it, with a "save" button.
After pressing the "save", which is request.POST, I want to redirect the user back to the first url, with the list of all the "plp" objects in one list. That means to the site, where he first pressed "Edit".
Can I somehow save the url of where the "Edit" was clicked, and pass it to my views.py?
Thank you
listdns.html:
<td>
Uredi
</td>
urls.py:
rl(r'^(?P<plp_id>\d+)/uredi$', plp_list_uredi,name="plpuredi")
views.py:
def plp_list_uredi(request, plp_id=None):
moj_plp=PLPPostavka.objects.get(id=plp_id)
form=PLPPostavkaForm(request.POST or None,request=request,dns=moj_plp.dns, instance=moj_plp)
context ={
'plp':moj_plp,
'form':form,
}
if request.POST:
if form.is_valid():
plp = form.save()
return redirect(request.path)
return render(request, "plp_pos/uredi.html",context)
uredi.html
<form action="" method="POST">
{% csrf_token %}
<div class="box">
<div class="box-header">
<h4 class="box-title">
Urejanje PLP Postavke
</h4>
</div>
<div class="box-body">
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="col-md-2 control-label detail">{{ field.label }}</label>
<div class="col-md-10">
{% if field|field_type == "datefield" %}
{% render_field field class+="form-control dateinput" %}
{% else %}
{% render_field field class+="form-control" %}
{% endif %}
</div>
</div>
{% endfor %}
</div>
<div class="box-footer">
<div class="box-tools pull-right">
<input type="submit" value="Shrani" class="btn btn-primary" />
</div>
</div>
Don't you only have 1 page to edit all the elements? Then you could perhaps hardcode the link e.g.
return HttpResponseRedirect(my_edit_url)
If this doesn't work and you need to go 2 pages back take a look at this post:
How to redirect to previous page in Django after POST request

my haystck results are not displaying on the results page

I am new to python and django and I was following a tut with major errata. right now I am trying to get the results page to display my results. This is not my code, it's from a tutorial. Here is my code.
my views.py
def post_search(request):
form = request.GET.get('q')
results = SearchQuerySet().models(Post).filter(content=form)
# count total results
total_results = results.count()
template = 'blog/post/search.html',
context = {
'form': form,
'results': results,
'total_results': total_results
}
return render(request, template, context)
my search.html
{% extends "blog/base.html" %}
{% block title %}Search{% endblock %}
{% block content %}
{% if request.GET %}
<h1>Posts containing "{{ form.query }}"</h1>
<h3>Found {{ total_results }} result{{ total_results|pluralize}}</h3>
{% for result in results %}
{% with post=result.object %}
<h4>{{ post.title }}</h4>
{{ post.body|truncatewords:5 }}
{% endwith %}
{% empty %}
<p>There are no results for your query.</p>
{% endfor %}
<p>Search again</p>
{% else %}
<h1>Search for posts</h1>
<form action="." method="get">
<div>
<div class="input-group">
<input type="text" class="form-control" autocomplete="off" name="q" id="search" placeholder="search" value="{{ request.GET.q }}">
<span class="input-group-btn">
<button type="submit" class="btn btn-default">search</button>
</span>
</div><!-- /input-group -->
<ul class="list-group" id="search-results" style="margin: 5px 0 0 0; width: 325px">
</ul>
</div>
</form>
{% endif %}
{% endblock content%}
my post_text.txt
{{ object.title }}
{{ object.tags.all|join:", " }}
{{ object.body }}
search_indexes.py
from haystack import indexes
from .models import Post
class PostIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
publish = indexes.DateTimeField(model_attr='publish')
# title_auto = indexes.EdgeNgramField(model_attr='title')
# content_auto = indexes.EdgeNgramField(model_attr='content')
def get_model(self):
return Post
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.all()
the only thing that shows on the page after I run a search is
Posts containing ""
Found 1 result
Search again
and if I do this
{{results}}
it returns this
[<SearchResult: blog.post (pk='2')>]
where it says post containing a name will show
found results returns a number which lets me know that the results are working
any and all help or guidance in the right direction is greatful. Tried to read the docs. They seem so vague to me. and as I said I am a novice.and there are only video on the topic and that with frank hibbert and he uses it with ajax

Categories

Resources