{"id":21147,"library":"django-defender","title":"Django Defender","description":"Redis-based Django app that locks out users after too many failed login attempts. Current version 0.9.8, requires Python ~=3.7 and Django >=3.2 (or 4.x). Maintained by Jazzband. Release cadence: irregular, last release Jan 2024.","status":"active","version":"0.9.8","language":"python","source_language":"en","source_url":"https://github.com/kencochrane/django-defender","tags":["django","security","brute-force","redis"],"install":[{"cmd":"pip install django-defender","lang":"bash","label":"pip install django-defender"}],"dependencies":[{"reason":"Required; used for tracking failed attempts and lockout state.","package":"redis","optional":false},{"reason":"Required; Django web framework.","package":"django","optional":false}],"imports":[{"note":"watch_login is a decorator, not a direct import from the package root.","symbol":"watch_login","correct":"from defender.decorators import watch_login"},{"note":"Common utility function to check if a user is locked out.","symbol":"is_already_locked","correct":"from defender import is_already_locked"},{"note":"Used to manually unlock a user.","symbol":"unlock","correct":"from defender import unlock"}],"quickstart":{"code":"INSTALLED_APPS = [\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'defender',\n]\n\n# settings.py\nDEFENDER_REDIS_URL = os.environ.get('DEFENDER_REDIS_URL', 'redis://localhost:6379/0')\n\nMIDDLEWARE = [\n    'django.middleware.security.SecurityMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n    'defender.middleware.FailedLoginMiddleware',\n]\n\n# Add to urlpatterns in urls.py\nfrom defender import urls as defender_urls\nurlpatterns += [\n    path('defender/', include(defender_urls)),\n]\n\n# run migrations\n# python manage.py migrate defender","lang":"python","description":"Basic setup: add 'defender' to INSTALLED_APPS, configure REDIS_URL, add FailedLoginMiddleware, include defender URLs."},"warnings":[{"fix":"Ensure Redis is running and DEFENDER_REDIS_URL is correctly configured.","message":"django-defender requires a running Redis server. Without it, login attempts will fail silently or raise a ConnectionError.","severity":"breaking","affected_versions":"all"},{"fix":"Set DEFENDER_COOLOFF_TIME (or DEFENDER_ATTEMPT_COOLOFF_TIME / DEFENDER_LOCKOUT_COOLOFF_TIME from v0.9.6) to your desired value in seconds.","message":"The default COOLOFF_TIME is 300 seconds (5 minutes). After that, the counter resets. This can be confusing if you expect lockout to persist longer.","severity":"gotcha","affected_versions":"all"},{"fix":"Ensure the view sets request.user before calling watch_login, or use the middleware instead.","message":"watch_login decorator expects the request object to have a user attribute. If used on a custom login view that doesn't set request.user, it will raise AttributeError.","severity":"gotcha","affected_versions":"all"},{"fix":"Upgrade to Python >=3.7 and Django >=3.2.","message":"Python 3.6 and Django 3.1 support dropped in v0.9.2. Django 2.2 dropped in v0.9.3.","severity":"deprecated","affected_versions":">=0.9.2"}],"env_vars":null,"last_verified":"2026-04-27T00:00:00.000Z","next_check":"2026-07-26T00:00:00.000Z","problems":[{"fix":"Use 'from defender.decorators import watch_login' instead.","cause":"Trying to import watch_login directly from the defender package instead of the decorators submodule.","error":"AttributeError: module 'defender' has no attribute 'watch_login'"},{"fix":"Start Redis or set DEFENDER_REDIS_URL to the correct Redis URL.","cause":"Redis server is not running or not reachable at the configured URL.","error":"ConnectionError: Error 111 connecting to localhost:6379. Connection refused."},{"fix":"Add 'DEFENDER_REDIS_URL = os.environ.get('DEFENDER_REDIS_URL', 'redis://localhost:6379/0')' to settings.py.","cause":"DEFENDER_REDIS_URL is not set in Django settings.","error":"django.core.exceptions.ImproperlyConfigured: The DEFENDER_REDIS_URL setting must not be empty."}],"ecosystem":"pypi","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}