Search results in template

91 views
Skip to first unread message

Malik Rumi

unread,
Dec 19, 2017, 8:12:07 PM12/19/17
to Django users

I am implementing search on a local Django project: Django 1.11.5, Python 3.6.3, Ubuntu 16.04. My issue is getting the search results onto the template.

I am using standard CBV for everything else in the site, but for this one I wrote my own. It uses the same template as my ListView, which shows all objects fine.



def serp(request):
    if request.method == 'GET' and 'searchbox' in request.GET:
        q = request.GET.get('searchbox')
    query = SearchQuery(q)
    object_list = Entry.objects.annotate(
        rank=SearchRank(F(
            'search_vector'), query)).filter(
        search_vector=query).order_by(
        '-rank').values_list('title', 'rank')
    return render(request, 'serp_list.html', {'object_list': object_list})


Django Debug Toolbar reports everything went as expected. The right template and view were called, as well as the right db query. I had previously tested this query in the shell, and it pulls up a 4 element queryset, as it should. 

Even though no search results show on the page, there are 4 instances of the html that should be surrounding each result and rows=4 loops=1 in the query plan.

One thing I found curious is that the **mere change** in the variable name 'object_list' to 'resultslist' gave me no db query! I had always thought variable names didn't matter in Python, 
but apparently 'object_list' is virtually a reserved word when it comes to views (even FBVs) and templates in Django? Here is the template:

<blogpost start>
<article class="blogpost">
	<header>
		{% for object in object_list %}
		<h2><ahref="{{ object.get_absolute_url }}">{{ object.title }}</a></h2>
			<div class="post-info">
				<span class="post-date">
					<i class="icon-calendar"></i>
						<span class="day">{{object.chron_date}}</span>
							<span class="month">{{object.clock}} </span>
	</span>
<span class="submitted"><i class="icon-user-1"></i> by <a href="#"></a></span>
<span class="comments"><i class="icon-chat"></i> <a href="#">22 comments</a></span>
									</div>
	</header>
		<div class="blogpost-content">
			{{ object.content|truncatewords:30 }}</div>
				<footer class="clearfix">
					<div class="tags pull-left"><i class="icon-tags"></i> <a href="#">tag 1</a>, <a href="#">tag 2</a>, <a href="#">long tag 3</a></div>
	<div class="link pull-right"><i class="icon-link"></i><a href="{{ object.get_absolute_url }}">Read More</a></div>
				</footer>
</article>
		<!-- blogpost end-->
		{% endfor %}

Any assistance in getting the search results to show up greatly appreciated.
--

ps -


Despite the 99% similarity, this is not identical to my issue in https://groups.google.com/forum/#!searchin/django-users/template%7Csort:relevance/django-users/o4UKSTBtwyg/cU4JeRJHHgAJ There I had the for variable 'i' but put the name of the list in the {{regular variables}}. That is not the issue this time, but clearly I have trouble grasping the regular and efficient use of Django context and templates. And yes, I have looked at the docs as well as elsewhere. So if, in addition to an answer, you can point me to a real clear, simple, step by step primer on making these two things work together, I would gladly look at it.  









-

Matemática A3K

unread,
Dec 20, 2017, 1:23:54 AM12/20/17
to django...@googlegroups.com
On Tue, Dec 19, 2017 at 10:12 PM, Malik Rumi <malik....@gmail.com> wrote:

I am implementing search on a local Django project: Django 1.11.5, Python 3.6.3, Ubuntu 16.04. My issue is getting the search results onto the template.

I am using standard CBV for everything else in the site, but for this one I wrote my own. It uses the same template as my ListView, which shows all objects fine.



def serp(request):
    if request.method == 'GET' and 'searchbox' in request.GET:
        q = request.GET.get('searchbox')
    query = SearchQuery(q)
    object_list = Entry.objects.annotate(
        rank=SearchRank(F(
            'search_vector'), query)).filter(
        search_vector=query).order_by(
        '-rank').values_list('title', 'rank')
    return render(request, 'serp_list.html', {'object_list': object_list})


