我目前正在构建一个Django应用程序,它从外部源(通过HTTP)获取所有数据。因此,我不想使用自己的数据库。我已根据此SO问题中所述的解决方案实现了我自己的用户和远程用户后端:Django users and authentication from external source。我使用签名的cookie来存储会话。
实施上述规定后,我能够毫无错误地登录。但是,在检查用户是否在模板中进行身份验证时(或尝试在视图中将其注销时),我收到错误:
ValidationError at /products/
["'None' value must be an integer."]
在模板中的行
{% if user.is_authenticated %}
其中request.user作为模板变量用户在相应的视图中传递(我知道我不应该这样做,它只是暂时的)。
以下是其他一些相关的追溯:
/home/ines/.virtualenvs/diet/lib/python3.6/site-packages/django/contrib/auth/middleware.py in <lambda>
request.user = SimpleLazyObject(lambda: get_user(request))
...
/home/ines/.virtualenvs/diet/lib/python3.6/site-packages/django/contrib/auth/middleware.py in get_user
request._cached_user = auth.get_user(request)
...
/home/ines/.virtualenvs/diet/lib/python3.6/site-packages/django/contrib/auth/__init__.py in get_user
user_id = _get_user_session_key(request)
...
/home/ines/.virtualenvs/diet/lib/python3.6/site-packages/django/contrib/auth/__init__.py in _get_user_session_key
return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
错误发生在to_python()
调用中,其中文件django / db / models / fields / __ init__.py:return int(value)
中的行942在收到value='None'
时抛出异常。
现在让我发布我的应用程序中的所有(希望)相关代码。
settings.py
AUTHENTICATION_BACKENDS = ('products.backends.ApiAuthBackend',)
产品/ backends.py
from django.contrib.auth.backends import RemoteUserBackend
from products.api import API
from products.models import ApiUser
class ApiAuthBackend(RemoteUserBackend):
"""
Authenticate against the API.
"""
create_unknown_user = False # does not create a User object if it is not in the database
def authenticate(self, request, username=None, password=None):
api = API()
access_token = api.login(username=username, password=password)
if api.logged_in():
user = ApiUser()
user.username = username
request.session['access_token'] = access_token
return user
return None
def get_user(self, user_id):
return ApiUser(username=user_id)
产品/ models.py
from django.contrib.auth.models import AbstractUser
class ApiUser(AbstractUser):
def save(self, **kwargs):
"""
Disables saving to database.
"""
pass
objects = None
username = ''
first_name = ''
last_name = ''
email = ''
password = ''
groups = ''
user_permissions = ''
is_staff = False
is_active = True
is_superuser = False
last_login = None
date_joined = None
def get_group_permissions(self, obj=None):
return []
def get_all_permissions(self, obj=None):
return []
def has_perm(self, perm, obj=None):
return []
def has_perms(self, obj=None, **kwargs):
return []
def has_module_perms(self, app_label):
return []
进一步检查django / contrib / auth / __ init__.py,第154行:
request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
问题似乎是request.session[SESSION_KEY]
,其中SESSION_KEY = '_auth_user_id'
设置为'None'
。这是由于ApiUser类不再具有属性字段这一事实 - 我已经在models.py中覆盖了它们,因为我没有使用数据库。因此,我的ApiUser实例没有pk,因此SESSION_KEY属性设置为'None'
。当Django想要进行身份验证时,会话的SESSION_KEY属性会传递给前面提到的to_python()
函数,而该函数又会引发异常。
我的问题是,我该如何解决这个问题?我尝试将user.pk设置为1并在models.py中的authenticate函数中将session[SESSION_KEY]
手动设置为1,这导致我没有得到异常,但是其他数据写入会话(access_token)已经消失了。
提前感谢你的时间和答案,如果碰巧在某个地方找到解决方案,我非常抱歉(我做了很多搜索,但是还没找到它)。
更新(根据Bear Brown的要求):
这是我的API类代码,不确定它是否真正相关,因为它只是用于与外部数据库进行通信,但无论如何。
产品/ api.py
import requests
import json
class API:
URL = "[...]"
KEY = "[...]"
LOGIN = "authenticate/access-token"
REGISTER = "authenticate/register"
session = None
debug = True
def __init__(self, **kwargs):
if len(kwargs) > 2 or not kwargs:
return
try:
self.login(**kwargs)
except requests.exceptions.ConnectionError:
print("Could not connect to the database.")
except requests.exceptions.RequestException:
print("Could not retrieve data from the database.")
def login(self, **kwargs):
if self.session is not None:
return
assert 0 < len(kwargs) < 3
access_token = ''
if len(kwargs) == 2:
username, password = kwargs['username'], kwargs['password']
url = self.URL + self.LOGIN
user_data = {
'username': username,
'password': password,
'active-check': 0,
'api_key': self.KEY
}
r = requests.post(url, data=user_data)
r.raise_for_status()
response = r.json()
if response['success'] is True:
access_token = response['access_token']
else: # len(kwargs) == 1
access_token = kwargs['access_token']
if access_token:
self.session = requests.Session()
self.session.headers.update({'Authorization': 'Client-ID ' + access_token})
if self.debug: print("Successfully logged in.")
return access_token
def logout(self):
if self.session is None:
return
self.session.close()
self.session = None
def logged_in(self):
return self.session is not None
这是一个指向pastebin上的完整错误回溯的链接(添加到已经很长的帖子似乎太长了): full error traceback on pastebin