Repository: jazzband/django-axes

Issue:
Issue loading default settings on production server
Just launching a project onto AWS (fargate) servers & we're unable to login due to a missing setting. Although it's one of the axes defaults, so it's not being loaded properly.

This is happening only on the AWS setup, unable to replicate locally or within our docker setup which builds our fargate containers when running `docker-compose`. Which makes this an awkward issue to raise.

Trying to login via admin results in the following;

```bash
AttributeError: 'Settings' object has no attribute 'AXES_PROXY_ORDER'
  File "django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "django/contrib/admin/sites.py", line 399, in login
    return LoginView.as_view(**defaults)(request)
  File "django/views/generic/base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "django/utils/decorators.py", line 45, in _wrapper
    return bound_method(*args, **kwargs)
  File "django/views/decorators/debug.py", line 76, in sensitive_post_parameters_wrapper
    return view(request, *args, **kwargs)
  File "django/utils/decorators.py", line 45, in _wrapper
    return bound_method(*args, **kwargs)
  File "django/utils/decorators.py", line 142, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "django/utils/decorators.py", line 45, in _wrapper
    return bound_method(*args, **kwargs)
  File "django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "django/contrib/auth/views.py", line 61, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "django/views/generic/base.py", line 97, in dispatch
    return handler(request, *args, **kwargs)
  File "django/views/generic/edit.py", line 141, in post
    if form.is_valid():
  File "django/forms/forms.py", line 185, in is_valid
    return self.is_bound and not self.errors
  File "django/forms/forms.py", line 180, in errors
    self.full_clean()
  File "django/forms/forms.py", line 382, in full_clean
    self._clean_form()
  File "django/forms/forms.py", line 409, in _clean_form
    cleaned_data = self.clean()
  File "django/contrib/auth/forms.py", line 205, in clean
    self.user_cache = authenticate(self.request, username=username, password=password)
  File "django/contrib/auth/__init__.py", line 73, in authenticate
    user = backend.authenticate(request, **credentials)
  File "axes/helpers.py", line 459, in inner
    return func(*args, **kwargs)
  File "axes/backends.py", line 44, in authenticate
    if AxesProxyHandler.is_allowed(request, credentials):
  File "axes/handlers/proxy.py", line 82, in is_allowed
    cls.update_request(request)
  File "axes/handlers/proxy.py", line 69, in update_request
    request.axes_ip_address = get_client_ip_address(request)
  File "axes/helpers.py", line 161, in get_client_ip_address
    proxy_order=settings.AXES_PROXY_ORDER,
  File "django/conf/__init__.py", line 80, in __getattr__
    val = getattr(self._wrapped, name)
```

Looking at `django-appconf` we assume that the issues comes from this section of code;

```python
        prefix = getattr(meta, 'prefix', getattr(meta, 'app_label', None))
        if prefix is None:
            # Figure out the prefix by looking one level up.
            # For 'django.contrib.sites.models', this would be 'sites'.
            model_module = sys.modules[new_class.__module__]
            prefix = model_module.__name__.split('.')[-2]
```

Therefore the solution to this would be to set the prefix via the meta in `AxesAppConf` to avoid sniffing the module name & be explicit about the setting prefix.


Discussion:

Resolving patch:
diff --git a/axes/conf.py b/axes/conf.py
--- a/axes/conf.py
+++ b/axes/conf.py
@@ -5,6 +5,9 @@
 
 
 class AxesAppConf(AppConf):
+    class Meta:
+        prefix = "axes"
+
     # disable plugin when set to False
     ENABLED = True
 


Test patch: