我目前正在构建一个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作为模板变量user传递到相应的视图中(我知道我不应该这样做,它只是临时的)。
以下是一些其他相关的traceback:
/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中的第942行:return int(value)
在收到value='None'
时抛出一个Exception。
现在让我发布我的应用程序中的所有相关代码(希望如此)。
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类不再有属性字段-我在www.example.com中覆盖了它们models.py因为我没有使用数据库。因此,我的ApiUser示例没有pk,因此SESSION_KEY属性设置为'None'
。当Django想要进行身份验证时,会话的SESSION_KEY属性被传递给前面提到的to_python()
函数,这反过来又引发了一个Exception。
我的问题是,我该如何解决这个问题?我尝试将www.example.com设置user.pk为始终为1,并在www.example.com的authenticate函数中手动将session[SESSION_KEY]
设置为1models.py,这导致我没有收到异常,但写入会话(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。
1条答案
按热度按时间mm5n2pyu1#
我在类似的情况下,看到这个错误。对我来说,解决方案是在创建user对象时为自定义
User
类的id
属性提供一个值。这允许默认的
login()
函数在为所需的主键属性提供值时工作。我的后端现在看起来像这样: