Django redirect after search using a queryset - python

When I click search on the homepage, I want it to take that query set e.g (http://127.0.0.1:8000/?q=car) and then use the same url, but in the search view. I have tried searching around but couldn't find anything that was working.
Views:
class IndexView(ListView):
model = Post
# queryset = Post.objects.filter(live=True)
template_name = "public/index.html"
def get_queryset(self):
queryset = super().get_queryset().filter(live=True)
query = self.request.GET.get("q")
if query:
queryset = queryset.filter(title__icontains=query)
return redirect(reverse('search-view'), queryset)
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['queryset'] = self.get_queryset()
context['category'] = Category.objects.all()
return context
URLS:
urlpatterns = [
path('', views.IndexView.as_view(), name="index-view"),
path('post/create/', views.PostCreateView.as_view(), name="post-create"),
path('post/<slug>/update/', views.PostUpdateView.as_view(), name="post-update"),
path('post/<slug>/', views.PostDetailView.as_view(), name="post-detail"),
path('category/', views.CategoryView.as_view(), name="category"),
path('category/<int:pk>/', views.CategoryDetailView.as_view(), name="category-detail"),
path('search/', views.SearchListView.as_view(), name="search-view")
]
I have tried doing it using a redirect and reverse but it is not working at all, it is actually giving me an error for using slice on a forloop that is using queryset. 'slice' object has no attribute 'lower'
I have 2 templates/views. When I click search on the homepage, I want it to carry on over to the search view and then run the search query on there.
Thanks.

I think you're mixing up the logic a bit on where to do the actual query. You should't do the actual query search in the IndexView that is meant for the SearchListView.
From the information that's available right now (without the SearchListView) I'd say you could do a redirect and pass the parameter onto the query url and let the view in SearchListView decide what to do with the information:
def get_queryset(self):
queryset = super().get_queryset().filter(live=True)
query = self.request.GET.get("q")
if query:
# Fetch the url for the search-view
base_url = reverse('search-view')
# Make the search query url encoded
query_string = urlencode({'q': query})
# Tie it together with the url
url = '{}?{}'.format(base_url, query_string)
# Fire away
return redirect(url)
return redirect(reverse('search-view'), queryset)
Source:
Some code taken from The Ultimate Guide to Django Redirects by Daniel Hepper

Related

Django_Filters and queryset

Hi guys I'm building a simple django website where the user can look for long play. The problem right now is that I can't figure out why the filter, built with the django_filters library, doesn't work.
If you type "Queen" in the search bar in the home you are redirected to the searches.html where are listed all the LPs by Queen (just 4). On the left there's a filter. If you type "kind" in the form and submit then you should just see the album "Kind of Magic". This doesn't work. And I think the problem is the queryset "sp":
class searchesView(TemplateView):
template_name = "search/searches.html"
def post(self, request, *args, **kwargs):
print('FORM POSTED WITH {}'.format(request.POST['srh']))
srch = request.POST.get('srh')
if srch:
sp = Info.objects.filter(Q(band__icontains=srch)) | Info.objects.filter(Q(disco__icontains=srch))
myFilter = InfoFilter(request.GET, queryset=sp)
sp = myFilter.qs
paginator = Paginator(sp, 10)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(self.request, 'search/searches.html', {'sp':sp, 'myFilter':myFilter,
'page_obj': page_obj
})
else:
return render(self.request, 'search/searches.html')
I think the problem is the queryset because then I tried to build a result.html page where I listed all the LPs in the database and applied the same filter above and here it works perfectly:
def result(request):
result = Info.objects.all()
myFilter = InfoFilter(request.GET, queryset=result)
result = myFilter.qs
return render (request, 'search/result.html', {'result':result, 'myFilter':myFilter})
The difference is that in the first case I've got a TemplateView with a certain queryset, and in the second case I've got a simple function with a different queryset. Someone know what the problem could be? I uploaded to code on repl if you want to try out the website: https://Django-Template.edwardradical.repl.co
Thanks a lot!
Issue has to do with the mix of POST/GET here.
You are reading parameters out of both post/get dictionaries here, which is not a great pattern to go down.
In this case, a POST request to the url with the filters should yield the correct results ie:
POST /searches/?band=kind&disco=
{
srh: data
}
In current solution, I believe the srh is not included in the post body, which causes issue in queryset.

Django Error NoReverseMatch because it's missing a pk

So currently I'm on a page with a url link like this
urls.py :
path('key/<int:pk>', views.KeyDetailView.as_view(), name='roomkey-detail'),
views.py :
class KeyDetailView(generic.DetailView):
model = RoomKey
this lists out a list of keys available to be borrowed for a particular room. Then when I try to head to the next page, where is a request I can make to borrow out one of the keys for that room, Here is the urls and views responsible for rendering the roomkey-request page
urls.py :
path('key/<int:pk>/request', views.KeyRequestCreate.as_view(), name='roomkey-request')
views.py :
class KeyRequestCreate(CreateView):
model = KeyRequest
fields = ['roomkey', 'requester', 'borrower', 'request_comments']
template_name = 'catalog/roomkey_request_form.html'
there is a button that on that page that links to a terms and agreement page that looks like this
roomkey_request_form.html :
terms and conditions
urls.py :
path('key/<int:pk>/request/agreement', views.KeyAgreement, name='key-agreement'),
views.py :
def KeyAgreement(request):
return render(
request,
'catalog/roomkey_agreement.html',
)
however when try to click on that request button to request a key, django throws an error
NoReverseMatch at /catalog/key/2/request
Reverse for 'key-agreement' with arguments '('',)' not found. 1 pattern(s)
tried: ['catalog\\/key\\/(?P<pk>[0-9]+)\\/request\\/agreement$']
I have a button on the terms and agreement to go back to the request page something that looks like this
<button href="{% url 'roomkey-request' roomkey.pk %}" >Return to request</button>
will this return to the request page with the correct pk? I think i'm just confused with how the url handles pk and how it get's passed on.I am thinking this had to do with something the keyagreement not being able to take in that pk from the details page, can someone please explain to me what I am doing wrong or point me to some resource that can help me understand how urls pass along the pk from view to view? I am fairly new to django so thanks for your help in advance!!
Try:
def KeyAgreement(request, pk): #-->pk in argument
return render(
request,
'catalog/roomkey_agreement.html',
)
If you want to use roomkey.pk in the roomkey_request_form.html template, you need to add roomkey to the template context. You can do this in the get_context_data method.
Since you already have the roomkey pk from the URL, you can remove it from fields. Then set roomkey in the form_valid method before saving.
class KeyRequestCreate(CreateView):
model = KeyRequest
fields = ['requester', 'borrower', 'request_comments']
template_name = 'catalog/roomkey_request_form.html'
def get_context_data(self, **kwargs):
context = super(KeyRequestCreate, self).get_context_data(**kwargs)
context['roomkey'] = get_object_or_404(RoomKey, pk=pk)
def form_valid(self, form):
form.instance.roomkey = get_object_or_404(RoomKey, pk=pk)
return super(KeyRequestCreate, self).get_form(form)
If you want to use roomkey in the agreement view, you'll have to make some changes to it as well.
First, you need to add pk to the function signature since you have <int:pk> in its URL pattern.
Then, you need to include roomkey in the template context.
from django.shortcuts import get_object_or_404
def key_agreement(request, pk):
roomkey = get_object_or_404(roomkey, pk=pk)
return render(
request,
'catalog/roomkey_agreement.html',
{'roomkey': roomkey}
)
Note that I've renamed the view function to key_agreement to match the recommended style. You'll need to update the URL pattern as well.
path('key/<int:pk>/request/agreement', views.KeyAgreement, name='key-agreement'),

How to change context in Django TemplateView based on url

I am stuck with an automation problem in that I have a django project with multiple subdomains, one for each council model instance. All of my models have a foreignfield of 'council' and that is how I am filtering the context of the sites by creating a separate view for each council that I add.
However I would like to somehow create a way to automate adding a view class with the right filtered context each time I add a model instance (Council).
Also I will need to add another url in the urlpatterns which again I am stumped maybe a bit of python could help there but the views are my main concern.
in my views:
class RedmarleyPageView(TemplateView):
template_name = "index.html"
def get_context_data(self, **kwargs):
council_no = 1
context = super (RedmarleyPageView, self).get_context_data (**kwargs)
context['members'] = Member.objects.filter (council=council_no)
context['roles'] = Role.objects.filter (council=council_no)
context['minutes'] = Minute.objects.filter (council=council_no)
context['agendas'] = Agenda.objects.filter (council=council_no)
context['documents'] = Document.objects.filter (council=council_no)
context['articles'] = Article.objects.filter (council=council_no)
context['councils'] = Council.objects.filter (id=council_no)
context['settings'] = Setting.objects.filter(council=council_no)
context['events'] = Event.objects.filter(council=council_no)
context['eventscategories'] = EventCategory.objects.all()
return context
urls:
url(r'^$', HomePageView.as_view(), name='home'),
url(r'^redmarley/', RedmarleyPageView.as_view(), name='redmarley'),
Redmarley in this context is the council inst. and I need it to be able to create a view and url like above for each instance.

Redirects clashing with URL patterns Django

I'm trying to get Django's redirects app working, it works with some of the redirects I have in the database, but not all of them.
The ones that match with one of my url patterns 404 without redirecting, the rest are fine.
urlpatterns = [
...
url(r'^(?P<category>[-\w]+)/$', views.SubCategoryListView.as_view(),
name='category_list'),
url(r'^(?P<category>[-\w]+)/(?P<slug>[-\w]+)/$',
views.content_or_sub_category, name='choice')
...
]
For example, say the URL 'example.com/foo/bar' is supposed to redirect. It would match the second url pattern above, get sent to content_or_sub_category, which checks whether 'foo' is a valid category or not, and if 'bar' is a valid slug. Upon finding that they're not, it 404s (as in the code below) and doesn't redirect.
In the docs it says
Each time any Django application raises a 404 error, this middleware checks the redirects database for the requested URL as a last resort.
https://docs.djangoproject.com/en/1.8/ref/contrib/redirects/
But instead of redirects kicking in any time a 404 is raised it appears to only happen if Django doesn't find a matching pattern.
Is that how it's supposed to be behaving?
I found this question Raise 404 and continue the URL chain about how to resume evaluating the rest of the url patterns if one 404s, but it looks like that's either something you can't or shouldn't do.
The only answer on that question suggested putting some logic in your urls.py, but that would need to check the validity of the URL, and then make the urlpatterns list according to whether it's valid or not. I googled and couldn't find a way to do that from within urls.py.
I'm using Django 1.8.
class SubCategoryListView(ListView):
model = Content
queryset = Content.objects.published()
paginate_by = 15
def get_queryset(self):
qs = super(SubCategoryListView, self).get_queryset()
if 'category' in self.kwargs:
if self.kwargs['category'] is not None:
qs = qs.filter(
categories__slug__contains=self.kwargs['category'])
return qs
def get_context_data(self, **kwargs):
context = super(SubCategoryListView, self).get_context_data(**kwargs)
if 'category' in self.kwargs and self.kwargs['category'] is not None:
context['category'] = get_object_or_404(Category,
slug=self.kwargs[
'category'])
return context
...
def content_or_sub_category(self, **kwargs):
sub_category = get_object_or_false(Category.objects.sub_categories(),
slug=kwargs['slug'])
content = get_object_or_false(Content.objects.published(),
slug=kwargs['slug'])
if content:
return ContentDetailView.as_view()(self, **kwargs)
if sub_category:
return ContentListView.as_view()(self,
**{'category': kwargs['category'],
'sub_category': kwargs['slug']})
raise Http404
Some redirects that don't work:
/46421 --> /economy/has-prime-minister-broken-promise-tax-credits/
/are-living-standards-on-the-rise --> /economy/are-living-standards-rise/
/articles/nhs_budget_cut-28646 --> /economy/has-nhs-budget-been-cut/
But something like this does work:
/health/live/2015/jan/number_12_hour_wait_hospital_bed_accident_emergency-38409 --> /health/spike-numbers-waiting-12-hours-e-hospital-bed/
Not sure what return ContentDetailView.as_view()(self, **kwargs) is, but in general if you want to redirect, you would use something along the lines of:
return redirect(reverse('category_list', args=[ARG1, ARG2]))
Where "category_list" is the name of the url pattern, and ARG1 & ARG2 are the arguments you want in the url.

Url Not Found Django

So I'm having a bit of trouble. I get a 404 when I try to visit the url for a certain model:
url(r'^rewards/(?P<slug>[-\w]+)/$', RedeemReward.as_view(), name="reward"),
url(r'^rewards/(?P<slug>[-\w]+)/$', CompanyDetail.as_view(), name="company"),
So the top url will be something like rewards/amazon-gift card, while the bottom url would be something like rewards/amazon (to show all of the gift cards that amazon has to offer). The reward url works as expected, but I get a 404 when I try to visit the bottom url. The view:
class CompanyDetail(DetailView):
model = Company
context_object_name = 'company'
slug_field = 'company_slug'
template_name = 'asdx/companies.html'
def get_rewards(self):
rewards = Reward.objects.filter(company=self.object)
return rewards
def get_context_data(self, **kwargs):
context = super(CompanyDetail, self).get_context_data(**kwargs)
context['rewards'] = self.get_rewards()
return context
What's going on here?
Your patterns for the two views are identical, so the CompanyDetail view can never be called. Instead, the pattern for RedeemReward matches all slugs and raises a 404 for slugs that do not match whatever its model class is. (Probably Reward.) Put something in the URLs to differentiate company URLs from reward URLs.

Categories

Resources