django save data to session without increasing its expiry - django

Basically I want to do something like:
request.session['last_date'] = datetime.datetime.now()
without django modify (increase) session expire_date (i.e, it should stay as it is)
I have SESSION_SAVE_EVERY_REQUEST = True
The session expiry should remain unchanged when last_date is modified as above. However for all other changes expiry should change. I do not want to set it as a global policy for the session.

When you want to change the default behavior of the session engine, the usual practice is to write a custom session backend. Fortunately, it isn't very difficult. We will make ours by subclassing django.contrib.session.backends.db.SessionStore
from django.contrib.session.backends.db import SessionStore as DbStore
class SessionStore(DbStore):
def load(self):
try:
self.current_session = self.model.objects.get(
session_key=self.session_key,
expire_date__gt=timezone.now()
)
return self.decode(s.session_data)
except (self.model.DoesNotExist, SuspiciousOperation) as e:
if isinstance(e, SuspiciousOperation):
logger = logging.getLogger('django.security.%s' % e.__class__.__name__)
logger.warning(force_text(e))
self._session_key = None
return {}
def create_model_instance(self, data):
"""
Return a new instance of the session model object, which represents the
current session state. Intended to be used for saving the session data
to the database.
"""
try:
expiry = self.current_session.expire_date
except AttributeError:
expiry = None
return self.model(
session_key=self._get_or_create_session_key(),
session_data=self.encode(data),
expire_date=self.get_expiry_date(expiry=expiry),
)
Most of the code is from django.contrib with few slight modificiations. Now all you have to do is to tell django to use our new session store by modifying the settings.py
SESSION_ENGINE = 'myapp.session'
Assuming that you put the above code in a file named session.py
Response to edit in question:
This code shows how to modify the session with out changing it's expiry time. Now you mention you want this behavour only if the last_date item is changed, make a modification as follows;
expiry = None
try:
if current_session.get('last_date') != date.get('last_date') :
expiry = self.current_session.expire_date
except AttributeError:
pass

Related

django is not persisting session in cookies unless I concatenate a list [duplicate]

I have a pretty simply utility function that gets an open web order if their is a session key called 'orderId', and will create one if there is no session key, and the parameter 'createIfNotFound' is equal to true in the function. Stepping through it with my debugger I can see that the piece of code that sets the session key after an order has been created does get hit with no exceptions, but when I check the Http request object' session field, it does not have that attribute ?
Utility
def get_open_web_order(request, createIfNotFound=False):
# Check for orderId in session
order_id = request.session.get('orderId')
web_order = None
if None != order_id:
try:
web_order = WebOrder.objects.get(id=order_id, status='O')
logging.info('Found open web order')
except WebOrder.DoesNotExist:
logging.info('Web order not found')
if (None == web_order) and (createIfNotFound == True):
logging.info('Creating new web order')
web_order = WebOrder()
web_order.status = 'O'
web_order.save()
request.session['orderId'] = web_order.id
# Assign logged in user and default billing and shipping
if request.user.is_authenticated() and hasattr(request.user, 'customer'):
customer = request.user.customer
web_order.customer = customer
web_order.set_defaults_from_customer()
web_order.save()
return web_order
In some cases you need to explicitly tell the session that it has been modified.
You can do this by adding request.session.modified = True to your view, after changing something in session
You can read more on this here - https://docs.djangoproject.com/en/1.10/topics/http/sessions/#when-sessions-are-saved
I had a similar issue, turns out I had set SESSION_COOKIE_DOMAIN in settings.py to the incorrect domain so it would not save any of my new session data. If you are using SESSION_COOKIE_DOMAIN, try checking that!
For example, if I am running the server on my localhost but I have in my settings SESSION_COOKIE_DOMAIN = "notlocalhost", then nothing I change in request.session will save.

sqlalchemy - data doesnt get pushed to database while commit but present in session(in memory)