Django Debug Toolbar reports everything went as expected. The right template and view were called, as well as the right db query. I had previously tested this query in the shell, and it pulls up a 4 element queryset, as it should. 

Even though no search results show on the page, there are 4 instances of the html that should be surrounding each result and rows=4 loops=1 in the query plan.

One thing I found curious is that the **mere change** in the variable name 'object_list' to 'resultslist' gave me no db query! I had always thought variable names didn't matter in Python, 
but apparently 'object_list' is virtually a reserved word when it comes to views (even FBVs) and templates in Django?

No, it is not a reserved keyword, the thing is by convention, CBV's Listview (and others with MultipleObjectsMixin) makes available in that variable the objects retrieved the view. It's a convention so you can easily reuse code. What is important is the name you pass to the template,
{**'object_list'**: your_retrieved_objects}
 as it expects that. If nothing evaluates the variable which contains your query / queryset results, it might be never executed - is that your doubt?
Your code seems fine, it seems that the object_list is not getting to the context of the Template and the "for" is not executing due to an empty object_list. I don't remember where is it in DDT, but there is a place where it shows the context for the Template, look for there if there is no empty (or not defined) "object_list"

HTH
 

--

ps -


Despite the 99% similarity, this is not identical to my issue in https://groups.google.com/forum/#!searchin/django-users/template%7Csort:relevance/django-users/o4UKSTBtwyg/cU4JeRJHHgAJ There I had the for variable 'i' but put the name of the list in the {{regular variables}}. That is not the issue this time, but clearly I have trouble grasping the regular and efficient use of Django context and templates. And yes, I have looked at the docs as well as elsewhere. So if, in addition to an answer, you can point me to a real clear, simple, step by step primer on making these two things work together, I would gladly look at it.  









-

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/6670bc0d-9f64-4b6f-9f62-a007d969e0b9%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

James Schneider

unread,
Dec 20, 2017, 9:24:57 AM12/20/17
to django...@googlegroups.com


On Dec 19, 2017 5:12 PM, "Malik Rumi" <malik....@gmail.com> wrote:

I am implementing search on a local Django project: Django 1.11.5, Python 3.6.3, Ubuntu 16.04. My issue is getting the search results onto the template.

I am using standard CBV for everything else in the site, but for this one I wrote my own. It uses the same template as my ListView, which shows all objects fine.



def serp(request):
    if request.method == 'GET' and 'searchbox' in request.GET:
        q = request.GET.get('searchbox')
    query = SearchQuery(q)
    object_list = Entry.objects.annotate(
        rank=SearchRank(F(
            'search_vector'), query)).filter(
        search_vector=query).order_by(
        '-rank').values_list('title', 'rank')
    return render(request, 'serp_list.html', {'object_list': object_list})


Django Debug Toolbar reports everything went as expected. The right template and view were called, as well as the right db query. I had previously tested this query in the shell, and it pulls up a 4 element queryset, as it should. 


Your view has a bug. If the first 'if' statement returns false, then the view fails because the 'q' variable is never set, and your viewer will get a 500 error. I think the two lines following the 'q =' line should be indented inside of the if statement, and object_list should be set to an empty list by default before the 'if' statement.

You should also ensure that this view is the one that is actually being called by the URL dispatcher.

Checking what is in the template context with the DDT is the next step. If the template context is incorrect, then you'll need to debug within your view to determine where/why the context is not being set correctly.

'object_list' is a generic variable name used by list CBV's in the template context, but should be available to FBV's since there is no magic context creation.

-James

Malik Rumi

