spacekoki
spacekoki

Reputation: 137

How to place search query on url on django?

I am creating a search application with Django.

I made an article model and a Feedback model that records the rating of articles.

After entering search box and displaying the search results, click one of the results then goes to the detail screen.

After selecting feedback on the detail screen and pressing the submit button, I want to save a search query to the feedback model.

I think that solution is to add a query in the URL like portal/search/?=query and read it, but I don't know how to code it. Also, could you teach me if there is an implementation method other than reading query in the URL?

Also, when I go back from the detail screen, I want to display the previous search results too.

Please comment if you have any questions. Forgive for my poor English.

models.py

from django.db import models
from django.urls import reverse
from taggit.managers import TaggableManager

class KnowHow(models.Model):

    BASIC_TAGS =(
        ('1','one'),
        ('2','two'),
        ('3','three'),
        ('4','four'),
        ('5','five'),
        ('6','six'),
    )

    CATEGORY =(
        ('1','Type2'),
        ('2','Type1'),
    )


    author = models.ForeignKey('auth.User',on_delete=models.CASCADE)
    category = models.CharField(max_length=1,choices=CATEGORY,default='1')
    title = models.CharField(max_length=200)
    text = models.TextField(blank=True,default=' ')
    # delault=' ':import system will give a error if text column is null
    file = models.FileField(blank=True,upload_to='explicit_knowhows')
    basic_tag = models.CharField(max_length=1,choices=BASIC_TAGS,default='1')
    free_tags = TaggableManager(blank=True)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('portal:index')


