Skip to main content

Lazy signup for Django

Project description


django-lazysignup is a package designed to allow users to interact with a site as if they were authenticated users, but without signing up. At any time, they can convert their temporary user account to a real user account.

django-lazysignup is beta software. Bug reports, patches and extensions are welcomed. While this package is in beta, backwards-compatibility will be maintained for a single point release and a DeprecationWarning issued.


Tested on Django 1.2.x, though should work on Django 1.0 and later (although you will need to customise one of the templates.) It requires django.contrib.auth to be in the INSTALLED_APPS list.


django-lazysignup can be installed with your favourite package management tool from PyPI:

pip install django-lazysignup

Once that’s done, you need to add lazysignup to your INSTALLED_APPS. You will also need to add lazysignup’s authentication backend to your site’s AUTHENTICATION_BACKENDS setting:


If you are using Django prior to 1.2, you should override the lazysignup/convert.html template to remove the {% csrf_token %} template tag. This may be handled more elegantly in a future release.

Finally, you need to add lazysignup to your URLConf, using something like this:

urlpatterns += (''
    (r'^convert/', include('lazysignup.urls')),


The package works by creating temporary user accounts based on a user’s session key whenever a flagged view is requested. You can specify which views trigger this behaviour using the lazysignup.decorators.allow_lazy_user decorator.

When an anonymous user requests such a view, a temporary user account will be created for them, and they will be logged in. The user account will have an unusable password set, so that it can’t be used to log in as a regular user. The way to tell a regular use from a temporary user is to call the is_lazy_user() function from lazysignup.templatetags.lazysignup_tags. If this returns True, then the user is temporary. Note that user.is_anonymous() will return False and user.is_authenticated() will return True. See below for more information on is_lazy_user.

A view is provided to allow such users to convert their temporary account into a real user account by providing a username and a password.

A Django management command is provided to clear out stale, uncoverted user accounts - although this depends on your use of database-backed sessions, and assumes that all user accounts with an expired session are safe to delete. This may not be the case for all apps, so you may wish to provide your own cleaning script.

The allow_lazy_user decorator

Use this decorator to indicate that accessing the view should cause anonymous users to have temporary accounts created for them.

For example:

from django.http import HttpResponse
from lazysignup.decorators import allow_lazy_user

def my_view(request):
  return HttpResponse(request.user.username)

When accessing the above view, a very simple response containing the generated username will be displayed.

The is_lazy_user template filter

This template filter (which can also be imported directly and used in your view code) will return True if the user is a generated user. You need to pass it the user to test. For example, a site navigation template might look like this:

{% load i18n lazysignup_tags %}

<nav id="account-bar">
    <li><a href="{% url home %}">{% trans "Home" %}</a></li>
    {% if not user|is_lazy_user %}
      <li><a href="#">{% trans "Account" %}</a></li>
      <li><a href="{% url auth_logout %}">{% trans "Log out" %}</a></li>
    {% else %}
      <li><a href="{% url lazysignup_convert %}">{% trans "Save your data" %}</a> {% trans "by setting a username and password" %}</li>
    {% endif %}

This filter is very simple, and can be used directly in view code, or tests. For example:

from lazysignup.templatetags.lazysignup_tags import is_lazy_user

def testIsLazyUserAnonymous(self):
    user = AnonymousUser()
    self.assertEqual(False, is_lazy_user(user))

User agent blacklisting

The middleware will not created users for certain requests from blacklisted user agents. This is simply a fairly crude method for preventing many spurious users being created by passing search engines.

The blacklist is specified with the USER_AGENT_BLACKLIST setting. This should be an iterable of regular expression strings. If the user agent string of a request matches a regex (search() is used, so the match can be anywhere in the string) then a user will not be created.

If the list is not specified, then the default is as follows

  • slurp
  • googlebot
  • yandex
  • msnbot
  • baiduspider

Specifying your own USER_AGENT_BLACKLIST will replace this list.

Using the convert view

Users will be able to visit the /convert/ view. This provides a form with a username, password and password confirmation. As long as they fill in valid details, their temporary user account will be converted into a real user account that they can log in with as usual.

You may pass your own form class into the convert view in order to customise user creation. The code requires expects the following:

  • It expects to be able to create the form passing in the generated User object with an instance kwarg (in general, this is fine when using a ModelForm based on the User model)
  • It expects to be able to call save() on the form to convert the user to a real user
  • It expects to be able to call a get_credentials() method on the form to obtain a set of credentials to authenticate the new user with. The result of this call should be a dictionary suitable for passing to django.contrib.auth.authenticate(). Typically, this would be a dict with username and password keys - but this may vary if you’re using a different authentication backend.

The default configuration, using the provided UserCreationForm, should be enough for most users, but the customisation point is there if you need it.


Over time, a number of user accounts that haven’t been converted will build up. To avoid performance problems from an excessive number of user accounts, it’s recommended that the remove_expired_users management command is run on a regular basis. It runs from the command line:

python remove_expired_users

In a production environment, this should be run from cron or similar.

This works be removing user accounts from the system whose associated sessions are no longer in the session table. user.delete() is called for each user, so related data will be removed as well.

Note of course that these deletes will cascade, so if you need to keep data associated with such users, you’ll need to write your own cleanup job. It also expects that you’re using database backed sessions. If that’s not the case, then you’ll again need to write your own cleanup.

Helping Out

If you want to add a feature or fix a bug, please go ahead! Fork the project on GitHub, and when you’re done with your changes, let me know. Fixes and features with tests have a greater chance of being merged. To run the tests, do:

python test --settings=lazysignup.test_settings lazysignup

Note that the tests require the mock package.



This version simplifies the implementation significantly by removing the requirement for a middleware. This is a backwards-incompatible change as the lazysignup.middleware module no longer exists, and needs to be removed from your MIDDLEWARE_CLASSES list.

This change means that you can no longer disable the software by removing the middleware class from your configuration. You might want to do this temporarily in development, for example, when testing that your views still work when an anonymous user is presented to them (perhaps through the user agent blacklisting functionality).

To this end, support for a new settings has been added: ENABLE_LAZYSIGNUP. It’s set to True by default. Setting it to False will both prevent automatic login and automatic user creation.

The allow_lazy_user decorator no longer has to be first in the decorator list. Thanks to Jauco Noordzij for pointing this out initially.

There is now also an is_lazy_user template filter, which will return True if the current user is an automatically created user. It will return False for users who are anonymous, or were authenticated by some other means. Thanks again to Jauco Noordzij for the idea for this feature.

Note that the is_lazy_user filter (which you can also just import and use directly in your view code) is the preferred way of checking for an automatically created user. has_usable_password will continue to return False for such users, but is no longer the canonical way to identify them. Users authenticated with other authentication backends (for example, LDAP backends) may return False when has_usable_password is called.


This version fixes a number of issues, adds new features and has backwards- incompatible changes:

  • Fix a RST issue in README
  • Fix an issue where a POST to the convert view would break if the user was anonymous. The convert view now redirects to the LOGIN_URL by default, parameterised in the view.

New features:

  • It is now easier to customise the process of converting a lazy user into a real user. Previous versions allowed a custom form to be passed to the convert view, but the code always expected a username and password1 field to get credentials from to log the user in. Now, a new get_credentials() method is called on the form to obtain these credentials.
  • The tests module now includes a no_lazysignup decorator that you can apply to a method on your test case, which removes the lazy signup middleware for the duration of that test only. This is useful for testing what happens when a view that is marked with with the allow_lazy_user decorator ends up with an anonymous user (most commonly, when a search engine visits).

Backwards-incompatible changes:

  • Generated usernames are now based on the session key, rather than actually being the session key. This is to avoid a potential security issue where an app might simply display a username, giving away a significant part of the user’s session key. The username is now generated from a SHA1 hash of the session key. This change means that existing generated users will become invalid.


This version introduces a backwards-incompatible changes, renaming the @allow_lazy decorator to @allow_lazy_user. This is to avoid confusion with the decorator of the same name in django.utils.functional.


This version fixes a number of issues:

  • Correct a duplicated test
  • Fix a bug where a new user would not be created if they already had a session key.

It also introduces a new feature, user agent blacklisting. This aims to prevent explosive growth in the number of users created by search engines, etc. This feature is still in test, so patches and feedback welcome. Note that this introduces a backwards-incompatibly behaviour. Prior to this release, it was safe to assume that all views marked with the @allow_lazy decorator would receive an authenticated user. This is now no longer the case.

See the README.rst file for more information.


  • Compatibility fixes for Django <1.2
  • The convert view is now itself lazy


  • Fix some packaging errors


  • Initial release

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for django-lazysignup, version 0.5.0
Filename, size File type Python version Upload date Hashes
Filename, size django-lazysignup-0.5.0.tar.gz (18.2 kB) File type Source Python version None Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN SignalFx SignalFx Supporter DigiCert DigiCert EV certificate StatusPage StatusPage Status page