Does this `if` condition ever runs? - python

I am reading some codes and I am confused here:
class PostListView(ListView):
# compressed
def get_context_data(self, **kwargs):
session_key = 'viewed_topic_{}'.format(self.topic.pk) # <-- here
if not self.request.session.get(session_key, False):
self.topic.views += 1
self.topic.save()
self.request.session[session_key] = True # <-- until here
kwargs['topic'] = self.topic
return super().get_context_data(**kwargs)
so the if condition checks that if there is no session with that key then increment self.topics.view by one.
I am confused here because whenever a user log in to the website their session will be created automatically and there are zero chances for being no session unless a user doesn't log in to the website. (Please notice that this project doesn't allow unauthenticated users to view the home page, sign in is a must.)
Is this if condition will ever be executed?

session_key = 'viewed_topic_{}'.format(self.topic.pk) the line indicates to post/topic specific session key.
if not self.request.session.get(session_key, False): this line checks if the key is available on the session. If not simply adding the key into the session.
Note: This is not the login session key. So, don't be confused with the login session key.

The if condition will be executed(expect there's an exception before). The if block will be executed depending on the condition.
If the condition is considered true depends on if the key for that topic id is not set. This should be true on the 1nd request for that topic ID and false for any later request in the same session. It might be different if other code sets that key or an exception is raised.
BTW You could just use:
if session_key not in self.request.session:
That would also be more readable.

Related

Change user state in database based on session

I have webapp that shows who is currently logged in on it's front page. I did that by having isActive field in user model, if user is logged isActive = True if user is logged out isActive = False. Unfortunately now I needed to implement auto log out on browser close and I've achieved that thanks to
REMEMBER_COOKIE_DURATION = datetime.timedelta(minutes=0)
app.config["SESSION_PERMANENT"] = False
But this obviously doesn't change state in database
My approach was to check active users and check sessions, get the ID's of both and compare them. If user ID is in active_user_list and not in session_id get this user ID and change it's state in database.
def checking_loop():
delay = 180
while True:
active_users = User.query.filter_by(isActive=True).all()
#don't worry about querying database in a loop It's stored locally
#on the PC
active_users_id = []
user_in_session = []
if session.get('_user_id'):
user_in_session.append(session['_user_id'])
for x in active_users:
active_users_id.append(x.id)
users_to_logout = [x for x in active_users_id if x not in user_in_session]
time.sleep(delay)
logout_loop = Thread(target=checking_loop)
logout_loop.start()
So, what's not working? I didn't know that when I call session I only get one ID.
Can I make this work somehow or do you have a better approach for this?
I don't think this will work. Flask session depends on context https://flask.palletsprojects.com/en/2.0.x/reqcontext/#how-the-context-works. I don't know how your code works without view context, but generally global session derives it's value from currently served request (the assumption is only one request can be handled at the time). So in session you'll get current user, not all users.
I'm not sure when session connected to user is deleted or how to iterate over active sessions, but if they are stored in cookies backend won't be able to do that.

Get a 'Session matching query does not exist' error when users auto login via chrome

Error page
The website gets a 'Session matching query does not exist' error when users auto login via chrome. The error happens in the middleware that makes sure users can't login in different browsers.
from django.contrib.sessions.models import Session
class OneSessionPerUser:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
if request.user.is_authenticated:
current_session_key = request.user.logged_in_user.session_key
if current_session_key and current_session_key != request.session.session_key:
Session.objects.get(session_key=current_session_key).delete()
request.user.logged_in_user.session_key = request.session.session_key
request.user.logged_in_user.save()
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Does anybody know what might be the problem here or does anybody know how to disable the chrome auto login?
The problem is here:
if current_session_key and current_session_key != request.session.session_key:
Session.objects.get(session_key=current_session_key).delete()
I had the same problem, and as I understand it, you should change the get for filter. Because if get doesn't get an answer, he throws this error. With filter, a filter can be an empty query. So I did:
if current_session_key and current_session_key != request.session.session_key:
Session.objects.filter(session_key=current_session_key).delete()
after this, it was all good. No more strange bugs.
Edit:
After some good points made by my partner, what happened is that the session key got deleted or destroyed, so, when you apply the get, it doesn't get an answer because there is no empty value to get, that's why it spits the error. With filter, you are still searching a empty value, but in this case you can get empty queries, and because the values are unique, you won't take out two sessions at once.
Sorry if I have bad English, this is not my first language.

How do I not show delete confirmation in Django admin unless conditions are met

I overrode the delete_queryset method to add a condition before being able to delete a model. It looks like this:
def delete_queryset(self, request, queryset):
if condition_is_not_met:
self.message_user(request, 'Error', level=messages.ERROR)
return
super().delete_queryset(request, queryset)
This DOES prevent the deletion but it shows that error message and below it says, Successfully deleted 'x' objects (Again, not deleted so success message should not be there). I don't want to display that error message and what would be even better is if the confirmation page did not even show up if the condition is not met (This is a bonus though). Any suggestions?
Just override the has_delete_permission method on modeladmin, which will return True or False based on the condition.
def has_delete_permission(self, request, obj=None):
if condition_is_not_met:
return False
return True
Docs

Include authenticated user in dictionary for all views

I am working through the Pyramid authorization tutorial and I have noticed the pattern where
logged_in = request.authenticated_userid
is added to each view dictionary. Can it be avoided? I.e. is there a configuration which automatically ads user id to each view. Or is there a way to create a base, abstract view with the user id and inherit from it?
Part of the code from the tutorial:
#view_config(context='.models.Page', renderer='templates/view.pt', permission='view')
def view_page(context, request):
# not relevant code
return dict(page = context, content = content, edit_url = edit_url,
logged_in = request.authenticated_userid)
#view_config(name='add_page', context='.models.Wiki', renderer='templates/edit.pt',
permission='edit')
def add_page(context, request):
# not relevant code
return dict(page=page, save_url=save_url,
logged_in=request.authenticated_userid)
It's been awhile since I last looked, but I think logged_in in the samples is just an example to use to conditionally check if there is a logged on user or not. You could probably just as easily refer to request.authenticated_userid within any of your views or templates, too, and get the same behavior and not have to explicitly add a status to the response dict. The request object should be available to be referenced in your view templates, too.
Alternatively, I've used their cookbook to add a user object to the request to make a friendly request.user object that I can use to both check for logged in status where needed, plus get at my other user object details if I need to as well.