I am adding data to sqlalchemy. But sometimes data is not getting updated or inserted to database. But the commit is successful and I can see the data in memory of session's object.
ie
session.identity_map
Running on sqlalchemy 1.3.3. python 2.7. ubuntu 18.04
from sqlalchemy.orm import Session
from . import Errors as ExecuteErrors
class Errors(object):
def __init__(self, sqlalchemy_engine, d):
self.sqlalchemy_engine = sqlalchemy_engine
self.d = d
def upsert(self, error):
session = Session(self.sqlalchemy_engine)
row = session.query(ExecuteErrors).filter_by(**{'c_name':error['c_name'], 'c_type':error['c_type'],
'f_name':error['f_name']}).scalar()
session.close()
if row:
self.update(error)
else:
self.insert(error)
def insert(self, error):
e = ExecuteErrors(**{'c_name':error['c_name'], 'c_type':error['c_type'], 'f_name':error['f_name'],
'msg':error['msg'], 'details':error['details']})
session = Session(self.sqlalchemy_engine, expire_on_commit=False)
session.add(e)
session.identity_map
session.commit()
session.close()
def update(self, error):
session = Session(self.sqlalchemy_engine, expire_on_commit=False)
session.query(ExecuteErrors).filter_by(**{'c_name':error['c_name'], 'c_type':error['c_type'],
'f_name':error['f_name']}).update({'msg': error['msg'], 'details': error['details']})
session.commit()
session.close()
def get_errors(self):
session = Session(self.sqlalchemy_engine)
e = session.query(ExecuteErrors).all()
session.close()
return e
def clear(self):
session = Session(self.sqlalchemy_engine)
session.query(ExecuteErrors).delete()
session.commit()
session.close()
Calling this with:
e = Error(engine, 'emp')
e.upsert({'c_name':'filter','c_type':'task','f_name':'f1','msg':'TypeError','details':'xyz'})
This should add row in database or update row with new data.
Its working for some insert and for some not.
You could find a possible workaround by explicitely flushing your session when needed.
That said, I think you should reconsider the way you're using sessions. Sessions are intended to manage database connections but you're using them as if there where actual connections.
IMHO, a better way to do would be to create a session at Error instanciation and use it when needed in all your methods.
An even better way to proceed could be to create a session at begining of your "calling" module and pass it to the Error instanciation, and to any other object which need access to database.
Doing this, you could even experience better performance and it may solve your problem (?)
More details about how to manage sessions in the sqlalchemy doc.
EDIT: In addition, the sqlalchemy doc lists some potential problems when used with sqlite. One of them could be the cause of your problem.

Django - can not access session

Using Django REST Framework in development, I have the following (previously working) code example, where in one view I set session data, and in another view I use that data.
And like I said, this code used to work, but now for some reason the stored session data can not be accessed anymore.
View for setting session data
#api_view(["POST"])
#permission_classes((AllowAny, ))
def set_session_data(request):
session_data_dict = loads(request.body.decode('utf-8'))
if not isinstance(session_data_dict, dict):
return Response({"message": "Expected a JSON object with key-val pairs to be sent. Key-val pairs to be set to session. Received something else.", status: status.HTTP_400_BAD_REQUEST})
try:
for key, value in session_data_dict.items():
request.session[key] = value
response_data = {"status": rest_status.HTTP_200_OK}
except:
response_data = {"status": rest_status.HTTP_500_INTERNAL_SERVER_ERROR}
return Response(response_data)
View that accesses stored session data:
#api_view(["GET"])
#permission_classes((AllowAny, ))
def check_user_logged_in(request):
try:
data = {"login_token": request.session["login_token"]}
except KeyError:
data = {"login_token": ""}
return Response(data, status=rest_status.HTTP_200_OK)
I have tested a little, and I can access the data in the session in the set_session_data view, after it has been added, like so:
request.session['login_token']
But when I try the same in the check_user_logged_in view, I get a KeyError.
So I tried checking if the sessions for both views are the same session, by checking the value of request.COOKIES[settings.SESSION_COOKIE_NAME] in each view. But in both views that results in the following error:
KeyError: 'sessionid'
Now, I have not touched the session settings, so they are the default settings from django-admin startproject. ('django.contrib.sessions' in INSTALLED_APPS, 'django.contrib.sessions.middleware.SessionMiddleware' in MIDDLEWARE, and nothing added.)
Can anyone explain why this is happening?

set time to live for each session separately KVsession flask

KBsession stores the session TTL based on PERMANENT_SESSION_LIFETIME is there a way to override this for specific sessions
EDIT:
so I have two different API for login I need to give any user login from one of them an infinite session TTL, the other one will take PERMANENT_SESSION_LIFETIME value
note: KBsession back-end is redis
I think the best way is use Session Interface to create specific processing. This is just an example, but I hope you can understand approach.
from flask import Flask, session as flask_session, jsonify
flask_app = Flask(__name__)
# just a few user types
UNIQUE_USER_TYPE = 'unique'
DEFAULT_USER_TYPE = 'default'
#flask_app.route('/login-default')
def login_default():
flask_session['user_type'] = DEFAULT_USER_TYPE
return 'login default done'
#flask_app.route('/login-unique')
def login_unique():
flask_session['user_type'] = UNIQUE_USER_TYPE
return 'login unique done'
#flask_app.route('/session-state')
def get_session_state():
return jsonify(dict(flask_session))
class UserTypeSessionInterface(SecureCookieSessionInterface):
def get_expiration_time(self, app, session):
"""
I just override method. Just demonstration.
It's called from save_session() and open_session()
"""
if session.get('user_type') == UNIQUE_USER_TYPE:
# set 1 hour for unique users
delta = datetime.utcnow() + timedelta(hours=1)
else:
# set 3 hour for default users
delta = datetime.utcnow() + timedelta(hours=3)
# add datetime data into session
session['lifetime'] = delta.strftime('%Y-%m-%dT%H:%M:%S')
return delta
# use our custom session implementation
flask_app.session_interface = UserTypeSessionInterface()
Now run server, open new private window, /login-default and /session-state:
# default behaviour
{
"lifetime": "2018-11-06T16:22:21",
"user_type": "default"
}
Open one more private window, /login-unique and /session-state:
# unique behaviour
{
"lifetime": "2018-11-06T14:25:17",
"user_type": "unique"
}
So, session store tool doesn't matter(redis, cassandra or something else). All what you need is just implement open_session() and save_session():
class YourSessionProcessor(SessionInterface):
def open_session(self, app, request):
# just do here all what you need
pass
def save_session(self, app, session, response):
# just do here all what you need
pass
flask_app.session_interface = YourSessionProcessor()
Also you can use custom session class(just an example):
from flask.sessions import SessionMixin
from werkzeug.datastructures import CallbackDict
class CustomSession(CallbackDict, SessionMixin):
def __init__(self, initial=None, sid=None):
def on_update(self):
self.modified = True
CallbackDict.__init__(self, initial, on_update=on_update)
self.sid = sid
self.modified = False
# YourSessionProcessor
def open_session(self, app, request):
# you can find any useful data in request
# you can find all settings in app.config
sid = request.cookies.get(app.session_cookie_name)
# ... do here everything what you need
return CustomSession(sid=sid)
Hope this helps.