unread,
Dec 22, 2017, 6:37:58 PM12/22/17
to Django users
Thanks to both of you. The template context is what it should be, and putting all the view in the same block made no difference. :-(

James Schneider

unread,
Dec 22, 2017, 7:19:55 PM12/22/17
to django...@googlegroups.com


<blogpost start>
<article class="blogpost">
	<header>
		{% for object in object_list %}
		<h2><ahref="{{ object.get_absolute_url }}">{{ object.title }}</a></h2>
			

Here's your problem. Your results are being entered in to your header and not in to the body of the HTML. I bet if you look at the source code of the rendered page, they're all there.

-James

James Schneider

unread,
Dec 22, 2017, 7:22:24 PM12/22/17
to django...@googlegroups.com

Here's your problem. Your results are being entered in to your header and not in to the body of the HTML. I bet if you look at the source code of the rendered page, they're all there.

Scratch that, I'm thinking of <head> not <header>.

James Schneider

unread,
Dec 22, 2017, 7:32:34 PM12/22/17
to django...@googlegroups.com
There is still an issue with the HTML from what I can see, your {% for %} loop starts within an <article> tag, but the </article> tag is within the {% for %} loop, so you are trying to create multiple article entries, but only the first has an opening <article> tag. Not sure how/if that affects the display of the results.

The point from my previous goof still stands, do your results show within the rendered page HTML, but just aren't being displayed?

-James

Malik Rumi

unread,
Dec 23, 2017, 8:37:05 PM12/23/17
to django...@googlegroups.com
FIRST, James, let me say how much I greatly appreciate you hanging in there and trying to help me.
2nd, as to your two points about the html:
   a. This template, serp_list.html, is identical to ktab_list.html, except that serp_list has the title 'SEARCH' at the top and some bolierplate text about this being the search results page. Other than that they are identical, including the template for loop. 
       serp_list is supposed to be called by localhost/serp/, which points to views.serp, which is an FBV. 
       ktab_list is a vanilla implementation of ListView, called by localhost/index/, and it shows all Entry objects as you would expect.  
       The header you see is the blog post header, ie, this is where the title and the get absoulte url to the detail page is supposed to go. On /index/ that works like it should. On serp, well, that's why I am here.
  b. Your second point is probably well taken, that the for loop should completely wrap the article, and that might explain why the author and comments part of the last three posts were not the same font as the first post. I have changed that in the html, but it has not changed the result. 

3rd, just because things weren't complicated enough, now I am getting inconsistent results. Whereas before I got views.serp called, a db query with 4 results, on the right template, now I am getting a value error ('returns None' - how's that for a news flash?). The only difference is that I downloaded the free version of pycharm to see if their visual debugger would help me. So calling the page through pycharm, instead of the regular terminal, is the only difference, and I don't think that should make a difference. But either way, I still have no result on my page - and oh yea, pycharm says the same thing my print statements did - that the query was there and pulled up the expected objects. They just won't show up on the template. 

4th, I don't know how relevant this is, but the form which created the search box has the form action = '/serp/', but despite that, if I use the search box in /index/, I stay on /index/ and just get the query attached to the end of that url, rather than /serp/ plus the query. I have to manually type in /serp/ to get  views.serp called. When it stays on  /index/ plus the query, the result shown are just Entry.objects.all(), as you would expect on a ListView. Any thoughts on that? Is it related? Why isn't my form action working? THANKS!



“None of you has faith until he loves for his brother or his neighbor what he loves for himself.”

--
You received this message because you are subscribed to a topic in the Google Groups "Django users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-users/xSW1BjLXXSQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to django-users+unsubscribe@googlegroups.com.

To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

James Schneider

unread,
Dec 24, 2017, 5:45:38 AM12/24/17
to django...@googlegroups.com


On Dec 23, 2017 5:36 PM, "Malik Rumi" <malik....@gmail.com> wrote:
FIRST, James, let me say how much I greatly appreciate you hanging in there and trying to help me.

No worries.

