Django application not showing local time when hosted using Heroku - django

Beginner Django programmer here. I'm working on a Django app and am having timezone issues. My code successfully converts time objects into local time when I run on my local server but this conversion does not work when my application is hosted on Heroku.
I am currently converting timezone using the tzlocal extension. In my views I use the code:
activate(get_localzone())
On my local server, get_localzone() successfully returns the local time. On the Heroku-hosted version of my application, get_localzone() returns UTC.
Here are my time settings in settings.py:
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
Not sure if this matters but I'm using a Postgres database:
DATABASES = {'default': dj_database_url.config(default='postgres://localhost')}
Here is an example of my home view to give you a sense of how I'm using the tzlocal package:
def home(request):
if not request.user.is_authenticated():
return render(request, 'idealist/index.html')
else:
context = RequestContext(request)
activate(get_localzone())
user = UserProfile.objects.filter(user=request.user)[0]
user_projects = user.projects()
events = user.events()
event_dict = SortedDict()
string = get_localzone()
for event in reversed(events):
date = event.datew.astimezone(get_localzone())
date = date.strftime("%B") + " " + str(date.day)
if date in event_dict.keys():
event_dict[date].append(event)
else:
event_dict[date] = [event]
return render_to_response('idealist/account_home.html', {'string': string, 'user': user, 'event_dict': event_dict, 'user_projects': user_projects}, context)
Also, here are the other time-related packages that I'm importing:
from django.utils.timezone import *
import pytz
from tzlocal import *
import datetime
from pytz import timezone
Thank you so much for your help. If there is any other information I can provide you with, please let me know!

Before the answer a piece of advice, never store localized timezones. Always store dates in UTC and convert on the application as needed.
get_localzone() is probably picking up the server timezone, yours is in whatever you call local timezone while heroku server is probably set to UTC. You have a few options:
1) Set your server timezone to whatever you want
I'm not 100% sure this will work but maybe it's worth trying. Maybe setting the TZ variable to your desirable timezone will make that the local timezone. Try using:
heroku config:add TZ="America/Los_Angeles"
Replacing with your desired timezone from this list.
2) Force django to use the timezone that you want
This might be a better option since you won't have to remember to set the timezone again in case you change providers or change the heroku server.
Instead of using get_localzone() pass in the name of the timezone you want directly like this:
timezone.activate(pytz.timezone('America/Los_Angeles'))
I'm assuming you always want to use the same timezone throughout your application, but if you want to allow the user to select it's own timezone you can store that value in the user session or profile. The Django docs has some good resources on this.

Related

Is Django application affected by the server timezone?

I have the below settings:
TIME_ZONE = UTC
USE_TZ = True
My server timezone is UTC+1 and the end user's timezone is UTC+2. Should I care about the server timezone or it is not related at all?
Server time zone isn't related in most cases. Your application will use UTC. The most common application in python uses datetime.datetime.now(tzInfo=None) which will automatically pass tzInfo as None when you call datetime.datetime.now() . Unless you specify what timezone tzInfo is, the datetime object will always return UTC.

Django Session KeyError when key exists

The following code works locally when I use Django's development server, but I am running into intermittent bugs in production with Nginx and Gunicorn.
views.py
def first_view(request):
if request.method == "POST":
# not using a django form in the template, so need to parse the request POST
# create a dictionary with only strings as values
new_post = {key:val for key,val in request.POST.items() if key != 'csrfmiddlewaretoken'}
request.session['new_post'] = new_mappings # save for use within next view
# more logic here (nothing involving views)
return redirect('second_view')
def second_view(request):
if request.method == 'POST':
new_post = request.session['new_post']
# ... more code below
# render template with form that will eventually post to this view
I will sometimes receive a KeyError after posting to the second view. Based on the documentation on when sessions are saved, it seems like the session variable should be saved since it is modifying the session directly. Also, if I take the sessionid provided the error page's debug panel and access the session via Django's API, I can see the 'new_post' session variable
python manage.py shell
>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore(session_key='sessionid_from_debug_panel')
>>> s['new_post']
# dictionary with expected post items
Is there something I'm missing? Thanks in advance for your help!
Ok, I finally figured out the issue.
By default Django uses cached sessions when you create a new project using django-admin startproject project_name_here
In the documentation it warns that caching should only be used in production if using the Memcached cache backend since the local-memory cache backend is NOT multi-process safe. https://docs.djangoproject.com/en/1.11/topics/http/sessions/#using-cached-sessions
The documentation also cautions against local memory caching in the deployment checklist: https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/#caches
I changed the SESSION_ENGINE in settings.py to 'django.contrib.sessions.backends.db' and the error went away. https://docs.djangoproject.com/en/1.11/ref/settings/#session-engine
Hope this is helpful to someone else!