SQLAlchemy query not retrieving committed data in alternate session

I have a problem where I insert a database item using a SQLAlchemy / Tastypie REST interface, but the item is missing when subsequently get the list of items. It shows up only after I get the list of items a second time.
I am using SQLAlchemy with Tastypie/Django running on Apache via mod_wsgi. I use a singleton Database Manager class to hold my engine and declarative_base, and with Tastypie, a separate class to get the session and make sure I roll-back if there is a problem with the commit. As in the update below, the problem occurs when I don't close my session after inserting. Why is this necessary?
My original code was like this:
Session = scoped_session(sessionmaker(autoflush=True))
# Singleton Database Manager class for managing session
class DatabaseManager():
engine = None
base = None
def ready(self):
host='mysql+mysqldb://etc...'
if self.engine and self.base:
return True
else:
try:
self.engine = create_engine(host, pool_recycle=3600)
self.base = declarative_base(bind=self.engine)
return True
except:
return False
def getSession(self):
if self.ready():
session = Session()
session.configure(bind=self.engine)
return session
else:
return None
DM = DatabaseManager()
# A session class I use with Tastypie to ensure the session is destroyed at the
# end of the transaction, because Tastypie creates singleton Resources used for
# all threads
class MySession:
def __init__(self):
self.s = DM.getSession()
def safeCommit(self):
try:
self.s.commit()
except:
self.s.rollback()
raise
def __del__(self):
try:
self.s.commit()
except:
self.s.rollback()
raise
# ... Then ... when I get requests through Apache/mod_wsgi/Django/Tastypie
# First Request
obj_create():
db = MySession()
print db.s.query(DBClass).count() # returns 4
newItem = DBClass()
db.s.add(newItem)
db.s.safeCommit()
print db.s.query(DBClass).count() # returns 5
# Second Request after First Request returns
obj_get_list():
db = MySession()
print db.s.query(DBClass).count() # returns 4 ... should be 5
# Third Request is okay
obj_get_list():
db = MySession()
print db.s.query(DBClass).count() # returns 5
UPDATE
After further digging, it appears that the problem is my session needed to be closed after creating. Perhaps because Tastypie's object_create() adds the SQLAlchemy object to it's bundle, and I don't know what happens after it leaves the function's scope:
obj_create():
db = MySession()
newItem = DBClass()
db.s.add(newItem)
db.s.safeCommit()
copiedObj = copyObj(newItem) # copy SQLAlchemy record into non-sa object (see below)
db.s.close()
return copiedObj
If someone cares to explain this in an answer, I can close the question. Also, for those who are curious, I copy my object out of SQLAlchemy like this:
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
class MyTastypieResource(Resource):
...
def copyObject(self, object):
base = {}
# self._meta is part of my tastypie resource
for p in class_mapper(self._meta.object_class).iterate_properties:
if p.key not in base and p.key not in self._meta.excludes:
base[p.key] = getattr(object,p.key)
return Struct(**base)
The problem was resolved by closing my session. The update in the answer didn't solve the problem fully - I ended up adding a middleware class to close the session at the end of a transaction. This ensured everything was written to the database. The middleware looks a bit like this:
class SQLAlchemySessionMiddleWare(object):
def process_response(self, request, response):
try:
session = MyDatabaseManger.getSession()
session.commit()
session.close()
except Exception, err:
pass
return response
def process_exception(self, request, exception):
try:
session = MyDatabaseManger.getSession()
session.rollback()
session.close()
except Exception, err:
pass