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.
Related
I have a class that take some info from a form, make some changes to it. And than saves it into database
At the moment all the logic is in the post method. And I want to make the code more structured and I want to put some part of it to a separate method. Is it possible? If so, how can I do it?
here is my code:
class AddSiteView(View):
form_class = AddSiteForm
template_name = 'home.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, { 'form': form })
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
site_instanse = form.save()
url = request.POST.get('url', '')
if url.endswith('/'):
url = url + "robots.txt"
else:
url = url + "/robots.txt"
robot_link = Robot(
site = site_instanse,
link = url,
)
robot_link.save()
pk = Robot.objects.get(site=site_instanse)
return redirect('checks:robots', pk.id)
return render(request, self.template_name, { 'form': form })
I want to make 2 changes to it:
The 1st thing I want to do is to move this part of code to a separate method
if url.endswith('/'):
url = url + "robots.txt"
else:
url = url + "/robots.txt"
And the 2nd thing I want to do is to move this part of code also in a separate method
robot_link = Robot(
site = site_instanse,
link = url,
)
robot_link.save()
pk = Robot.objects.get(site=site_instanse)
return redirect('checks:robots', pk.id)
The reason is that I will be adding more functions here. And I don't want to have it all in post method. If it is possible, please, help me. I've already tried several ways of solving this problem, but they didn't work
Thank you
There is nothing special about Django preventing you from using plain python functions. So, if you know how to define methods and functions, you should take the same approach. For example, the first part can be the function
def get_robots_url(url):
if url.endswith('/'):
url = url + "robots.txt"
else:
url = url + "/robots.txt"
return url
Then you call the extracted function in the same place
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
site_instance = form.save()
url = request.POST.get('url', '')
url = get_robots_url(url)
....
You can also define a function inside the class - a method, to group the code. For the 2nd part:
class AddSiteView(View):
...
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
site_instanse = form.save()
url = request.POST.get('url', '')
url = get_robots_url(url)
return self.create_robot(site_instanse, url)
return render(request, self.template_name, { 'form': form })
def create_robot(self, site_instance, url):
robot_link = Robot(
site = site_instanse,
link = url,
)
robot_link.save()
pk = Robot.objects.get(site=site_instance)
return redirect('checks:robots', pk.id)
i'm using an api to get informations about coronavirus cases around the world and i have an issue in the last line of code
from django.shortcuts import render
import requests
from django import forms
from django.views.generic import TemplateView
from .forms import homeform
def home(request):
if request.method =='POST':
form = homeform(request.POST)
if form.is_valid():
text = form.cleaned_data
field = text['name']
print(field)
country = field
else:
form = homeform()
country = 'algeria'
url = "https://covid-193.p.rapidapi.com/statistics"
querystring = {"country": country}
headers = {
'x-rapidapi-host': "covid-193.p.rapidapi.com",
'x-rapidapi-key': "36b864062emshac7e191eb5087e6p169e6bjsn24c86f3408c1"
}
response = requests.request("GET", url, headers=headers, params=querystring).json()
data = response['response']
print(data)
d = data[0]
context = {
'all': d['cases']['total'],
'recovered': d['cases']['recovered'],
'deaths': d['deaths']['total'],
'new': d['cases']['new'],
'serioz': d['cases']['critical'],
'active':d['cases']['active'],
'deaths_new':d['deaths']['new']
}
return render(request, 'index.html',{'form':form}, context)
here i have a problem with rendering the page it just shows my html code in the webpage but when i remove context from the last line of code evrything becomes normal , please help me
I think you are trying to pass two context dictionaries. Your {'form':form} is in the place of the context argument so your context isn't actually getting passed as a context. Add the form to the context dictionary in the line above return and remove {'form':form} from your render function.
I think you need to change your code to this :
return render(request, 'index.html',{'form':form,
'context':context})
This way you will get both in the html file.
I have written two class one for posting datas for payment and other one to show payment-successful message with order_id. I am sending order id from first function and i want to catch this id to show in my payment-successful template.
class ApiVIew(TemplateView):
template_name = 'payment.html'
def post(self,request):
r = requests.post(url='www.randomsite.com',params = {'authToken':'12345','card_no':'1234','card_cvv':'****'})
return HttpResponse(json.dumps({'response':r.json(),'status':'ok'}))
I call this class is ajax and parse there,so if r gives no error then i redirect(window.location=localhost:8000/success) to the success-payment.html page. so response gives me a json data:
{'isSuccess': 1, 'order_id': 1cq2,}
so i want to get this order_id and pass it to another function/class written below.
def payment_successfullView(request):
return render(request,'payment-successfull.html')
How can i achieve so? Thanks in advance.
1. Most simple way
urls.py:
...
path('<str:order_id>/success/', views.payment_successfullView, name='success'),
...
Views:
from django.shortcuts import redirect, reverse
class ApiVIew(TemplateView):
template_name = 'payment.html'
def post(self, request):
r = requests.post(url='www.randomsite.com',params = {'authToken':'12345','card_no':'1234','card_cvv':'****'})
if r.isSuccess:
return redirect(reverse('success', args=(r.order_id, )))
# do your stuff in case of failure here
def payment_successfullView(request, order_id):
return render(request,'payment-successfull.html', {
'order_id': order_id,
})
2. Another method using sessions:
urls.py:
...
path('success/', views.payment_successfullView, name='success'),
...
Views:
from django.shortcuts import redirect, reverse
from django.http import HttpResponseForbidden
class ApiVIew(TemplateView):
template_name = 'payment.html'
def post(self, request):
r = requests.post(url='www.randomsite.com',params = {'authToken':'12345','card_no':'1234','card_cvv':'****'})
if r.isSuccess:
request.session['order_id'] = r.order_id # Put order id in session
return redirect(reverse('success', args=(r.order_id, )))
# do your stuff in case of failure here
def payment_successfullView(request):
if 'order_id' in request.session:
order_id = request.session['order_id'] # Get order_id from session
del request.session['order_id'] # Delete order_id from session if you no longer need it
return render(request,'payment-successfull.html', {
'order_id': order_id,
})
# order_id doesn't exists in session for some reason, eg. someone tried to open this link directly, handle that here.
return HttpResponseForbidden()
Ok, I think the best answer points you in the right direction and let you figure out the fun part.
Tips:
Your APIView has to redirect to payment_successfullView
You have the order_id so you could use a DetailView
If you want to display a list of orders (order_id's) use ListView
I think using those tips, you'll be fine. Happy coding.
Note
You might want to read about Form views also, such view has an attribute called success_url. Ring a bell?
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.
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