Django on Google App Engine in 13 simple steps

In this tutorial I will show you how to get a simple datastore-backed Django application up and running on the Google App Engine. I will assume that you are somewhat familiar with Django.

Update 1: You can download the full set of files from here. Make sure to fix the sys.path in main.py.

Update 2: There is now a Turkish translation of this tutorial, courtesy of Türker Sezer.

Update 3: Now in Russian as well.

Update 4: Brazilian Portuguese tranlation by Marcio Andrey Oliveira.


Step 1: Register an app name and install the development kit per the instructions.

Step 2: Create a directory for your application—for this tutorial my application is called mashname:

tmp$ mkdir mashname
tmp$ cd mashname

Step 3: Add a file called main.py to your new directory:

# main.py

import os, sys
os.environ["DJANGO_SETTINGS_MODULE"] = "mashname.settings"
sys.path.append("/home/brox/tmp/mashname")

# Google App Engine imports.
from google.appengine.ext.webapp import util

# Force Django to reload its settings.
from django.conf import settings
settings._target = None

import django.core.handlers.wsgi
import django.core.signals
import django.db
import django.dispatch.dispatcher

# Log errors.
#django.dispatch.dispatcher.connect(
#   log_exception, django.core.signals.got_request_exception)

# Unregister the rollback event handler.
django.dispatch.dispatcher.disconnect(
django.db._rollback_on_exception,
django.core.signals.got_request_exception)

def main():
    # Create a Django application for WSGI.
    application = django.core.handlers.wsgi.WSGIHandler()

    # Run the WSGI CGI handler with that application.
    util.run_wsgi_app(application)

if __name__ == "__main__":
    main()

This is basically the same file as suggested here, except I had to set the Python path to be able to test locally. I also had to set the DJANGO_SETTINGS_MODULE—this might not be necessary when running on the App Engine. I had to disable the error logging which I was not able to get working.

Step 4: Add a file called app.yaml to the same directory:

application: mashname
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: main.py

Make sure to get the application name right.

Step 5: From your mashname directory, create a new Django project:

tmp/mashname$ django-admin.py startproject mashname

(I’m assuming that your current Django setup is working as it should.)

Step 6: You should now be able to test your application:

tmp/mashname$ cd ..
tmp$ dev_appserver.py mashname
INFO     2008-04-08 19:08:10,023 appcfg.py] Checking for updates to the SDK.
INFO     2008-04-08 19:08:10,384 appcfg.py] The SDK is up to date.
INFO     2008-04-08 19:08:10,404 dev_appserver_main.py] Running application mash
name on port 8080: http://localhost:8080

Point your browser towards http://127.0.0.1:8080/ and you should get the standard Django It worked! message.

Step 7: Create a Django app within the project:

tmp$ cd mashname
tmp/mashname$ python mashname/manage.py startapp main

Step 8: Now it is time to add a model. We will be creating a simple application that logs all visitors to the data store and displays their IP address and time of visit. Edit ~/mashname/mashname/main/models.py so that it looks like this:

# models.py

from google.appengine.ext import db

class Visitor(db.Model):
    ip = db.StringProperty()
    added_on = db.DateTimeProperty(auto_now_add=True)

There is no need to sync the database since we are not using regular Django models.

Step 9: Now we create a view that is responsible for both adding data to the Visitor model and showing the previous visitors. Edit views.py (in the same directory as models.py) so that it does what we want:

# views.py

from django.http import HttpResponse

from mashname.main.models import Visitor

def main(request):
    visitor = Visitor()
    visitor.ip = request.META["REMOTE_ADDR"]
    visitor.put()

    result = ""
    visitors = Visitor.all()
    visitors.order("-added_on")

    for visitor in visitors.fetch(limit=40):
        result += visitor.ip + u" visited on " + unicode(visitor.added_on) + u""

    return HttpResponse(result)

Step 10: Finally, make your urls.py point towards the view:

# urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns("",
    (r"^$", "mashname.main.views.main"),
)

Step 11: Test your application (as in step 6) and everything should hopefully work. For each page reload a new entry is added to the Visitor model and shown in the view.

Step 12: Upload your application to the Google App Engine:

tmp$ appcfg.py update mashname

For the first upload you will have to provide the mail address and password for your Google account.

Step 13: Enjoy! To view the final results, go to http://mashname.appspot.com/.

