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

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.

Related

How to use Django-import-export ImportForm and ConfirmImportForm outside admin

I want to use django-import-export's forms to implement the import feature for regular users, so it needs to be outside the admin section.
So far, all the implementations I have found are about
a) extending the feature inside the admin or
b) reimplementing the default views outside the admin.
But since the default forms work perfectly (specially the ConfirmImportForm that shows a diff between old records and new records attempting to be imported) I would like to subclass them as part of my project (outside admin) without reimplementing the whole view's logic, if such thing is even possible.
So far I tried (foolishly, I'm afraid) to subclass the import_export.admin.ImportMixin as two separated class views to import a model Period with resource PeriodResource. The methods import_action and process_import were reimplemented (basically copy and paste the same code and eliminating any code using self.site_admin) as View.get() and View.post():
# staging/views.py
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse
from django.views import generic
from django.template.response import TemplateResponse
from django.utils.translation import gettext_lazy as _
from django.utils.encoding import force_text
from import_export.forms import ConfirmImportForm
from import_export.signals import post_import
import import_export.admin
from .models import Period
from .resources import PeriodResource
class PeriodImportView(import_export.admin.ImportMixin, generic.View):
"""
Subclassing of ImportMixin as a generic View implementing ImportForm
"""
#: template for import view
import_template_name = 'period/import.html'
#: resource class
resource_class = PeriodResource
#: model to be imported
model = Period
def get_confirm_import_form(self):
'''
Get the form type used to display the results and confirm the upload.
'''
return ConfirmImportForm
def get(self, request, *args, **kwargs):
"""
Overriding the GET part of ImportMixin.import_action method to be used without site_admin
"""
resource = self.get_import_resource_class()(**self.get_import_resource_kwargs(request, *args, **kwargs))
context = self.get_import_context_data()
import_formats = self.get_import_formats()
form_type = self.get_import_form()
form = form_type(import_formats,
request.POST or None,
request.FILES or None)
# context.update(self.admin_site.each_context(request))
context['title'] = _("Import")
context['form'] = form
context['opts'] = self.model._meta
context['fields'] = [f.column_name for f in resource.get_user_visible_fields()]
# request.current_app = self.admin_site.name
return TemplateResponse(request, [self.import_template_name],
context)
def post(self, request, *args, **kwargs):
"""
Overriding the POST part of ImportMixin.import_action method to be used without site_admin
"""
resource = self.get_import_resource_class()(**self.get_import_resource_kwargs(request, *args, **kwargs))
context = self.get_import_context_data()
import_formats = self.get_import_formats()
form_type = self.get_import_form()
form = form_type(import_formats,
request.POST or None,
request.FILES or None)
if request.POST and form.is_valid():
input_format = import_formats[
int(form.cleaned_data['input_format'])
]()
import_file = form.cleaned_data['import_file']
# first always write the uploaded file to disk as it may be a
# memory file or else based on settings upload handlers
tmp_storage = self.write_to_tmp_storage(import_file, input_format)
# then read the file, using the proper format-specific mode
# warning, big files may exceed memory
try:
data = tmp_storage.read(input_format.get_read_mode())
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
except UnicodeDecodeError as ex1:
return HttpResponse(_(u"<h1>Imported file has a wrong encoding: %s</h1>" % ex1))
except Exception as ex2:
return HttpResponse(_(u"<h1>%s encountered while trying to read file: %s</h1>" % (type(ex2).__name__, import_file.name)))
result = resource.import_data(dataset, dry_run=True,
raise_errors=False,
file_name=import_file.name,
user=request.user)
context['result'] = result
if not result.has_errors() and not result.has_validation_errors():
context['confirm_form'] = self.get_confirm_import_form()(initial={
'import_file_name': tmp_storage.name,
'original_file_name': import_file.name,
'input_format': form.cleaned_data['input_format'],
})
# context.update(self.admin_site.each_context(request))
context['title'] = _("Import")
context['form'] = form
context['opts'] = self.model._meta
context['fields'] = [f.column_name for f in resource.get_user_visible_fields()]
# request.current_app = self.admin_site.name
return TemplateResponse(request, [self.import_template_name],
context)
class PeriodConfirmImportView(import_export.admin.ImportMixin, generic.View):
"""
Subclassing of ImportMixin as a generic View implementing ConfirmImportForm
"""
#: template for import view
import_template_name = 'period/import.html'
#: resource class
resource_class = PeriodResource
#: model to be imported
model = Period
def post(self, request, *args, **kwargs):
"""
Perform the actual import action (after the user has confirmed the import)
"""
# if not self.has_import_permission(request):
# raise PermissionDenied
confirm_form = ConfirmImportForm(request.POST)
if confirm_form.is_valid():
import_formats = self.get_import_formats()
input_format = import_formats[
int(confirm_form.cleaned_data['input_format'])
]()
tmp_storage = self.get_tmp_storage_class()(name=confirm_form.cleaned_data['import_file_name'])
data = tmp_storage.read(input_format.get_read_mode())
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
result = self.process_dataset(dataset, confirm_form, request, *args, **kwargs)
tmp_storage.remove()
self.generate_log_entries(result, request)
self.add_success_message(result, request)
post_import.send(sender=None, model=self.model)
url = reverse('staging:index')
return HttpResponseRedirect(url)
and then just show the forms in the template:
# staging/templates/period/import.html
{% if confirm_form %}
<form action="{% url 'staging:confirm_import_period' %}" method="POST">
{% csrf_token %}
{{ confirm_form.as_p }}
<p>
{% trans "Below is a preview of data to be imported. If you are satisfied with the results, click 'Confirm import'" %}
</p>
<div class="submit-row">
<input type="submit" class="default" name="confirm" value="{% trans "Confirm import" %}">
</div>
</form>
{% else %}
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{% trans "This importer will import the following fields: " %}
<code>{{ fields|join:", " }}</code>
</p>
<fieldset class="module aligned">
{% for field in form %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}
{{ field }}
{% if field.field.help_text %}
<p class="help">{{ field.field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
</fieldset>
<div class="submit-row">
<input type="submit" class="default" value="{% trans "Submit" %}">
</div>
</form>
{% endif %}
and my urls.py looks like this:
# staging/urls.py
from django.urls import path
from .views import PeriodIndexView, PeriodImportView, PeriodConfirmImportView
app_name = 'staging'
urlpatterns = [
path('period/', PeriodIndexView.as_view(), name='index'),
path('period/import/', PeriodImportView.as_view(), name='import_period'),
path('period/confirm_import/', PeriodConfirmImportView.as_view(), name='confirm_import_period'),
]
So far it works as intended, but this approach is so tightly coupled to the internal implementation of ImportMixin that I am afraid it will not survive any version upgrade of django-import-export.
Is there any way to achieve that without reimplementing the whole import_action and process_import methods?
After a lot of try and error I gave up on avoiding the re-implementation of methods import_action and process_import from import_export.admin.ImportMixin. Instead, I created my own mixins subclassing import_export.admin.ImportMixin and django.views.generic.View and removing all references to self.site_admin from methods import_action and process_import into equivalent methods get() and post().
# staging/views.py
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse
from django.views import generic
from django.template.response import TemplateResponse
from django.utils.translation import gettext_lazy as _
from django.utils.encoding import force_text
from import_export.forms import ConfirmImportForm
from import_export.signals import post_import
import import_export.admin
from .models import Period
from .resources import PeriodResource
class ImportView(import_export.admin.ImportMixin, generic.View):
"""
Subclassing of ImportMixin as a generic View implementing ImportForm
"""
#: template for import view
import_template_name = 'import.html'
#: resource class
resource_class = None
#: model to be imported
model = None
def get_confirm_import_form(self):
'''
Get the form type used to display the results and confirm the upload.
'''
return ConfirmImportForm
def get(self, request, *args, **kwargs):
"""
Overriding the GET part of ImportMixin.import_action method to be used without site_admin
"""
return self.post(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
"""
Overriding the POST part of ImportMixin.import_action method to be used without site_admin
"""
resource = self.get_import_resource_class()(**self.get_import_resource_kwargs(request,
*args,
**kwargs))
context = self.get_import_context_data()
import_formats = self.get_import_formats()
form = self.get_import_form()(import_formats, request.POST or None, request.FILES or None)
if request.POST and form.is_valid():
input_format = import_formats[
int(form.cleaned_data['input_format'])
]()
import_file = form.cleaned_data['import_file']
# first always write the uploaded file to disk as it may be a
# memory file or else based on settings upload handlers
tmp_storage = self.write_to_tmp_storage(import_file, input_format)
# then read the file, using the proper format-specific mode
# warning, big files may exceed memory
try:
data = tmp_storage.read(input_format.get_read_mode())
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
except UnicodeDecodeError as ex1:
return HttpResponse(_(u"<h1>Imported file has a wrong encoding: %s</h1>" % ex1))
except Exception as ex2:
return HttpResponse(_(u"<h1>%s encountered while trying to read file: %s</h1>" % (type(ex2).__name__, import_file.name)))
result = resource.import_data(dataset, dry_run=True,
raise_errors=False,
file_name=import_file.name,
user=request.user)
context['result'] = result
if not result.has_errors() and not result.has_validation_errors():
context['confirm_form'] = self.get_confirm_import_form()(initial={
'import_file_name': tmp_storage.name,
'original_file_name': import_file.name,
'input_format': form.cleaned_data['input_format'],
})
# context.update(self.admin_site.each_context(request))
context['title'] = _("Import " + self.get_model_info()[1])
context['form'] = form
context['opts'] = self.model._meta
context['fields'] = [f.column_name for f in resource.get_user_visible_fields()]
# request.current_app = self.admin_site.name
return TemplateResponse(request, [self.import_template_name],
context)
class ConfirmImportView(import_export.admin.ImportMixin, generic.View):
"""
Subclassing of ImportMixin as a generic View implementing ConfirmImportForm
"""
#: template for import view
import_template_name = 'import.html'
#: resource class
resource_class = None
#: model to be imported
model = None
def get_confirm_import_form(self):
'''
Get the form type used to display the results and confirm the upload.
'''
return ConfirmImportForm
def post(self, request, *args, **kwargs):
"""
Perform the actual import action (after the user has confirmed the import)
"""
confirm_form = self.get_confirm_import_form()(request.POST)
if confirm_form.is_valid():
import_formats = self.get_import_formats()
input_format = import_formats[
int(confirm_form.cleaned_data['input_format'])
]()
tmp_storage = self.get_tmp_storage_class()(name=confirm_form.cleaned_data['import_file_name'])
data = tmp_storage.read(input_format.get_read_mode())
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
result = self.process_dataset(dataset, confirm_form, request, *args, **kwargs)
tmp_storage.remove()
self.generate_log_entries(result, request)
self.add_success_message(result, request)
post_import.send(sender=None, model=self.model)
url = reverse('{}:{}_index'.format(self.get_model_info()[0], self.get_model_info()[1]))
return HttpResponseRedirect(url)
So now from my custom mixins ImportView and ConfirmImportView y can subclass the specific classes to import my specific models just setting the model and resource_class attributes, which was sort of what I was looking for.
class PeriodImportView(ImportView):
"""
ImportView specific for model Period and resource PeriodResource
"""
#: resource class
resource_class = PeriodResource
#: model to be imported
model = Period
class PeriodConfirmImportView(ConfirmImportView):
"""
ConfirmImportView specific for model Period and resource PeriodResource
"""
#: resource class
resource_class = PeriodResource
#: model to be imported
model = Period

invalid django form makes is_valid method always return false

My django form is invalid and so the .is_valid method never returns true. As a result, I am getting an "Expected HttpResponse but received None" type of error because my code never executes what is within the if-condition. I am wondering how to make my form valid. I am new to django so I am probably missing something obvious. Here is my code:
views.py
template_name1 = 'multiplication/detail.html'
template_name2 = 'multiplication/multiplied.html'
class myForm(forms.Form):
quantity1 = forms.IntegerField(required=False)
quantity2 = forms.IntegerField(required=False)
form = myForm()
def get(request):
return render(request,template_name1,{'form': form} )
def multiply_two_integers(x,y):
return x*y
def post(request):
if (form.is_valid()):
x = request.POST.get('quantity1')
y = request.POST.get('quantity2')
product = multiply_two_integers(x, y)
return render(request, template_name2, {'form': form, 'product':
product })
template_name1
<h1>Multiplication Function</h1>
<form action = "{% url 'multiplication:post' %}" method = "post">
{{ form.as_p }}
{% csrf_token %}
<input type = "submit" value ="Multiply">
<!--<button type="submit"> Multiply </button>-->
<h1>{{product}}</h1>
</form>
template_name2
<h1>{{product}}</h1>
urls/multiplication
from django.urls import path
from multiplication import views
app_name = 'multiplication'
urlpatterns = [
# /multiplication/
path('', views.get, name = 'get'),
path('multiplied', views.post, name='post')
]
This code is very strange. You seem to have a set of functional views, but are trying to randomly use some concepts from class-based views.
The reason why your form is not valid is because you never pass any data to it; an unbound form cannot be valid. You should not be instantiating the form outside of a view; you need to do it in the view, and when the request is a POST you should pass the POST data to it.
In function-based views you should not define separate functions for get and post. Combine them, as sown in the Django docs.
There is another point that you have missed about the error message; your reaction to it telling you that you have not returned a response if the form is invalid is to ask "why isn't it valid", but you should also do what it says and return a response in this case; the form will sometimes be actually invalid, and you should deal with this case.
Finally, to get the data from the form you should use form.cleaned_data, not request.POST.
def multiply_two_integers(x,y):
return x*y
def my_view(request):
if request.method == 'POST':
form = MyForm(request.POST)
if (form.is_valid()):
x = form.cleaned_data['quantity1']
y = form.cleaned_data['quantity2']
product = multiply_two_integers(x, y)
return render(request, template_name2, {'product': product })
else:
form = MyForm()
return render(request,template_name1,{'form': form} )

How to fill Django database on view buttonclick

I'm just learning Django, so my question might seem not worth attention, but i spent some time googling and havnt found an answer.
I have two models and a function to fill it
urls.py
url(r'^upd/$', update_database, name="upd")
views.py
def update_database(request):
grabdata()
def grabdata():
url = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_day.geojson"
weburl = urllib.request.urlopen(url)
if (weburl.getcode() == 200):
data = json.loads(weburl.read())
EarthQuakes.objects.all().delete()
LastGetSession.objects.all().delete()
lastsession = LastGetSession(hand=data["metadata"]["title"])
lastsession.save()
for i in data["features"]:
place = i["properties"]["place"]
place = str.split(",")
place = place[-1]
time = i["properties"]["time"]
mag = i["properties"]["mag"]
rept = i["properties"]["felt"]
if rept is None:
rept = 1
longitude = 0
latitude = 0
earthquake = EarthQuakes(place=place, time=time,
mag=mag, rept=rept,
longitude=longitude, latitude=latitude)
earthquake.save()
template
<a id="update_button" class="btn btn-info btn-lg" href="{% url 'upd' %}" >Update_database</a>
But i didn't get how to execute it from a view.
Or I assume there is a way to call a conroller function that calls the model function or something like that
It returns the "The view earthquake.views.update_database didn't return an HttpResponse object. It returned None instead."
It feels like there is one missing piece or the pazzle
On Your views.py
def my_view(request):
grabdata()
return HttpResponse('Done')
in urls.py add a new url
url(r'^my-view/$', views.my_view,name='my_view')
in your template
Update

Django Gaurdian + Haystack search results permissions

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

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