Django Gaurdian + Haystack search results permissions - python

I am trying to limit the results a user returns during a search based on permissions in django guardian... I have tried many things, but i am going in circles. Any advice would be appreciated :
Scenario : user should only see search results in which they have view_product permissions to.
In my search template i added :
{% load guardian_tags %}
{% get_obj_perms request.user for product as "product_perms" %}
{% if "view_product" in product_perms %}
Which results in Exception Type: VariableDoesNotExist
haystack urls.py
from __future__ import unicode_literals
try:
from django.conf.urls import patterns, url
except ImportError:
from django.conf.urls.defaults import patterns, url
from haystack.views import SearchView
urlpatterns = patterns('haystack.views',
url(r'^$', SearchView(), name='haystack_search'),
)
haystack views.py
from __future__ import unicode_literals
from django.conf import settings
from django.core.paginator import Paginator, InvalidPage
from django.http import Http404
from django.shortcuts import render_to_response
from django.template import RequestContext
from haystack.forms import ModelSearchForm, FacetedSearchForm
from haystack.query import EmptySearchQuerySet
RESULTS_PER_PAGE = getattr(settings, 'HAYSTACK_SEARCH_RESULTS_PER_PAGE', 20)
class SearchView(object):
template = 'search/search.html'
extra_context = {}
query = ''
results = EmptySearchQuerySet()
request = None
form = None
results_per_page = RESULTS_PER_PAGE
def __init__(self, template=None, load_all=True, form_class=None, searchqueryset=None, context_class=RequestContext, results_per_page=None):
self.load_all = load_all
self.form_class = form_class
self.context_class = context_class
self.searchqueryset = searchqueryset
if form_class is None:
self.form_class = ModelSearchForm
if not results_per_page is None:
self.results_per_page = results_per_page
if template:
self.template = template
def __call__(self, request):
"""
Generates the actual response to the search.
Relies on internal, overridable methods to construct the response.
"""
self.request = request
self.form = self.build_form()
self.query = self.get_query()
self.results = self.get_results()
return self.create_response()
def build_form(self, form_kwargs=None):
"""
Instantiates the form the class should use to process the search query.
"""
data = None
kwargs = {
'load_all': self.load_all,
}
if form_kwargs:
kwargs.update(form_kwargs)
if len(self.request.GET):
data = self.request.GET
if self.searchqueryset is not None:
kwargs['searchqueryset'] = self.searchqueryset
return self.form_class(data, **kwargs)
def get_query(self):
"""
Returns the query provided by the user.
Returns an empty string if the query is invalid.
"""
if self.form.is_valid():
return self.form.cleaned_data['q']
return ''
def get_results(self):
"""
Fetches the results via the form.
Returns an empty list if there's no query to search with.
"""
return self.form.search()
def build_page(self):
"""
Paginates the results appropriately.
In case someone does not want to use Django's built-in pagination, it
should be a simple matter to override this method to do what they would
like.
"""
try:
page_no = int(self.request.GET.get('page', 1))
except (TypeError, ValueError):
raise Http404("Not a valid number for page.")
if page_no < 1:
raise Http404("Pages should be 1 or greater.")
start_offset = (page_no - 1) * self.results_per_page
self.results[start_offset:start_offset + self.results_per_page]
paginator = Paginator(self.results, self.results_per_page)
try:
page = paginator.page(page_no)
except InvalidPage:
raise Http404("No such page!")
return (paginator, page)
def extra_context(self):
"""
Allows the addition of more context variables as needed.
Must return a dictionary.
"""
return {}
def create_response(self):
"""
Generates the actual HttpResponse to send back to the user.
"""
(paginator, page) = self.build_page()
context = {
'query': self.query,
'form': self.form,
'page': page,
'paginator': paginator,
'suggestion': None,
}
if self.results and hasattr(self.results, 'query') and self.results.query.backend.include_spelling:
context['suggestion'] = self.form.get_suggestion()
context.update(self.extra_context())
return render_to_response(self.template, context, context_instance=self.context_class(self.request))
haystack forms.py
def search(self):
if not self.is_valid():
return self.no_query_found()
if not self.cleaned_data.get('q'):
return self.no_query_found()
sqs = self.searchqueryset.auto_query(self.cleaned_data['q'])
if self.load_all:
sqs = sqs.load_all()
return sqs