2nd, as to your two points about the html:
   a. This template, serp_list.html, is identical to ktab_list.html, except that serp_list has the title 'SEARCH' at the top and some bolierplate text about this being the search results page. Other than that they are identical, including the template for loop. 
       serp_list is supposed to be called by localhost/serp/, which points to views.serp, which is an FBV. 
       ktab_list is a vanilla implementation of ListView, called by localhost/index/, and it shows all Entry objects as you would expect.  
       The header you see is the blog post header, ie, this is where the title and the get absoulte url to the detail page is supposed to go. On /index/ that works like it should. On serp, well, that's why I am here.
  b. Your second point is probably well taken, that the for loop should completely wrap the article, and that might explain why the author and comments part of the last three posts were not the same font as the first post. I have changed that in the html, but it has not changed the result. 

Hooray, at least I've gotten something right. ;-


3rd, just because things weren't complicated enough, now I am getting inconsistent results. Whereas before I got views.serp called, a db query with 4 results, on the right template, now I am getting a value error ('returns None' - how's that for a news flash?). The only difference is that I downloaded the free version of pycharm to see if their visual debugger would help me. So calling the page through pycharm, instead of the regular terminal, is the only difference, and I don't think that should make a difference. But either way, I still have no result on my page - and oh yea, pycharm says the same thing my print statements did - that the query was there and pulled up the expected objects. They just won't show up on the template. 

Confused. You mentioned PyCharm affected the results but then indicated the same results?


4th, I don't know how relevant this is, but the form which created the search box has the form action = '/serp/', but despite that, if I use the search box in /index/, I stay on /index/ and just get the query attached to the end of that url, rather than /serp/ plus the query. I have to manually type in /serp/ to get  views.serp called. When it stays on  /index/ plus the query, the result shown are just Entry.objects.all(), as you would expect on a ListView. Any thoughts on that? Is it related? Why isn't my form action working? THANKS!



A few (possibly dumb) questions:

Have you verified that the serp.html page is the one actually being rendered? DDT can verify this.

Have you confirmed the context being provided to the template actually contains the variables and query results you are expecting? DDT can also verify this.

Can you do something simple like {{ object_list }} in your template just to see if anything is printed? When doing quick troubleshooting I like to do this:

|{{ object_list }}|

That way if I see || in the template (and source), I know the variable is empty.

Do you have any non-standard middleware or template context processors enabled?

Are your forms set to use a method of GET or POST? Sounds like GET but just want to verify.

When you are on /index/ and you search, does the runserver console show a hit against /serp/ (per the form action) or is it simply a hit against /index/ again? Or is there a redirect for some reason?

-James


Malik Rumi

unread,
Dec 26, 2017, 4:55:05 PM12/26/17
to django...@googlegroups.com
PyCharm had my project running in a separate process. Don't worry about that. I don't think it's a huge issue


I have the right view


The template context says I have a queryset


The explanation seems right.


And so does the selection

I put print statements in the view:

##########
Quit the server with CONTROL-C.
This is the value of query:  SearchQuery(eaa)
This is the value of QuerySet:  <QuerySet [('2017-10-09 Incentive, a further review', 0.151982), ('2017-11-14  Pulling The Trigger', 0.151982), ('2017-10-05 A Failure of Incentive?', 0.121585), ('2017-10-20 spider refactor', 0.121585)]>
[26/Dec/2017 18:57:31] "GET /serp/?q=eaa HTTP/1.1" 200 66580

##########

Note this is all done through my simple html search form, defined in the template. My Django Form, SearchBox(), does absolutely nothing, as if it isn't connected to anything. I don't understand what the problem there is, I can't show that this is related or not. I do know I'd rather have the functionality than a pretty form.

An ordinary ListView of all objects works normally, as does my DetailView. I have tried several times to re-write views.serp as a cbv with get_context_data, but either context or request or Searchbox() end up being undefined, or an ImportError.


Finally, a screenshot of my search page, with four spaces for the 4 entries that correctly correspond to the query 'eaa'. They just aren't there.






😪



“None of you has faith until he loves for his brother or his neighbor what he loves for himself.”

--
You received this message because you are subscribed to a topic in the Google Groups "Django users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-users/xSW1BjLXXSQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