django session key changing upon authentication

I have a Django app which records users' product choices for both authenticated users. My intention is to use the request.session.session_key variable to associate anonymous data with a user if they decide to register later, a la this post:
Django storing anonymous user data
However, it seems that the session key changes when the user logs in/ registers so the session key can no longer be associated with the user. Is this the correct behaviour of the Django session framework. Is there a solid way to achieve the functionality I'm looking for?
Any help much appreciated.
In settings.py
SESSION_ENGINE = 'youapp.session_backend'
in directory youapp in file session_backend.py
from django.contrib.sessions.backends.db import SessionStore as DbSessionStore
class SessionStore(DbSessionStore):
def cycle_key(self):
pass
And session not changed after login
While the approach suggested by nnmware may work for this particular case, there is a better one.
Instead of just doing nothing inside cycle_key, we should call the super method and then save the session.
Because if you look inside the original cycle_key function you will see that the data from the old session is copied to the new one, but is not actually saved.
In settings.py
SESSION_ENGINE = 'yourapp.session_backend'
Check that SESSION_ENGINE is pointing at a module (.py file), but not to the backend class!
Now, in your 'yourapp/session_backend.py' do the following:
from django.contrib.sessions.backends.db import SessionStore as DbSessionStore
class SessionStore(DbSessionStore):
def cycle_key(self):
super(SessionStore, self).cycle_key()
self.save()
One of the solutions would also be to update old session data in the Session store:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django.contrib.sessions.backends.db import SessionStore as DbSessionStore
from shop.models.cart import Cart
class SessionStore(DbSessionStore):
def cycle_key(self):
old_session_key = super(SessionStore, self).session_key
super(SessionStore, self).cycle_key()
self.save()
Cart.objects.filter(session_key=old_session_key).update(session_key=self.session_key)

deferred email sending in django?

Is there an easy way to be able to send an email at a later time, say Aug 1, 2012 6 pm? I have tried to read some documentation on django-mailer, but I could not get to an answer.
I am starting out in web development so may not be able to hack the existing application of django-mailer to get this done.
Celery can fit your need.
First set up a celery task:
#task
def sendmail():
pass
Send a mail later, an example from the doc:
from datetime import datetime, timedelta
tomorrow = datetime.now() + timedelta(days=1)
sendmail.apply_async(args=[], eta=tomorrow)

django - str of datetime with user's timezone

I'm trying to build a blurb of text that I'll ultimately send in an SMS message. The blurb includes text of a datetime object that has UTC as the timezone, but needs the text to be localized for the user's timezone instead. I have the user's timezone stored in the db.
I know that I can use timezone.activate() and timezone.deactivate() to change the current timezone, but I don't know if that's the best thing to do, when all I want is the text of the datetime to print out in the user's local timezone. I don't know if changing the current timezone will have unwanted system consequences, even if just for a short time.
Activating timezone using timezone.activate() will only affect current request, so there is really no "unwanted system consequences" to worry about.
By activating the timezone in middleware:
from django.utils import timezone
class TimezoneMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated():
timezone.activate(request.user.get_profile().timezone)
you'll be able to render user's local time by using {{ datatime }} parameter in your SMS template.