Related

The view products.views.get_product didn't return an HttpResponse object. It returned None instead

ValueError at /product/apple-ipad-air-5th-gen-64-gb-rom-109-inch-with-wi-fi5g-purple/
The view products.views.get_product didn't return an HttpResponse object. It returned None instead.
how can i solve this problem please help me
`
from django.shortcuts import render,redirect
from products.models import Product
from accounts.models import *
from django.http import HttpResponseRedirect
from products.models import *
from django.utils.timezone import datetime
# Create your views here.
def get_product(request, slug):
product = Product.objects.get(slug=slug)
# comment = Comment.objects.get(slug=slug)
if request.method == "POST":
star = request.POST.get('star')
name = request.user.first_name
body = request.POST.get('body')
review = Comment(star=star, name=name,body=body,date_added = datetime.today())
review.product = product
review.save()
return redirect(f'/product/{slug}', slug=product.slug)
try:
context = {'product': product, }
if request.GET.get('size'):
size = request.GET.get('size')
price = product.get_product_price_by_size(size)
context['selected_size'] = size
context['updated_price'] = price
return render(request, 'product\product.html' , context = context)
except Exception as e:
print(e)
`
i am making a ecommerce website and i add review option then i got this error
You got this error because return is inside if statement.
I have modified this:
try:
context = {'product': product, }
if request.GET.get('size'):
size = request.GET.get('size')
price = product.get_product_price_by_size(size)
context['selected_size'] = size
context['updated_price'] = price
except Exception as e:
print(e)
return render(request, 'product\product.html', context)

Django function redirects back to class

I am trying to get it so anytime my function delete_curricula_info is activated it then redirects to CurriculumClasses url
class CurriculumClasses(TemplateView):
template_name = 'architect/assignclasses.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = AssignClassForm(self.request.GET or None)
context['classes'] = Classes.objects.all()
context['curricula_infos'] = CurriculaInfo.objects.all()
context['course_abbr'] = self.kwargs.get('course_abbr')
context['year'] = self.kwargs.get('year')
context['semester'] = self.kwargs.get('semester')
context['module_abbr'] = self.kwargs.get('module_abbr')
context['curricula'] = Curricula.objects.get(
course_period__course__course_abbreviation=self.kwargs.get('course_abbr'),
module_period__module__module_abbreviation=self.kwargs.get('module_abbr'),
course_period__period__academic_year=self.kwargs.get('year'),
course_period__period__semester=self.kwargs.get('semester')).pk
return context
def delete_curricula_info(request, pk):
curricula_info = get_object_or_404(CurriculaInfo, pk=pk)
curricula_info.delete()
return redirect('curriculum_classes')
Heres my urls.py
path('courses/<str:course_abbr>/<str:module_abbr>/<int:year>/<int:semester>/classes/',
views.CurriculumClasses.as_view(), name='curriculum_classes'),
path('delete-curricula-info/<int:pk>/', views.delete_curricula_info, name='delete_curricula_info')
The url for my CurriculumClasses is using a dynamic one so i could not hardcode the url to the return redirect of my function. So how do i redirect as my current method does not work. Thanks.
You can redirect it like this:
from django.urls import reverse
def delete_curricula_info(request, pk):
curricula_info = get_object_or_404(CurriculaInfo, pk=pk)
curricula_info.delete()
course_abbr = 'xxx'
module_abbr = 'xxx'
year = 2018
semester = 1
args = (course_abbr, module_abbr, year, semester)
return redirect(reverse('curriculum_classes', args=args))

How can I keep GET instance on template form after submission?