James Schneider

unread,
Dec 26, 2017, 5:27:03 PM12/26/17
to django...@googlegroups.com



The template context says I have a queryset

I see it there. Can you post up the model for Entry?




And so does the selection

Yes. It appears that you will have 4 results in your queryset.



I put print statements in the view:

##########
Quit the server with CONTROL-C.
This is the value of query:  SearchQuery(eaa)
This is the value of QuerySet:  <QuerySet [('2017-10-09 Incentive, a further review', 0.151982), ('2017-11-14  Pulling The Trigger', 0.151982), ('2017-10-05 A Failure of Incentive?', 0.121585), ('2017-10-20 spider refactor', 0.121585)]>
[26/Dec/2017 18:57:31] "GET /serp/?q=eaa HTTP/1.1" 200 66580

##########

At this point, the view doesn't appear to be the problem. We need to start tracking things down in the template code.




Note this is all done through my simple html search form, defined in the template. My Django Form, SearchBox(), does absolutely nothing, as if it isn't connected to anything. I don't understand what the problem there is, I can't show that this is related or not. I do know I'd rather have the functionality than a pretty form.


No reason you can't have both, but let's work on the result display issue first.

An ordinary ListView of all objects works normally, as does my DetailView. I have tried several times to re-write views.serp as a cbv with get_context_data, but either context or request or Searchbox() end up being undefined, or an ImportError.


Are you overriding get_context_data() correctly? It doesn't sound like it.


Finally, a screenshot of my search page, with four spaces for the 4 entries that correctly correspond to the query 'eaa'. They just aren't there.

This is where the problem is, I think.

You should set up a simple loop in your template:

<ul>
{% for obj in QuerySet %}
<li>{{obj}}</li>
{% empty %}
<li>No results</li>
{% endfor %}

If that works, you should go deeper per your Entry model:


<ul>
{% for obj in QuerySet %}
<li>{{obj.name}}: {{obj.weight}}</li>
{% empty %}
<li>No results</li>
{% endfor %}

I'm using name and weight as an attribute of Entry, but you should use something that matches your model definition. I think the issue is that you are not referring to your model correctly within the search results within the template.

-James

Malik Rumi

unread,
Dec 26, 2017, 11:35:58 PM12/26/17
to django...@googlegroups.com
Here is the Entry model:

class Entry(models.Model):
    title = models.CharField(max_length=100, blank=False, null=False)
    slug = models.SlugField(max_length=100)
    content = models.TextField()
    posted_date = models.DateTimeField(auto_now=True)
    chron_date = models.DateField(auto_now=False, auto_now_add=False, blank=True)
    clock = models.TimeField(auto_now=False, auto_now_add=False, blank=True)

    ALIGN = "Align"
    CULTURE = "Culture"
    EXPENSE = "Expense"
    INCOME = "Income"
    HUMAN_RELATIONSHIPS = "Human Relationships"
    CATEGORY_CHOICES = (
        (ALIGN, "Align"),
        (CULTURE, "Culture"),
        (EXPENSE, "Expense"),
        (INCOME, "Income"),
        (HUMAN_RELATIONSHIPS, "Human Relationships"),
    )
    category = models.CharField(max_length=25, choices=CATEGORY_CHOICES, default=INCOME)
    tags = models.ManyToManyField(Tag)
    search_vector = SearchVectorField(blank=True, null=True)

    class Meta:
        verbose_name = "Diary Entry"
        verbose_name_plural = "Diary Entries"
        ordering = ["-chron_date", "clock"]
        indexes = [
            GinIndex(fields=['search_vector'])
        ]

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('detail', kwargs={"slug": self.slug})


You already have the template in my original post to start this thread.

“None of you has faith until he loves for his brother or his neighbor what he loves for himself.”

--
You received this message because you are subscribed to a topic in the Google Groups "Django users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-users/xSW1BjLXXSQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Malik Rumi

