ValueError: The view blog.views.post_search didn't return an HttpResponse object. It returned None instead

433 Views Asked by At

I'm implementing "search function" to my django blog, using Solr and Haystack. In "http://127.0.0.1:8000/blog/search/", it says TypeError at /blog/search/ post_detail() missing 3 required positional arguments: 'year', 'month', and 'day' Also in "http://localhost:8000/blog/search", it says ValueError at /blog/search The view blog.views.post_search didn't return an HttpResponse object. It returned None instead.

  • blog/forms.py

    from django import forms
    
    class SearchForm(forms.Form):
        query = forms.CharField()

  • blog/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')
    
        def get_model(self):
            return Post
    
        def index_queryset(self, using=None):
            return self.get_model().published.all()

  • blog/urls.py

    from blog.feeds import LatestPostsFeed
    from django.conf.urls import url
    from django.urls import include, path
    from . import views
    
    app_name = 'blog'
    
    urlpatterns = [
        path('search', views.post_search, name='post_search'),
    ]

  • blog/views.py

    from .forms import SearchForm
    from haystack.query import SearchQuerySet
    
    def post_search(request):
        form = SearchForm()
        if 'query' in request.GET:
            form = SearchForm(request.GET)
            if form.is_valid():
                cd = form.cleaned_data
                results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
                # count total results
                total_results = results.count()
                return render(request, 'blog/post/search.html', {'form': form, 'cd': cd, 'results': results, 'total_results': total_results})

  • blog/templates/blog/post/search.html

    {% extends "blog/base.html" %}
    
    {% block title %}Search{% endblock %}
    
    {% block content %}
        {% if "query" in request.GET %}
            <h1>Posts containing "{{ cd.query }}"</h1>
            <h3>Found {{ total_results }} result{{ total_results|pluralize}}</h3>
            {% for result in results %}
                {% with post=result.object %}
                    <h4><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h4>
                    {{ post.body|truncatewords:5 }}
                {% endwith %}
            {% empty %}
                <p>There are no results for your query.</p>
            {% endfor %}
            <p><a href="{% url 'blog:post_search' %}">Search again</a></p>
        {% else %}
            <h1>Search for posts</h1>
            <form action="." method="get">
                {{ form.as_p }}
                <input type="submit" value="Search">
            </form>
        {% endif %}
    {% endblock %}

  • blog/templates/search/indexes/blog/post_text.txt

    {{ object.title }}
    {{ object.tags.all|join:", " }}
    {{ object.body }}