I have a simple search bar, I would like to keep the data the user submited and show it on the search bar after the form submission. How can I do that ?
I'm using GET for the search method, but I do not save any searched items on any model and I prefer not to, I was wondering if there was another way to show it without using the database storage.
Here is what my code looks like :
views.py
def index(request):
allGigs = Gig.objects.filter(status=True)
context = {'gigs': allGigs}
return render(request, 'index.html', context)
def search_gigs(request):
title = request.GET['title']
request.session['title'] = title #a try with session, but the data is kept once the user returns to front page...
gigs = Gig.objects.filter(title__contains=title)
return render(request, 'index.html', {"gigs": gigs})
models.py Gig Model has title CharField.
index.html
<form role="search" method="GET" action="{% url 'search' %}">
<input type="text" name="title">
<input type="submit" value="submit">
</form>
urls.py
url(r'^search/$', views.search_gigs, name='search'), #example : /search/?title=my_search_word
url(r'^$', views.index, name='index'),
I thought about using Django Sessions but the problem is that the user can only see what he searched after returning to the index page, any suggestion ?
You can use this sticky query method decorator on your view.
from urllib.parse import urlencode
try:
import urlparse
except ImportError:
from urllib import parse as urlparse
import wrapt
from django.http import HttpResponseRedirect
'''
Originally From:
https://www.snip2code.com/Snippet/430476/-refactor--Django--sticky-URL-query-para
'''
"""
File: decorators.py
Author: timfeirg
Email: kkcocogogo#gmail.com
Github: https://github.com/timfeirg/
Description: remember_last_query_params is from
http://chase-seibert.github.io/blog/2011/09/02/django-sticky-url-query-parameters-per-view.html
"""
class sticky_query(object):
"""Stores the specified list of query params from the last time this user
looked at this URL (by url_name). Stores the last values in the session.
If the view is subsequently rendered w/o specifying ANY of the query
params, it will redirect to the same URL with the last query params added
to the URL.
url_name is a unique identifier key for this view or view type if you want
to group multiple views together in terms of shared history
Example:
#remember_last_query_params("jobs", ["category", "location"])
def myview(request):
pass
"""
def __init__(self, views_name, query_params):
self._cookie_prefix = views_name + '_'
self._query_params = list(set(
query_params + ['page', 'paginate_by', 'order_by_fields']))
def _get_sticky_params(self, request):
"""
Are any of the query parameters we are interested in on this request
URL?
"""
gum = []
for current_param, v in request.GET.items():
if current_param in self._query_params:
gum.append(current_param)
return gum
def _get_last_used_params(self, session):
"""
Gets a dictionary of JUST the params from the last render with values
"""
litter = {}
for k in self._query_params:
last_value = session.get(self._cookie_prefix + k, None)
if last_value:
litter[k] = last_value
return litter
def _digest(self, current_url, litter):
"""
update an existing URL with or without paramters to include new
parameters from
http://stackoverflow.com/questions/2506379/add-params-to-given-url-in-python
"""
parse_res = urlparse.urlparse(current_url)
# part 4 == params
query = dict(urlparse.parse_qsl(parse_res[4]))
query.update(litter)
query = urlencode(query)
parse_res = urlparse.ParseResult(
parse_res[0], parse_res[1], parse_res[2], parse_res[3], query,
parse_res[5])
new_url = urlparse.urlunparse(parse_res)
return new_url
#wrapt.decorator
def __call__(self, wrapped, instance, args, kwargs):
request = args[0]
session = request.session
query = request.GET
gum = self._get_sticky_params(request)
if gum:
for k in gum:
sticky_key = self._cookie_prefix + k
session[sticky_key] = query[k]
else:
meta = request.META
litter = self._get_last_used_params(session)
if litter:
current_url = '{0}?{1}'.format(
meta['PATH_INFO'], meta['QUERY_STRING'])
new_url = self._digest(current_url, litter)
return HttpResponseRedirect(new_url)
return wrapped(*args, **kwargs)
Use this decorator on your view:
from django.utils.decorators import method_decorator
#method_decorator(sticky_query("search_page", ["title"]), name='dispatch')
There is a simple way to do so :
<input type="text" name="title" value="{{ request.POST.title }}">
After the form submit it will keep the POST title field value and use it as the input value.

Proper way to retrieve middleware values

