Pyramid rendering blank page on refresh after configuring sessions - python-2.7

I am having an issue where after adding SignedCookieSessionFactory to my pyramid configuration, when I attempt to refresh the page in a browser, only a blank page is returned (both head and body tags are empty). The first time I hit that page it renders as expected. A separate session also produces a blank page. In order to get the page to render again, I must restart the server. Refresh worked fine before I added the session factory, but I would lose the state when refreshing (as expected).
Pyramid config:
def main(global_config, **settings):
session_factory = SignedCookieSessionFactory('cossecret')
config = Configurator(settings=settings, session_factory=session_factory)
config.include('pyramid_jinja2')
config.include('.models')
config.include('.routes')
config.registry.games = Games()
config.scan()
return config.make_wsgi_app()
routes.py:
def includeme(config):
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
config.add_route('game', '/game/{game}')
view.py:
#view_config(route_name='game', renderer='templates/game.jinja2')
def game_view(request):
""" Returns Game Play page based on ID"""
if "game_id" in request.session:
if request.matchdict['game'] != request.session["game_id"]:
if request.matchdict['game'] in request.registry.games.games:
request.session.invalidate()
request.session["game_id"] = request.matchdict['game']
else:
raise HTTPNotFound
else:
if request.matchdict['game'] in request.registry.games.games:
request.session["game_id"] = request.matchdict['game']
else:
raise HTTPNotFound
response = {}
response['game'] = request.session['game_id']
if "player_id" in request.session:
response['player_id'] = request.session['player_id']
else:
response['player_id'] = "None"
return response
I am pretty new to Pyramid, so any ideas would be appreciated. Stepping through the renderer, I noticed that the renderer scanned the html headers in the template the first time, but completely skipped over them the second time and then ignores everything after the body tag. Not sure if this helps.

So apparently in another of my views that is part of my API, there was this:
json_return = json.dumps(return_data)
response = Response
response.content_type = 'json'
response.body = json_return
return Response(content_type='json', body=json_return)
I don't know why creating two response objects would cause this behavior, but removing the first one in favor of just using the "return" line resolved the issue.

Related

Update request.session variable before function ends

At a Django server, I have a page with a single button that starts a function.
This function takes a while to complete, and I tried writing updates on the process to a request.session variable with the intent of checking on its contents from a separate page.
It seems however that request.session variables are not updated until the function they are included in are done. At least the variable does not update until then.
Am I right, and if so, is there a way to write to request.session variable before the function's completion?
The sessions are set up properly, I can write and read variables with other examples. For now I'll also just make a temporary db record to store the status update info and read it from there, but I'm curious as of this request.session thing - is my guess correct, and is there a way around?
update:
views.py
#login_required
def autolink(request):
result, time = access_check(request, 'super')
if not result:
return redirect('index')
result = f_autolink(request)
if result is None:
result = request.session.get('technical', '')
return render(request, 'autolink.html', {'result': result, })
functions.py
def f_autolink(request):
if request.method == 'GET':
result = None
elif request.method == 'POST':
request.session['technical'] = 'starting the job'
result = f_kzd_autolink(request)
else:
result = None
return result
def f_kzd_autolink(request):
homeless = Kzd.objects.filter(payment__isnull=True, returned=False).distinct()
result = []
count = homeless.count()
counter = 0
request.session['technical'] = 'Starting link job - {c} records to process'.format(c=count)
for h in homeless:
counter += 1
request.session['technical'] = 'Checking record {c1} of {c}'.format(c1=counter, c=count)
/* long code that makes the h in homeless cycle run for about 3 minutes, unrelated to the question */
so basically, the view shows request.session.get('technical', ''), but neither function writes to it until they are done (it then writes about processing the last record).
The session is saved on a per-request basis when it was modified or when the setting settings.SESSION_SAVE_EVERY_REQUEST is set to True in your settings.
So the simple answer yes, the session is saved by the session middleware when processing the response created by a view. But you could do it manually by calling request.session.save() inside your view.
If you have code, that runs very long it would be better to imediately create a response and use tools like celery to asynchronously process your task.
And you should consider storing your data into an own database table/ own model if it is not really related to the user session.