58 thoughts on “Django on Google App Engine in 13 simple steps

  1. Been having some trouble myself with some of the restrictions they’ve placed on it… but great writeup, i think i might take a django route so that i can at least get something up there to test the service out.
    Thanks,
    Brent

  2. Thanks for the intro. Django is certainly an interesting-looking environment to program in. (I’ve got up through Ch. 4 of the Django Book this evening).

    You should fix your WordPress theme. It make the quotes slanty, which makes them interpreted as different characters than the standard single quote. I copied the add path line from your code and got a syntax error because of it.

    Thanks again for documenting Google’s hacked-up version of Django better than they do. =)

  3. @Brenton: Thanks for telling me about the single quote problem. I have replaced them with double quotes.

  4. @Shabda: It should be accessible most of time—try again. I get the occassional error message in the App Engine log file:

    This request used a high amount of CPU, and was roughly 2.2 times over the average request CPU limit. High CPU requests have a small quota, and if you exceed this quota, your app will be temporarily disabled.

    Don’t really know why, though. I added a link to the full set of files at the top of the page.

  5. Hi,

    this is helping me a great deal to get started. Now I’m using a template file and I’m trying to add a CSS file to it, do you know which file do I have to modify to let Django find my CSS file?

    Thanks in advance

  6. @Phuong: I just wrote a new tutorial that shows you how to serve templates and static files. Have a look at it here.

  7. Thanks for the excellent article Thomas.

    I am encountering the following error with step 6 (any thoughts on how to get past this?):

    ERROR 2008-04-27 06:17:40,287 dev_appserver.py] Encountered error loading module “main”: :
    ERROR 2008-04-27 06:17:40,287 dev_appserver.py] Parent package initialization files are present, but must be broken
    ERROR 2008-04-27 06:17:40,289 dev_appserver.py] Exception encountered handling request
    Traceback (most recent call last):
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 2247, in _HandleRequest
    base_env_dict=env_dict)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 334, in Dispatch
    base_env_dict=base_env_dict)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1743, in Dispatch
    self._module_dict)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1654, in ExecuteCGI
    reset_modules = exec_script(handler_path, cgi_path, hook)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1550, in ExecuteOrImportScript
    handler_path, cgi_path, import_hook)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1479, in LoadTargetModule
    module_code = import_hook.get_code(module_fullname)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 806, in decorate
    return func(self, *args, **kwargs)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1350, in get_code
    full_path, search_path, submodule = self.GetModuleInfo(fullname)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 806, in decorate
    return func(self, *args, **kwargs)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1305, in GetModuleInfo
    source_file, pathname, description = self.FindModuleRestricted(submodule, fullname, search_path)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 806, in decorate
    return func(self, *args, **kwargs)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1136, in FindModuleRestricted
    raise CouldNotFindModuleError()
    CouldNotFindModuleError
    INFO 2008-04-27 06:17:40,290 dev_appserver.py] “GET / HTTP/1.1” 500 –
    ERROR 2008-04-27 06:17:40,351 dev_appserver.py] Encountered error loading module “main”: :
    ERROR 2008-04-27 06:17:40,351 dev_appserver.py] Parent package initialization files are present, but must be broken
    ERROR 2008-04-27 06:17:40,353 dev_appserver.py] Exception encountered handling request
    Traceback (most recent call last):
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 2247, in _HandleRequest
    base_env_dict=env_dict)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 334, in Dispatch
    base_env_dict=base_env_dict)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1743, in Dispatch
    self._module_dict)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1654, in ExecuteCGI
    reset_modules = exec_script(handler_path, cgi_path, hook)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1550, in ExecuteOrImportScript
    handler_path, cgi_path, import_hook)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1479, in LoadTargetModule
    module_code = import_hook.get_code(module_fullname)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 806, in decorate
    return func(self, *args, **kwargs)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1350, in get_code
    full_path, search_path, submodule = self.GetModuleInfo(fullname)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 806, in decorate
    return func(self, *args, **kwargs)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1305, in GetModuleInfo
    source_file, pathname, description = self.FindModuleRestricted(submodule, fullname, search_path)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 806, in decorate
    return func(self, *args, **kwargs)
    File “/usr/local/google_appengine/google/appengine/tools/dev_appserver.py”, line 1136, in FindModuleRestricted
    raise CouldNotFindModuleError()
    CouldNotFindModuleError
    INFO 2008-04-27 06:17:40,354 dev_appserver.py] “GET /favicon.ico HTTP/1.1” 500 –

  8. @Dondi: I haven’t seen that particular error message before. It looks like there is something wrong with one of your files (“Parent package initialization files are present, but must be broken”).

    Check that the Django application you created in the previous step (“main”) contains all the necessary files (“__init__.py”, “models.py” and “views.py”). Then verify that the Django project directory has the files “__init__.py”, “manage.py”, “settings.py” and “urls.py”.

    Finally, make sure that there are no errors in the App Engine configuration files (“main.py” and “app.yaml”). You might have the wrong path or project name.

    If you downloaded the zip file onto a Linux box: Try running the dos2unix utility on the entire directory. I wrote the tutorial on a Windows machine so there might be some line feed/carriage return encoding weirdness.

  9. Thanks for a great post. I have been looking at _a_lot_ of App Engine tutorials today, and your’s is by far the best written, easiest to follow and no-hassle one. Great work!

  10. Hi Thomas

    Thanks for the article.

    I have Windows Vista, Django 1.02 and Python 2.6. I am getting the following error on step 6.

    Python 2.6.1: C:\Python26\python.exe
    Fri Feb 13 08:10:21 2009

    A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.

    C:\Program Files\Google\google_appengine\google\appengine\tools\dev_appserver.py in _HandleRequest(self=)
    2459 infile,

    2460 outfile,

    2461 base_env_dict=env_dict)

    2462 finally:

    2463 self.module_manager.UpdateModuleFileModificationTimes()

    base_env_dict undefined, env_dict = {‘APPLICATION_ID’: ‘removals’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REQUEST_METHOD’: ‘GET’, ‘SERVER_NAME’: ‘localhost’, ‘SERVER_PORT’: ‘8080’, ‘SERVER_PROTOCOL’: ‘HTTP/1.0’, ‘SERVER_SOFTWARE’: ‘Development/1.0′}
    C:\Program Files\Google\google_appengine\google\appengine\tools\dev_appserver.py in Dispatch(self=, relative_url=’/’, path=None, headers=, infile=, outfile=, base_env_dict={‘APPLICATION_ID’: ‘removals’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REQUEST_METHOD’: ‘GET’, ‘SERVER_NAME’: ‘localhost’, ‘SERVER_PORT’: ‘8080’, ‘SERVER_PROTOCOL’: ‘HTTP/1.0’, ‘SERVER_SOFTWARE’: ‘Development/1.0’})
    352 infile,

    353 outfile,

    354 base_env_dict=base_env_dict)

    355

    356 return

    base_env_dict = {‘APPLICATION_ID’: ‘removals’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REQUEST_METHOD’: ‘GET’, ‘SERVER_NAME’: ‘localhost’, ‘SERVER_PORT’: ‘8080’, ‘SERVER_PROTOCOL’: ‘HTTP/1.0’, ‘SERVER_SOFTWARE’: ‘Development/1.0′}
    C:\Program Files\Google\google_appengine\google\appengine\tools\dev_appserver.py in Dispatch(self=, relative_url=’/’, path=’main.py’, headers=, infile=, outfile=, base_env_dict={‘APPLICATION_ID’: ‘removals’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REQUEST_METHOD’: ‘GET’, ‘SERVER_NAME’: ‘localhost’, ‘SERVER_PORT’: ‘8080’, ‘SERVER_PROTOCOL’: ‘HTTP/1.0’, ‘SERVER_SOFTWARE’: ‘Development/1.0’})
    1869 infile,

    1870 outfile,

    1871 self._module_dict)

    1872 handler.AddDebuggingConsole(relative_url, env, outfile)

    1873 finally:

    self = , self._module_dict = {‘Cookie’: , ‘StringIO’: , ‘UserDict’: , ‘__main__’: , ‘_abcoll’: , ‘_collections’: , ‘_functools’: , ‘_hashlib’: , ‘_locale’: , ‘_struct’: , …}
    C:\Program Files\Google\google_appengine\google\appengine\tools\dev_appserver.py in ExecuteCGI(root_path=r’C:\Program Files\Google\google_appengine\removals’, handler_path=’main.py’, cgi_path=r’C:\Program Files\Google\google_appengine\removals\main.py’, env={‘APPLICATION_ID’: ‘removals’, ‘AUTH_DOMAIN’: ‘gmail.com’, ‘CONTENT_LENGTH’: ”, ‘CONTENT_TYPE’: ‘application/x-www-form-urlencoded’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘GATEWAY_INTERFACE’: ‘CGI/1.1’, ‘HTTP_ACCEPT’: ‘text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8’, ‘HTTP_ACCEPT_CHARSET’: ‘ISO-8859-1,utf-8;q=0.7,*;q=0.7’, ‘HTTP_ACCEPT_LANGUAGE’: ‘en-us,en;q=0.5’, ‘HTTP_CACHE_CONTROL’: ‘max-age=0’, …}, infile=, outfile=, module_dict={‘Cookie’: , ‘StringIO’: , ‘UserDict’: , ‘__main__’: , ‘_abcoll’: , ‘_collections’: , ‘_functools’: , ‘_hashlib’: , ‘_locale’: , ‘_struct’: , …}, exec_script=)
    1787 logging.debug(‘Executing CGI with env:\n%s’, pprint.pformat(env))

    1788 try:

    1789 reset_modules = exec_script(handler_path, cgi_path, hook)

    1790 except SystemExit, e:

    1791 logging.debug(‘CGI exited with status: %s’, e)

    reset_modules = True, exec_script = , handler_path = ‘main.py’, cgi_path = r’C:\Program Files\Google\google_appengine\removals\main.py’, hook =
    C:\Program Files\Google\google_appengine\google\appengine\tools\dev_appserver.py in ExecuteOrImportScript(handler_path=’main.py’, cgi_path=r’C:\Program Files\Google\google_appengine\removals\main.py’, import_hook=)
    1683 try:

    1684 if module_code:

    1685 exec module_code in script_module.__dict__

    1686 else:

    1687 script_module.main()

    module_code = <code object at 02593458, file “C:\Prog…oogle\google_appengine\removals\main.py”, line 2>, script_module = , script_module.__dict__ = {‘__builtins__’: {‘ArithmeticError’: , ‘AssertionError’: , ‘AttributeError’: , ‘BaseException’: , ‘BufferError’: , ‘BytesWarning’: , ‘DeprecationWarning’: , ‘EOFError’: , ‘Ellipsis’: Ellipsis, ‘EnvironmentError’: , …}, ‘__doc__’: None, ‘__file__’: r’C:\Program Files\Google\google_appengine\removals\main.py’, ‘__loader__’: , ‘__name__’: ‘main’, ‘__package__’: None, ‘django’: , ‘os’: , ‘settings’: , ‘sys’: , …}
    C:\Program Files\Google\google_appengine\removals\main.py in ()
    23

    24 # Unregister the rollback event handler.

    25 django.dispatch.dispatcher.disconnect(

    26 django.db._rollback_on_exception,

    27 django.core.signals.got_request_exception)

    django = , django.dispatch = , django.dispatch.dispatcher = , django.dispatch.dispatcher.disconnect undefined, django.db = , django.db._rollback_on_exception = , django.core = , django.core.signals = , django.core.signals.got_request_exception =

    : ‘module’ object has no attribute ‘disconnect’
    args = (“‘module’ object has no attribute ‘disconnect'”,)
    message = “‘module’ object has no attribute ‘disconnect'”

    Thanks

  11. Simon, one of the error messages complain about not finding the method disconnect in the Django dispatcher.py file. The django.dispatch module was heavily refactored before the Django 1.0 release (http://code.djangoproject.com/browser/django/trunk/django/dispatch/dispatcher.py?rev=8223), and the disconnect method is now deprecated. Could there be some kind of mismatch between your version of Django and the version that Google App Engine expects?

    I haven’t been following App Engine developments too closely, but I believe they still aren’t compatible with Django 1.0.

    You should also check that your local Django installation is working in the first place, maybe be going through the original Django tutorial.

  12. Great tutorial, thanks.

    It helps understanding the basics preparing for the next step,

    using app engine helper
    presented in your tutorial of 17 February 2009
    http://blog.pas.net.au/2009/02/17/django-and-google-app-engine-tutorial/

    or even using a ready made project like appEnginePatch for deploying native Django on appEngine, see
    http://code.google.com/p/app-engine-patch/wiki/Documentation
    or visit app engine patch discussion group
    http://groups.google.com/group/app-engine-patch

    Konrad

  13. hi,

    I executed the following command: appcfg.py update , to upload my application onto Google App Engine

    It is giving me the following error:

    Scanning files on local disk.
    Initiating update.
    2009-05-21 15:36:44,921 ERROR appcfg.py:1241 An unexpected error occurred. Abort
    ing.
    Traceback (most recent call last):
    File “E:\GoogleAppEngine\google\appengine\tools\appcfg.py”, line 1219, in DoUp
    load
    missing_files = self.Begin()
    File “E:\GoogleAppEngine\google\appengine\tools\appcfg.py”, line 1014, in Begi
    n
    version=self.version, payload=self.config.ToYAML())
    File “E:\GoogleAppEngine\google\appengine\tools\appengine_rpc.py”, line 344, i
    n Send
    f = self.opener.open(req)
    File “E:\Python-2.5.4\lib\urllib2.py”, line 387, in open
    response = meth(req, response)
    File “E:\Python-2.5.4\lib\urllib2.py”, line 498, in http_response
    ‘http’, request, response, code, msg, hdrs)
    File “E:\Python-2.5.4\lib\urllib2.py”, line 425, in error
    return self._call_chain(*args)
    File “E:\Python-2.5.4\lib\urllib2.py”, line 360, in _call_chain
    result = func(*args)
    File “E:\Python-2.5.4\lib\urllib2.py”, line 506, in http_error_default
    raise HTTPError(req.get_full_url(), code, msg, hdrs, fp)
    HTTPError: HTTP Error 403: Forbidden
    Error 403: — begin server output —
    You do not have permission to modify this app (app_id=u’mashname’).
    — end server output —

    Can somebody please help me out

  14. Thanks for your great post…Its very useful and simple to develop the Django app on Google app Engine.
    I m using windows-XP (SP2).
    I Installed Django-1.1,python2.6 and google appengine…
    I make a simple App named as “vichintan”.
    I Registered an app on my appengine account with the name of “vichintan”.
    and the url is “vichintan.appspot.com”
    I m using Windows Google Appengine Launcher…
    I Test this app successfully,when i was testing on the Launcher.

    i made this app with the help of your above post.
    and the difference is,where you name the “mashname” i used “vichintan”
    here is my app.yaml files code

    #########################
    application: vichintan
    version: 1
    runtime: python
    api_version: 1

    handlers:
    – url: /.*
    script: main.py
    ########################

    When m going to Upload my application(vichintan) to the Google App Engine…
    Its shows me the following error..

    ###########################################################################

    2010-04-05 21:19:41 Running command: “[‘C:\\Python26\\pythonw.exe’, ‘C:\\Program Files\\Google\\google_appengine\\appcfg.py’, ‘–no_cookies’, u’–email=codes47@gmail.com’, ‘–passin’, ‘update’, ‘C:\\Program Files\\Google\\google_appengine\\vichintan’]”
    Password for codes47@gmail.com: C:\Program Files\Google\google_appengine\appcfg.py:41: DeprecationWarning: the sha module is deprecated; use the hashlib module instead
    os.path.join(DIR_PATH, ‘lib’, ‘antlr3’),
    C:\Program Files\Google\google_appengine\google\appengine\tools\dev_appserver_login.py:33: DeprecationWarning: the md5 module is deprecated; use hashlib instead
    import md5
    Application: vichintan; version: 1.
    Server: appengine.google.com.
    Scanning files on local disk.
    Initiating update.
    Error 401: — begin server output —
    Must authenticate first.
    — end server output —
    2010-04-05 21:20:05 (Process exited with code 1)

    You can close this window now.

    ########################################################################

    Thanking you…

  15. Hello .
    I have a problem with settings of /google_appengine:
    I unzip the /google_appengine on your folder example:
    mashname]$ ls
    app.yaml index.yaml main.py mashname settings.py urls.py
    google_appengine __init__.py manage.py models.py settings.pyc views.py
    But i don’t know how to make working with django.
    Django working fine on my computer, but on /google_appengine
    python test.py say :
    google_appengine]$ python test.py
    Traceback (most recent call last):
    File “test.py”, line 1, in
    from google.appengine.ext import webapp
    File “/home/tutor/mashname/google_appengine/google/appengine/ext/webapp/__init__.py”, line 68, in
    import webob
    ImportError: No module named webob
    And
    google_appengine]$ ./appcfg.py update mashname
    Traceback (most recent call last):
    File “./appcfg.py”, line 68, in
    run_file(__file__, globals())
    File “./appcfg.py”, line 64, in run_file
    execfile(script_path, globals_)
    File “/home/tutor/mashname/google_appengine/google/appengine/tools/appcfg.py”, line 59, in
    from google.appengine.tools import appengine_rpc
    File “/home/tutor/mashname/google_appengine/google/appengine/tools/appengine_rpc.py”, line 32, in
    https_handler = urllib2.HTTPSHandler
    AttributeError: ‘module’ object has no attribute ‘HTTPSHandler’
    What is wrong ? What is correct way to set appengine to working with django?
    Thank you !

  16. @Catalin: I’m not sure what is happening, but from what I can see there are some missing dependencies. The webob module is missing and it looks like urllib2 doesn’t have SSL support. Have a look at this page: http://code.google.com/p/googleappengine/issues/detail?id=19

    I haven’t tested the code on the latest App Engine releases, so for all I know it might be using deprecated features.

  17. @mrkkrj: I was getting some errors that I didn’t have time to figure out, so I disabled storing of new visits.

Leave a Reply

Your email address will not be published. Required fields are marked *