Edit 1

  • blog/urls.py

        from blog.feeds import LatestPostsFeed
        from django.conf.urls import url
        from django.urls import include, path
        from . import views
        
        app_name = 'blog'
        
        urlpatterns = [
            path('', views.post_list, name='post_list'),
            url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<slug>[-\w]+)/$', views.post_detail, name='post_detail'),
            url(r'^(?P<post_id>\d+)/share/$', views.post_share, name='post_share'),
            path('<slug:slug>/', views.post_detail, name='post_detail'),
            url(r'^tag/(?P<tag_slug>[-\w]+)/$', views.post_list, name='post_list_by_tag'),
            path('feed', LatestPostsFeed(), name='post_feed'),
            url(r'^search/$', views.post_search, name='post_search'),
        ]
    ```
    
    - blog/views.py
    ```python
    
        from django.shortcuts import render, get_object_or_404
        from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
        from django.core.mail import send_mail
        from django.views.generic import ListView
        from django.db.models import Count
        from taggit.models import Tag
        from .models import Post, Comment
        from .forms import EmailPostForm, CommentForm, SearchForm
        from haystack.query import SearchQuerySet
        
        def post_list(request, tag_slug=None):
            object_list = Post.published.all()
            tag = None
        
            if tag_slug:
                tag = get_object_or_404(Tag, slug=tag_slug)
                object_list = object_list.filter(tags__in=[tag])
            paginator = Paginator(object_list, 3) # 3 posts in each page
            page = request.GET.get('page')
            try:
                posts = paginator.page(page)
            except PageNotAnInteger:
                # If page is not an integer deliver the first page
                posts = paginator.page(1)
            except EmptyPage:
                # If page is out of range deliver last page of results
                posts = paginator.page(paginator.num_pages)
            return render(request, 'blog/post/list.html', {'page': page, 'posts': posts, 'tag': tag})
        
        def post_detail(request, year, month, day, slug):
            post = get_object_or_404(Post, slug=slug, status='published', publish__year=year, publish__month=month, publish__day=day)
            # List of active comments for this post
            comments = post.comments.filter(active=True)
            new_comment = None
            # Comment posted
            if request.method == 'POST':
                # A comment was posted
                comment_form = CommentForm(data=request.POST)
                if comment_form.is_valid():
                    # Create Comment object but don't save to database yet
                    new_comment = comment_form.save(commit=False)
                    # Assign the current post to the comment
                    new_comment.post = post
                    # Save the comment to the database
                    new_comment.save()
        
            else:
                comment_form = CommentForm()
                
            # List of similar posts
            post_tags_ids = post.tags.values_list('id', flat=True)
            similar_posts = Post.published.filter(tags__in=post_tags_ids).exclude(id=post.id)
            similar_posts = similar_posts.annotate(same_tags=Count('tags')).order_by('-same_tags', '-publish')[:4]
        
            return render(request, 'blog/post/detail.html', {'post': post, 'comments': comments, 'comment_form': comment_form, 'similar_posts': similar_posts})
        
        def post_share(request, post_id):
            # Retrieve post by id
            post = get_object_or_404(Post, id=post_id, status='published')
            sent = False
        
            if request.method == 'POST':
                # Form was submitted
                form = EmailPostForm(request.POST)
                if form.is_valid():
                    # Form fields passed validation
                    cd = form.cleaned_data
                    post_url = request.build_absolute_uri(post.get_absolute_url())
                    subject = '{} ({}) recommends you reading "{}"'.format(cd['name'], cd['email'], post.title)
                    message = 'Read "{}" at {}\n\n{}\'s comments: {}'.format(post.title, post_url, cd['name'], cd['comments'])
                    send_mail(subject, message, '[email protected]', [cd['to']])
                    sent = True
            else:
                form = EmailPostForm()
            return render(request, 'blog/post/share.html', {'post': post, 'form': form, 'sent': sent})
        
        def post_search(request):
            form = SearchForm()
            if 'query' in request.GET:
                form = SearchForm(request.GET)
                if form.is_valid():
                    cd = form.cleaned_data
                    results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
                    # count total results
                    total_results = results.count()
            return render(request, 'blog/post/search.html', {'form': form, 'cd': cd, 'results': results, 'total_results': total_results})
        
        class PostListView(ListView):
            queryset = Post.published.all()
            context_object_name = 'posts'
            paginate_by = 3
            template_name = 'blog/post/list.html'
    
    ```
    
    # Edit 2
    
    - views.py
    ```python
    from django.shortcuts import render, get_object_or_404
    from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
    from django.core.mail import send_mail
    from django.views.generic import ListView
    from django.db.models import Count
    from taggit.models import Tag
    from .models import Post, Comment
    from .forms import EmailPostForm, CommentForm, SearchForm
    from haystack.query import SearchQuerySet
    
    def post_list(request, tag_slug=None):
        object_list = Post.published.all()
        tag = None
    
        if tag_slug:
            tag = get_object_or_404(Tag, slug=tag_slug)
            object_list = object_list.filter(tags__in=[tag])
        paginator = Paginator(object_list, 3) # 3 posts in each page
        page = request.GET.get('page')
        try:
            posts = paginator.page(page)
        except PageNotAnInteger:
            # If page is not an integer deliver the first page
            posts = paginator.page(1)
        except EmptyPage:
            # If page is out of range deliver last page of results
            posts = paginator.page(paginator.num_pages)
        return render(request, 'blog/post/list.html', {'page': page, 'posts': posts, 'tag': tag})
    
    def post_detail(request, year, month, day, slug):
        post = get_object_or_404(Post, slug=slug, status='published', publish__year=year, publish__month=month, publish__day=day)
        # List of active comments for this post
        comments = post.comments.filter(active=True)
        new_comment = None
        # Comment posted
        if request.method == 'POST':
            # A comment was posted
            comment_form = CommentForm(data=request.POST)
            if comment_form.is_valid():
                # Create Comment object but don't save to database yet
                new_comment = comment_form.save(commit=False)
                # Assign the current post to the comment
                new_comment.post = post
                # Save the comment to the database
                new_comment.save()
    
        else:
            comment_form = CommentForm()
            
        # List of similar posts
        post_tags_ids = post.tags.values_list('id', flat=True)
        similar_posts = Post.published.filter(tags__in=post_tags_ids).exclude(id=post.id)
        similar_posts = similar_posts.annotate(same_tags=Count('tags')).order_by('-same_tags', '-publish')[:4]
    
        return render(request, 'blog/post/detail.html', {'post': post, 'comments': comments, 'comment_form': comment_form, 'similar_posts': similar_posts})
    
    def post_share(request, post_id):
        # Retrieve post by id
        post = get_object_or_404(Post, id=post_id, status='published')
        sent = False
    
        if request.method == 'POST':
            # Form was submitted
            form = EmailPostForm(request.POST)
            if form.is_valid():
                # Form fields passed validation
                cd = form.cleaned_data
                post_url = request.build_absolute_uri(post.get_absolute_url())
                subject = '{} ({}) recommends you reading "{}"'.format(cd['name'], cd['email'], post.title)
                message = 'Read "{}" at {}\n\n{}\'s comments: {}'.format(post.title, post_url, cd['name'], cd['comments'])
                send_mail(subject, message, '[email protected]', [cd['to']])
                sent = True
        else:
            form = EmailPostForm()
        return render(request, 'blog/post/share.html', {'post': post, 'form': form, 'sent': sent})
    
    def post_search(request):
        form = SearchForm()
        if 'query' in request.GET:
            form = SearchForm(request.GET)
            if form.is_valid():
                cd = form.cleaned_data
                results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
                # count total results
                total_results = results.count()
            return render(request, 'blog/post/search.html', {'form': form, 'cd': cd, 'results': results, 'total_results': total_results})
        return render(request, 'blog/post/search.html', {'form': form})
    
    class PostListView(ListView):
        queryset = Post.published.all()
        context_object_name = 'posts'
        paginate_by = 3
        template_name = 'blog/post/list.html'

  • urls.py

    from blog.feeds import LatestPostsFeed
    from django.conf.urls import url
    from django.urls import include, path
    from . import views
    
    app_name = 'blog'
    
    urlpatterns = [
        path('', views.post_list, name='post_list'),
        path('<int:year>/<int:month>/<int:day>/<slug:slug>/', views.post_detail, name='post_detail'),
        path('<int:post_id>/share/', views.post_share, name='post_share'),
        path('tag/<slug:tag_slug>/', views.post_list, name='post_list_by_tag'),
        path('feed/', LatestPostsFeed(), name='post_feed'),
        path('search/', views.post_search, name='post_search'),
    ]