Django Cookie is not set properly

I am trying to set a cookie in a Django view. Something like this:
def my_view(request):
response = HttpResponse('Setting a cookie')
if 'my_cookie' in request.COOKIES:
return HttpResponse('Cookie found.')
else:
response.set_cookie('my_cookie', 'value')
return HttpResponse('Cookie set.')
In my mind, how it should work is this: on the first load, the cookie is not found, so its set and 'Cookie set.' is returned. If I reload the page, the cookie should be found, since it has been set already, so 'Cookie found.' is returned.
However, every time I reload, I get 'Cookie set.' for some reason. Any help? Thanks.
EDIT
I edited my code according the first comment:
def my_view(request):
response = HttpResponse('Setting a cookie')
if 'my_cookie' in request.COOKIES:
print 'Cookie found.'
response = HttpResponse(request.COOKIES['my_cookie'])
return response
else:
print 'Cookie set.'
response.set_cookie('my_cookie', 'value')
response = HttpResponse('value')
return response
I am returning the object that is being used to set the cookie. I am only trying to get the value. However, in my console, I always get 'Cookie set.', why is that?

Django pagination while objects are being added

I've got a website that shows photos that are always being added and people are seeing duplicates between pages on the home page (last added photos)
I'm not entirely sure how to approach this problem but this is basically whats happening:
Home page displays latest 20 photos [0:20]
User scrolls (meanwhile photos are being added to the db
User loads next page (through ajax)
Page displays photos [20:40]
User sees duplicate photos because the photos added to the top of the list pushed them down into the next page
What is the best way to solve this problem? I think I need to somehow cache the queryset on the users session maybe? I don't know much about caches really so a step-by-step explanation would be invaluable
here is the function that gets a new page of images:
def get_images_paginated(query, origins, page_num):
args = None
queryset = Image.objects.all().exclude(hidden=True).exclude(tags__isnull=True)
per_page = 20
page_num = int(page_num)
if origins:
origins = [Q(origin=origin) for origin in origins]
args = reduce(operator.or_, origins)
queryset = queryset.filter(args)
if query:
images = watson.filter(queryset, query)
else:
images = watson.filter(queryset, query).order_by('-id')
amount = images.count()
images = images.prefetch_related('tags')[(per_page*page_num)-per_page:per_page*page_num]
return images, amount
the view that uses the function:
def get_images_ajax(request):
if not request.is_ajax():
return render(request, 'home.html')
query = request.POST.get('query')
origins = request.POST.getlist('origin')
page_num = request.POST.get('page')
images, amount = get_images_paginated(query, origins, page_num)
pages = int(math.ceil(amount / 20))
if int(page_num) >= pages:
last_page = True;
else:
last_page = False;
context = {
'images':images,
'last_page':last_page,
}
return render(request, '_images.html', context)
One approach you could take is to send the oldest ID that the client currently has (i.e., the ID of the last item in the list currently) in the AJAX request, and then make sure you only query older IDs.
So get_images_paginated is modified as follows:
def get_images_paginated(query, origins, page_num, last_id=None):
args = None
queryset = Image.objects.all().exclude(hidden=True).exclude(tags__isnull=True)
if last_id is not None:
queryset = queryset.filter(id__lt=last_id)
...
You would need to send the last ID in your AJAX request, and pass this from your view function to get_images_paginated:
def get_images_ajax(request):
if not request.is_ajax():
return render(request, 'home.html')
query = request.POST.get('query')
origins = request.POST.getlist('origin')
page_num = request.POST.get('page')
# Get last ID. Note you probably need to do some type casting here.
last_id = request.POST.get('last_id', None)
images, amount = get_images_paginated(query, origins, page_num, last_id)
...
As #doniyor says you should use Django's built in pagination in conjunction with this logic.

Authentication in pyramid

I am trying to set up a basic navigation in pyramid (1.4a1). According to the tutorial given at tutorial groupfinder is called once we remember after login is successful. This works on my local but when I try the same on a server it doesn't call groupfinder at all and keeps looping between the two routes. Here's my code snippet:
from pyramid.security import remember, forget, authenticated_userid
from pyramid.httpexceptions import HTTPFound, HTTPForbidden
from pyramid.threadlocal import get_current_registry
from pyramid.url import route_url
from pyramid.view import view_config, forbidden_view_config
#view_config(route_name='index',
renderer='templates:templates/index.pt',
permission='Authenticated')
def index_view(request):
try:
full_name = (request.user.first_name + ' ' + request.user.last_name)
except:
full_name = "Anonymous"
return {"label": label, "user_name": full_name}
#forbidden_view_config()
def forbidden(request):
if authenticated_userid(request):
return HTTPForbidden()
loc = request.route_url('login.view', _query=(('next', request.path),))
return HTTPFound(location=loc)
#view_config(route_name='login.view')
def login_view(request):
came_from = request.route_url('index')
#perform some authentication
username = 'xyz'
if authenticate(username):
headers = remember(request, username)
#user was authenticated. Must call groupfinder internally and set principal as authenticated.
return HTTPFound(location=came_from, headers=headers)
else:
return HTTPForbidden('Could not authenticate.')
return HTTPForbidden('Could not authenticate.')
Also, my ACL looks like:
__acl__ = [(Allow, Authenticated, 'Authenticated'), DENY_ALL].
Can someone tell my why groupfinder is not being called? Is the request routing happening properly? Also, the same code works on my local setup fine. So there is no problem in groupfinder or ACL authorization settings.
Thanks much!
After lot of debugging and digging up I found out that the issue was very simple. Don't know the reason for the behavior but I had added secure = True attribute when calling AuthTktAuthenticationPolicy(). When I removed this attribute, it started working.