I'm trying to retrieve some loading times per view and displaying it in the page. To do so I'm using a middleware and a context_processor setup but I can't find the way to retrieve this values to the context of the view.
Here is the example:
I've added the middleware and context_processor to the settings.
middlewares.py (from this answer):
from __future__ import unicode_literals, absolute_import
from django.db import connection
from time import time
from operator import add
import re
from functools import reduce
class StatsMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
# get number of db queries before we do anything
n = len(connection.queries)
# time the view
start = time()
response = view_func(request, *view_args, **view_kwargs)
total_time = time() - start
# compute the db time for the queries just run
db_queries = len(connection.queries) - n
if db_queries:
db_time = reduce(add, [float(q['time']) for q in connection.queries[n:]])
else:
db_time = 0.0
# and backout python time
python_time = total_time - db_time
stats = {
'total_time': total_time,
'python_time': python_time,
'db_time': db_time,
'db_queries': db_queries,
}
# this works fine
print(stats)
# replace the comment if found
if response:
try:
# detects TemplateResponse which are not yet rendered
if response.is_rendered:
rendered_content = response.content
else:
rendered_content = response.rendered_content
except AttributeError: # django < 1.5
rendered_content = response.content
if rendered_content:
s = rendered_content
# The following code is commented
# because it can't compile bytes and
# as long as I understand the values comes
# from the rendered template which are the ones I'm interested in getting
# regexp = re.compile(
# r'(?P<cmt><!--\s*STATS:(?P<fmt>.*?)ENDSTATS\s*-->)'
# )
# match = regexp.search(s)
# if match:
# s = (s[:match.start('cmt')] +
# match.group('fmt') % stats +
# s[match.end('cmt'):])
# response.content = s
return response
context_processors.py
#from django.template import RequestContext
def foo(request):
context_data = dict()
context_data["stats"] = "stats"
return context_data
views.py:
from django.shortcuts import render
from accounts.models import User
def myFunc(request):
users = User.objects.all()
return render(request, 'index.html, {'users_list': users})
index.html:
{{stats}}
<br>
{% for user in users_list %}
{{user}} <br>
{% endfor %}
What is the proper way to retrieve these values? The idea is to compare those values later against the cache framework.

Links in django_facebook app result in Bad Request: missing signed_request

I'm using the django_facebook library installed on pythonanywhere.
When I click a link from one view to another, I get an error message:
400 Bad Request
Missing signed_request.
Could someone give me the brief on how links work in a facebook app?
Removing the #canvas_only decorator doesn't solve the problem, because I need access to the graph api.
Here's the code:
views.py:
from django.shortcuts import render
from django_facebook.decorators import canvas_only
#from django_facebook.decorators import facebook_required
#from django.utils.decorators import method_decorator
from models import Poem, Share
from django import forms
from django.views.generic import View
class PoemEntryForm(forms.Form):
words = forms.CharField( widget=forms.widgets.Textarea(), initial='ENTER\nPOEM\nHERE\nONE\nWORD\nPER\nLINE' )
#canvas_only
def home(request):
me = request.facebook.graph.get_object('me')
my_username = me['username']
request.session['username'] = my_username
try:
poems = Poem.objects.filter(user=my_username)
except Poem.DoesNotExist:
poems = []
# convert poems into tuples of information relevant to the home page
# sort them in reverse chronological order
# ie: title and created
poems = [(poem.title(), poem.created) for poem in sorted(poems, key=lambda poem: poem.created, reverse=True)]
try:
shared = Share.objects.filter(shared_to=my_username)
except Share.DoesNotExist:
shared = []
shared = [(poem.title(), poem.user, poem.created) for poem in sorted(shared, key=lambda poem: poem.created, reverse=True)]
return render(request, 'home.html', {
'me': me,
'my_poems': poems,
'shared': shared,
})
class Create(View):
##method_decorator(canvas_only)
def get(self, request, *args, **kwargs):
#self.me = request.facebook.graph.get_object('me')
form = PoemEntryForm(request.GET)
return render(request, 'create.html', {
'form': form,
'debug': request.session['username']
})
##method_decorator(canvas_only)
def post(self, request, *args, **kwargs):
if request.session['username']:
form = PoemEntryForm(request.POST)
poem = Poem()
poem.user = request.session['username']
poem.text = request.POST['words']
poem.save()
return render(request, 'submitted.html', {})
else:
return render(request, 'error_submitting.html', {})
submitted.html:
<html>
<body>
<h3>You submitted a poem</h3>
Home
</body>
</html>
So the deal is this.
When the django, or whatever is doing the replying replies with: missing signed_request., what it really means is that the session doesn't contain an entry of key 'signed_request'. You can find this request in the POST data of the initial request. Save it in the session, and you're good to go.

Categories

Resources