2

There are 2 best solutions below

4
Lars On

Try to do it like :-

    def post_search(request):
        form = SearchForm()
        if 'query' in request.GET:
            form = SearchForm(request.GET)
            if form.is_valid():
                cd = form.cleaned_data
                results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
                # count total results
                total_results = results.count()
#Changed some indention Here
        return render(request, 'blog/post/search.html', {'form': form, 'cd': cd, 'results': results, 'total_results': total_results})

What have i changed :-

You were passing the return render within if statement so Django assumed that return render is the part of if starement.

Edit-1

You're passing "year", "month" and "day" in parameter for get the posts according to year ? I have no idea why you're passing them as parameter.

You're trying to show a particular post in a post_detail page but You're not passing which post you're trying to open, I mean you're not passing the Post's id, so Django will never know, which post you want to see.

You can do it like :-

urls.py

path('post_detail/<slug:slug>/<int:post_id>/, views.post_detail, name='post_detail'),

views.py

    def post_detail(request, post_id, slug):
        post = get_object_or_404(Post,pk=post_id)
        # List of active comments for this post

        ......
        ..........

And Try to remove the parameters "year", "month" and "day"

0
ecrire06 On

I looked close to my code and found out that the key to solve the error was to add the return once again.


    def post_search(request):
        form = SearchForm()
        if 'query' in request.GET:
            form = SearchForm(request.GET)
            if form.is_valid():
                cd = form.cleaned_data
                results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
                # count total results
                total_results = results.count()
            return render(request, 'blog/post/search.html', {'form': form, 'cd': cd, 'results': results, 'total_results': total_results})
        return render(request, 'blog/post/search.html', {'form': form})

by adding "return render(request, 'blog/post/search.html', {'form': form})" after the if ends.