unread,
Dec 27, 2017, 12:10:32 AM12/27/17
to django...@googlegroups.com

Your first suggested loop:

This is the first page<br>

<ul>

{% for obj in QuerySet %}

<li>{{obj}}</li>

{% empty %}

<li>No results</li>

{% endfor %}

The result:

!DOCTYPE This is the first page

    • ('2017-10-09 Incentive, a further review', 0.151982)

    • ('2017-11-14 Pulling The Trigger', 0.151982)

    • ('2017-10-05 A Failure of Incentive?', 0.121585)

    • ('2017-10-20 spider refactor', 0.121585)


    Same template with your second loop added:


    !DOCTYPE


    <head></head>

    <html>

    <body>

    This is the first page<br>


    <ul>

    {% for obj in QuerySet %}

    <li>{{obj}}</li>

    {% empty %}

    <li>No results</li>

    {% endfor %}


    And now for something completely different...


    <ul>

    {% for obj in QuerySet %}

    <li>{{obj.title}}: {{obj.chron_date}}</li>

    <li>{{obj.content}}</li>

    {% empty %}

    <li>No results</li>

    {% endfor %}

    </body>

    </html>



    And the result:


    !DOCTYPE This is the first page

      • ('2017-10-09 Incentive, a further review', 0.151982)

      • ('2017-11-14 Pulling The Trigger', 0.151982)

      • ('2017-10-05 A Failure of Incentive?', 0.121585)

      • ('2017-10-20 spider refactor', 0.121585) And now for something completely different...

        • :

        • :

        • :

        • :


      <<>>


      So this looks like a variable problem, but I don't claim to understand that, either. I mentioned earlier on this thread that different variables gave me different results, but everyone says that can't be it.


      “None of you has faith until he loves for his brother or his neighbor what he loves for himself.”

      James Schneider

      unread,
      Dec 27, 2017, 12:20:22 AM12/27/17
      to django...@googlegroups.com



      And the result:


      !DOCTYPE This is the first page

      • ('2017-10-09 Incentive, a further review', 0.151982)

      • ('2017-11-14 Pulling The Trigger', 0.151982)

      • ('2017-10-05 A Failure of Incentive?', 0.121585)

      • ('2017-10-20 spider refactor', 0.121585) And now for something completely different...

        • :

        • :

        • :

        • :


      <<>>


      So this looks like a variable problem, but I don't claim to understand that, either. I mentioned earlier on this thread that different variables gave me different results, but everyone says that can't be it.



      Ah. Are you still chaining .values_list() to your query in the view? That would explain the output above.

      The loop is built with the assumption that we have full objects from the ORM, but because we chained .value_list(), we're actually getting a list of tuples containing two values.

      Your templates are built in the same manner. Can you run the second loop again with .values_list() removed from the query in the view?

      -James

      Malik Rumi

      unread,
      Dec 27, 2017, 12:32:42 AM12/27/17
      to django...@googlegroups.com
      That appears to be it. I got content and all that on the simple template this time. 

      I put values_list in there because that's the way it was done on the postgres full text search tutorial I was using. I assumed it was needed for rank, I never thought about it limiting me otherwise.
      If I seem a little less enthused than I should be, it is only because I am getting a little tired and hungry, and was emotionally prepared to put this whole thing aside and had started working on something else.
      But you have helped me tremendously, and I greatly appreciate it. Tomorrow I will play with it some more... unless my mood changes after I eat....  ;-)

      THANK YOU!!!!

      “None of you has faith until he loves for his brother or his neighbor what he loves for himself.”

      --
      You received this message because you are subscribed to a topic in the Google Groups "Django users" group.
      To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-users/xSW1BjLXXSQ/unsubscribe.
      To unsubscribe from this group and all its topics, send an email to django-users+unsubscribe@googlegroups.com.
      To post to this group, send email to django...@googlegroups.com.
      Visit this group at https://groups.google.com/group/django-users.
      Reply all
      Reply to author
      Forward
      0 new messages