How can I get the lenth of a session in django views

I am using a code for my wish list . I need the no of products in the wishlist to show there on my site .I tried various methods but I Think session will only do this .Can some help please .
How can I do so .
#never_cache
def wishlist(request, template="shop/wishlist.html"):
"""
Display the wishlist and handle removing items from the wishlist and
adding them to the cart.
"""
skus = request.wishlist
error = None
if request.method == "POST":
to_cart = request.POST.get("add_cart")
add_product_form = AddProductForm(request.POST or None,
to_cart=to_cart,request=request)
if to_cart:
if add_product_form.is_valid():
request.cart.add_item(add_product_form.variation, 1,request)
recalculate_discount(request)
message = _("Item added to cart")
url = "shop_cart"
else:
error = add_product_form.errors.values()[0]
else:
message = _("Item removed from wishlist")
url = "shop_wishlist"
sku = request.POST.get("sku")
if sku in skus:
skus.remove(sku)
if not error:
info(request, message)
response = redirect(url)
set_cookie(response, "wishlist", ",".join(skus))
return response
# Remove skus from the cookie that no longer exist.
published_products = Product.objects.published(for_user=request.user)
f = {"product__in": published_products, "sku__in": skus}
wishlist = ProductVariation.objects.filter(**f).select_related(depth=1)
wishlist = sorted(wishlist, key=lambda v: skus.index(v.sku))
context = {"wishlist_items": wishlist, "error": error}
response = render(request, template, context)
if len(wishlist) < len(skus):
skus = [variation.sku for variation in wishlist]
set_cookie(response, "wishlist", ",".join(skus))
return response
Session != Cookies. The session is managed by the server on the backend, cookies are sent to the users browser. Django uses a single cookie to help track sessions but you are simply using cookies in this instance.
The session framework lets you store and retrieve arbitrary data on a per-site-visitor basis. It stores data on the server side and abstracts the sending and receiving of cookies. Cookies contain a session ID – not the data itself (unless you’re using the cookie based backend).
It's difficult to tell what you want, but if you simply want to get a count of the number of items you are saving in the cookie, you simply have to count your skus and put it in the context being sent to the template:
if len(wishlist) < len(skus):
skus = [variation.sku for variation in wishlist]
set_cookie(response, "wishlist", ",".join(skus))
context = {"wishlist_items": wishlist, "error": error, "wishlist_length":len(wishlist)}
return render(request, template, context)
and use:
{{ wishlist_length }}
in your template