Django sessions: changing session key when modified

I am setting up a payment gateway and am using sessions to store data across page requests. The class below is used for organizing and storing information to the session.
class Gateway:
def __init__(self, session_key=None, session_name="FOO"):
# Store session ID and name
self.session_key = session_key
self.session_name = session_name
# Get the session
session = SessionStore(session_key=self.session_key)
try :
data = session[self.session_name]
except :
data = {user_id:None, checked_in:False }
self.__dict__.update(data)
def save(self) :
session = SessionStore(session_key=self.session_key)
session[self.session_name] = deepcopy(self.__dict__)
try :
del session['session_key']
del session['session_name']
except :
pass
session.save()
This view checks to see if the user is logged in. If he/she is, then he/she is redirected. If not, he/she is asked to either login or check in as a guest.
def check_in(request):
gateway = Gateway(session_key=request.session.session_key)
if request.user.is_authenticated():
gateway.user_id = request.user.id
gateway.checked_in = True
gateway.save()
return redirect('next_step')
else:
login_form = FormLogin()
if request.POST:
data = request.POST.copy()
if 'login' in data:
login_form = FormLogin(data)
if login_form.is_valid():
user = login(request, login_form)
if user:
gateway.user_id = user.id
gateway.checked_in = True
gateway.save()
return redirect('next_step')
elif 'guest' in data:
gateway.checked_in = True
gateway.save()
return redirect('next_step')
return render(
request,
'shop/login.html',
{
'login_form':login_form,
}
)
The next view checks the "checked_in" variable. This is to make sure that users are not skipping over the login/checkin process. (As a side note, the function "login(request, login_form)" is a function that is works perfectly in other contexts and returns the User if it was successful and None otherwise)
def next_step(request):
gateway = Gateway(session_key=request.session.session_key)
if not gateway.checked_in:#edited
messages.info(request, _(u'You must specify login first.'))
return redirect('check_in')
else:
#do the next step
Now for the problem:
Even when the user is authenticated, the "checked_in" variable is still false and causes the views to loop. A new session with a new session id is created each time that that I set the variable and save. The django docs have some explanation about the modification of sessions, but I cannot understand why new session is being created or why the session key is changing.
edit:
I am using the database backend.
I have duplicated this bug/issue:
URL RULE
url(r'^test/', 'shop.views.catalog.test', name="test")
VIEW FUNCTION
def test(request) :
key1 = request.session.session_key
request.session['test'] = 'test'
key2 = request.session.session_key
raise Exception("%s : %s === %s" % (key1, key2, request.session['test']))
Clear cookies for 127.0.0.1
go to 127.0.0.1:8000/test/
Exception at /test/
4793f2453758d7021a43a348a0f40a83 : 8568f729991e740395179c56cd37cf18 === test
refresh the page (w/o clearing cookies)
Exception at /test/
8568f729991e740395179c56cd37cf18 : 8568f729991e740395179c56cd37cf18 === test
so until the first time my session is modified, I have a different session key... unexpected behavior. I'm also curious why.
Django will not persist a session to the database if it has not been accessed or modified, so I believe the session_key you are using to initialise SessionStore is not actually backed by a database entry.
If this is the case: when you save your SessionStore it will be allocated a new session_key automatically [1] (as the existing key does not exist in the DB and we want to avoid session fixation [2]) and saved to the DB but the client will not be allocated this new session_key because your SessionStore is independent of request.session (which remains unmodified).
[1] https://github.com/django/django/blob/master/django/contrib/sessions/backends/db.py#L22
[2] https://groups.google.com/forum/?fromgroups#!topic/django-users/8b_6oTaXv7Q
The simple fix to test this hypothesis out would be to set request.session['kate'] = 'bob' before you initialise your Gateway class as this should force request.session to be persisted. You might like to refactor your Gateway class so that methods that need access to the session take request.session as an argument.
Check your SESSION_COOKIE_SECURE value and make sure you are using HTTPS when True..
https://github.com/lepture/flask-wtf/issues/76

Categories

Resources