class Feedback(models.Model):

    EFFECT =(
        ('1','great'),
        ('2','maybe good'),
        ('3','bad'),
    )
    NOVEL =(
        ('1','I didn't know that'),
        ('2','I know, but I forgot'),
        ('3','I know this.'),
    )

    kh = models.ForeignKey(KnowHow, on_delete=models.PROTECT)
    user = models.ForeignKey('auth.User',on_delete=models.CASCADE)
    query = models.TextField(blank=True)
    time = models.DateTimeField(auto_now_add=True)
    efficacy = models.CharField(max_length=1,choices=EFFECT,default='1')
    novelty = models.CharField(max_length=1,choices=NOVEL,default='1')


    def __str__(self):
        return self.time.strftime("%Y/%m/%d %H:%M:%S")

views.py

from django.urls import reverse, reverse_lazy
from django.http import HttpResponse
from django.views import generic
from django.views.generic.edit import ModelFormMixin
from django.shortcuts import redirect,get_object_or_404
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from .models import KnowHow
from taggit.models import Tag
from .forms import SearchForm,FeedbackForm
from django.db.models import Q

"""
Django Auth
The LoginRequired mixin
https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-loginrequired-mixin
The login_required decorator
https://docs.djangoproject.com/en/2.0/topics/auth/default/#the-login-required-decorator
@login_required
"""


class IndexView(LoginRequiredMixin,generic.list.ListView):
    model = KnowHow
    #paginate_by = 5
    ordering = ['-title']
    # template_name = 'portal/KnowHow_list.html'


class DetailView(ModelFormMixin,LoginRequiredMixin,generic.detail.DetailView):
    # from https://torina.top/detail/337/
    model = KnowHow
    form_class = FeedbackForm
    template_name = 'portal/KnowHow_detail.html'

    def form_valid(self, form):
        kh_pk = self.kwargs['pk']
        Feedback = form.save(commit=False)
        Feedback.kh = get_object_or_404(KnowHow, pk=kh_pk)
        Feedback.query=""
        Feedback.user=self.request.user
        Feedback.save()
        return redirect('portal:search')

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            self.object = self.get_object()
            return self.form_invalid(form)


class CreateView(LoginRequiredMixin, generic.edit.CreateView):  # The LoginRequired mixin
    model = KnowHow
    fields = ['category','title','text','file','basic_tag','free_tags'] 

    #template_name = 'portal/KnowHow_form.html'
    def form_valid(self, form):
        # This method is called when valid form data has been posted.
        # It should return an HttpResponse.
        # https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user
        form.instance.author = self.request.user
        return super(CreateView, self).form_valid(form)


class UpdateView(LoginRequiredMixin, generic.edit.UpdateView):  # The LoginRequired mixin
    model = KnowHow
    fields = ['category','title','text','file','basic_tag','free_tags']

    #template_name = 'portal/KnowHow_form.html'




class DeleteView(LoginRequiredMixin, generic.edit.DeleteView):  # The LoginRequired mixin
    model = KnowHow
    success_url = reverse_lazy('portal:index')

    def delete(self, request, *args, **kwargs):
        result = super().delete(request, *args, **kwargs)
        Tag.objects.filter(knowhow=None).delete()
        return result
    #template_name = 'portal/KnowHow_confirm_delete.html'

class SearchIndexView(LoginRequiredMixin, generic.ListView):

    template_name="search/search_index.html"
    model = KnowHow

    def post(self, request, *args, **kwargs):

        form_value = [
            self.request.POST.get('basic_tag', None),
            self.request.POST.get('free_tags', None),
        ]
        request.session['form_value'] = form_value

        self.request.GET = self.request.GET.copy()
        self.request.GET.clear()

        return self.get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        basic_tag = ''
        free_tags = ''
        if 'form_value' in self.request.session:
            form_value = self.request.session['form_value']
            basic_tag = form_value[0]
            free_tags = form_value[1]

        default_data = {'basic_tag': basic_tag,
                        'free_tags': free_tags,
                        }

        test_form = SearchForm(initial=default_data)
        context['test_form'] = test_form

        return context

    def get_queryset(self):

        if 'form_value' in self.request.session:
            form_value = self.request.session['form_value']
            basic_tag = form_value[0]
            free_tags = form_value[1]

            condition_basic_tag = Q()
            condition_free_tags = Q()

            if len(basic_tag) != 0 and basic_tag[0]:
                condition_basic_tag = Q(basic_tag=basic_tag)
            if len(free_tags) != 0 and free_tags[0]:
                condition_free_tags = Q(free_tags__name__in=free_tags)

            return KnowHow.objects.filter(condition_basic_tag & condition_free_tags).distinct()
        else:
            return KnowHow.objects.none()


@login_required
def help(request):
    return HttpResponse("Member Only Help Page")

urls.py

from django.urls import path

from . import views
# set the application namespace
# https://docs.djangoproject.com/en/2.0/intro/tutorial03/
app_name = 'portal'

urlpatterns = [
    # ex: /
    path('', views.IndexView.as_view(), name='index'),

    # ex: /KnowHow/create/
    path('KnowHow/create/', views.CreateView.as_view(), name='create'),

    # ex: /KnowHow/1/
    path('KnowHow/<int:pk>/detail/', views.DetailView.as_view(), name='detail'),

    # ex: /KnowHow/1/update/
    path('KnowHow/<int:pk>/update/', views.UpdateView.as_view(), name='update'),

    # ex: /KnowHow/1/delete
    path('KnowHow/<int:pk>/delete/', views.DeleteView.as_view(), name='delete'),

    # ex: /KnowHow/help/
    path('KnowHow/help/', views.help, name='help'),

    path('search/',views.SearchIndexView.as_view(), name='search')
]

Upvotes: 0

Views: 4178

Answers (2)

spacekoki
spacekoki

Reputation: 137

I changed get_context_data function in SearchIndexView to this: in the last line before return add these two lines

context['basic_tag'] = basic_tag
context['free_tags'] = free_tags

And I changed html too.

<a href="{% url 'portal:detail' KnowHow.pk %}{{current_query}}">{{ KnowHow.title }}</a>

Thanks, @n1ma

Upvotes: 0

nima
nima

Reputation: 1645

There are several solutions for your problem.

  1. First one is the exact solution you mentioned yourself. using a query string parameter like ?q= for KnowHow details view.
  2. Using a SearchLog model and using that model's identifier. When someone hits the /search/ endpoint, you create a new SearchLog and pass the pk for this record to your front. Basically it would be just like ?q= option. instead you can use ?search_id= to bind the feedback to an specific SearchLog
  3. Use user sessions. Bind the searched query to user's session and when they want to create a new Feedback use the query in their session.

For the first two options, you just need to create your urls for the detail links properly (in your search result page). In your template, do something like below:

# You are probably doing something like this
{% for r in results %}
<a href="{{r.absolute_url}}">{{r.name}}</a>
{% endfor %}

# You should do this instead
{% for r in results %}
<a href="{{r.absolute_url}}{{current_query}}">{{r.name}}</a>
{% endfor %}

You can either pass the current_query in your context when rendering the template, or use javascript to get that value from browser's location / query string.

Upvotes: 2

Related Questions