补丁请求未打补丁 - 返回 403 - django rest framework
Patch request not patching - 403 returned - django rest framework
我正在尝试使用补丁请求测试 API 端点以确保其正常工作。
我正在使用 APILiveServerTestCase
,但似乎无法获得修补项目所需的权限。我创建了一个用户 (adminuser
),他是超级管理员,可以访问所有内容和所有权限。
我的测试用例是这样的:
class FutureVehicleURLTest(APILiveServerTestCase):
def setUp(self):
# Setup users and some vehicle data we can query against
management.call_command("create_users_and_vehicle_data", verbosity=0)
self.user = UserFactory()
self.admin_user = User.objects.get(username="adminuser")
self.future_vehicle = f.FutureVehicleFactory(
user=self.user,
last_updated_by=self.user,
)
self.vehicle = f.VehicleFactory(
user=self.user,
created_by=self.user,
modified_by=self.user,
)
self.url = reverse("FutureVehicles-list")
self.full_url = self.live_server_url + self.url
time = str(datetime.now())
self.form_data = {
"signature": "TT",
"purchasing": True,
"confirmed_at": time,
}
我已经尝试了多种不同的测试方法 - 所有方法都给出了相同的结果 (403)。
我已经在测试中设置了 python 调试器,我已经尝试在浏览器中实际进入 http://localhost:xxxxx/admin/
并使用任何用户手动登录,但是当我点击时页面只会刷新登录后我再也没有 'logged in' 看到管理员。我不确定那是不是因为它在调试器中不能完全正常工作。
我的测试看起来像这样(使用 Requests 库):
def test_patch_request_updates_object(self):
data_dict = {
"signature": "TT",
"purchasing": "true",
"confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
}
url = self.full_url + str(self.future_vehicle.id) + "/"
client = requests.Session()
client.auth = HTTPBasicAuth(self.admin_user.username, "test")
client.headers.update({"x-test": "true"})
response = client.get(self.live_server_url + "/admin/")
csrftoken = response.cookies["csrftoken"]
# interact with the api
response = client.patch(
url,
data=json.dumps(data_dict),
cookies=response.cookies,
headers={
"X-Requested-With": "XMLHttpRequest",
"X-CSRFTOKEN": csrftoken,
},
)
# RESPONSE GIVES 403 PERMISSION DENIED
fte_future_vehicle = FutureVehicle.objects.filter(
id=self.future_vehicle.id
).first()
# THIS ERRORS WITH '' not equal to 'TT'
self.assertEqual(fte_future_vehicle.signature, "TT")
我使用 APIRequestFactory
进行了与 the documentation 非常相似的尝试
并强制验证:
def test_patch_request_updates_object(self):
data_dict = {
"signature": "TT",
"purchasing": "true",
"confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
}
url = self.full_url + str(self.future_vehicle.id) + "/"
api_req_factory = APIRequestFactory()
view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
api_request = api_req_factory.patch(
url, json.dumps(data_dict), content_type="application/json"
)
force_authenticate(api_request, self.admin_user)
response = view(api_request, pk=self.future_assignment.id)
fte_future_assignment = FutureVehicle.objects.filter(
id=self.future_assignment.id
).first()
self.assertEqual(fte_future_assignment.signature, "TT")
如果我进入调试器查看响应,它总是 403
。
viewset
本身很简单:
class FutureVehicleViewSet(ModelViewSet):
serializer_class = FutureVehicleSerializer
def get_queryset(self):
queryset = FutureVehicle.exclude_denied.all()
user_id = self.request.query_params.get("user_id", None)
if user_id:
queryset = queryset.filter(user_id=user_id)
return queryset
序列化程序非常基础 - 它只是 FutureVehicle
模型和所有字段。
我只是想不通为什么我的用户不登录 - 或者我在尝试修补时是否做错了什么?
总的来说,我对 Django Rest Framework 还很陌生,所以任何指导都会很有帮助!
编辑添加 - 我的 DRF 设置如下所示:
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
"DATETIME_FORMAT": "%m/%d/%Y - %I:%M:%S %p",
"DATE_INPUT_FORMATS": ["%Y-%m-%d"],
"DEFAULT_AUTHENTICATION_CLASSES": [
# Enabling this it will require Django Session (Including CSRF)
"rest_framework.authentication.SessionAuthentication"
],
"DEFAULT_PERMISSION_CLASSES": [
# Globally only allow IsAuthenticated users access to API Endpoints
"rest_framework.permissions.IsAuthenticated"
],
}
我确定 adminuser
是我们希望登录的用户 - 如果我进入调试器并检查用户,他们作为用户存在。在创建期间,创建的任何用户的密码都设置为 'test'.
推荐的解决方案
你写的测试也是在测试Django框架逻辑(即:Django admin登录)。我建议测试您自己的功能,这发生在 登录到 Django 管理员之后。 Django 的测试框架提供了一个登录管理员的帮助程序,client.login
。这让您可以专注于测试您自己的业务logic/not 需要维护内部django 身份验证业务逻辑测试,这可能会改变版本。
from django.test import TestCase, Client
def TestCase():
client.login(username=self.username, password=self.password)
备选方案
但是,如果您必须复制和管理client.login
正在做的事情的业务逻辑,这里有一些来自 Django 的业务逻辑:
def login(self, **credentials):
"""
Set the Factory to appear as if it has successfully logged into a site.
Return True if login is possible or False if the provided credentials
are incorrect.
"""
from django.contrib.auth import authenticate
user = authenticate(**credentials)
if user:
self._login(user)
return True
return False
def force_login(self, user, backend=None):
def get_backend():
from django.contrib.auth import load_backend
for backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
if hasattr(backend, 'get_user'):
return backend_path
if backend is None:
backend = get_backend()
user.backend = backend
self._login(user, backend)
def _login(self, user, backend=None):
from django.contrib.auth import login
# Create a fake request to store login details.
request = HttpRequest()
if self.session:
request.session = self.session
else:
engine = import_module(settings.SESSION_ENGINE)
request.session = engine.SessionStore()
login(request, user, backend)
# Save the session values.
request.session.save()
# Set the cookie to represent the session.
session_cookie = settings.SESSION_COOKIE_NAME
self.cookies[session_cookie] = request.session.session_key
cookie_data = {
'max-age': None,
'path': '/',
'domain': settings.SESSION_COOKIE_DOMAIN,
'secure': settings.SESSION_COOKIE_SECURE or None,
'expires': None,
}
self.cookies[session_cookie].update(cookie_data)
参考文献:
Django client.login: https://github.com/django/django/blob/main/django/test/client.py#L596-L646
我正在尝试使用补丁请求测试 API 端点以确保其正常工作。
我正在使用 APILiveServerTestCase
,但似乎无法获得修补项目所需的权限。我创建了一个用户 (adminuser
),他是超级管理员,可以访问所有内容和所有权限。
我的测试用例是这样的:
class FutureVehicleURLTest(APILiveServerTestCase):
def setUp(self):
# Setup users and some vehicle data we can query against
management.call_command("create_users_and_vehicle_data", verbosity=0)
self.user = UserFactory()
self.admin_user = User.objects.get(username="adminuser")
self.future_vehicle = f.FutureVehicleFactory(
user=self.user,
last_updated_by=self.user,
)
self.vehicle = f.VehicleFactory(
user=self.user,
created_by=self.user,
modified_by=self.user,
)
self.url = reverse("FutureVehicles-list")
self.full_url = self.live_server_url + self.url
time = str(datetime.now())
self.form_data = {
"signature": "TT",
"purchasing": True,
"confirmed_at": time,
}
我已经尝试了多种不同的测试方法 - 所有方法都给出了相同的结果 (403)。
我已经在测试中设置了 python 调试器,我已经尝试在浏览器中实际进入 http://localhost:xxxxx/admin/
并使用任何用户手动登录,但是当我点击时页面只会刷新登录后我再也没有 'logged in' 看到管理员。我不确定那是不是因为它在调试器中不能完全正常工作。
我的测试看起来像这样(使用 Requests 库):
def test_patch_request_updates_object(self):
data_dict = {
"signature": "TT",
"purchasing": "true",
"confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
}
url = self.full_url + str(self.future_vehicle.id) + "/"
client = requests.Session()
client.auth = HTTPBasicAuth(self.admin_user.username, "test")
client.headers.update({"x-test": "true"})
response = client.get(self.live_server_url + "/admin/")
csrftoken = response.cookies["csrftoken"]
# interact with the api
response = client.patch(
url,
data=json.dumps(data_dict),
cookies=response.cookies,
headers={
"X-Requested-With": "XMLHttpRequest",
"X-CSRFTOKEN": csrftoken,
},
)
# RESPONSE GIVES 403 PERMISSION DENIED
fte_future_vehicle = FutureVehicle.objects.filter(
id=self.future_vehicle.id
).first()
# THIS ERRORS WITH '' not equal to 'TT'
self.assertEqual(fte_future_vehicle.signature, "TT")
我使用 APIRequestFactory
进行了与 the documentation 非常相似的尝试
并强制验证:
def test_patch_request_updates_object(self):
data_dict = {
"signature": "TT",
"purchasing": "true",
"confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
}
url = self.full_url + str(self.future_vehicle.id) + "/"
api_req_factory = APIRequestFactory()
view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
api_request = api_req_factory.patch(
url, json.dumps(data_dict), content_type="application/json"
)
force_authenticate(api_request, self.admin_user)
response = view(api_request, pk=self.future_assignment.id)
fte_future_assignment = FutureVehicle.objects.filter(
id=self.future_assignment.id
).first()
self.assertEqual(fte_future_assignment.signature, "TT")
如果我进入调试器查看响应,它总是 403
。
viewset
本身很简单:
class FutureVehicleViewSet(ModelViewSet):
serializer_class = FutureVehicleSerializer
def get_queryset(self):
queryset = FutureVehicle.exclude_denied.all()
user_id = self.request.query_params.get("user_id", None)
if user_id:
queryset = queryset.filter(user_id=user_id)
return queryset
序列化程序非常基础 - 它只是 FutureVehicle
模型和所有字段。
我只是想不通为什么我的用户不登录 - 或者我在尝试修补时是否做错了什么?
总的来说,我对 Django Rest Framework 还很陌生,所以任何指导都会很有帮助!
编辑添加 - 我的 DRF 设置如下所示:
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
"DATETIME_FORMAT": "%m/%d/%Y - %I:%M:%S %p",
"DATE_INPUT_FORMATS": ["%Y-%m-%d"],
"DEFAULT_AUTHENTICATION_CLASSES": [
# Enabling this it will require Django Session (Including CSRF)
"rest_framework.authentication.SessionAuthentication"
],
"DEFAULT_PERMISSION_CLASSES": [
# Globally only allow IsAuthenticated users access to API Endpoints
"rest_framework.permissions.IsAuthenticated"
],
}
我确定 adminuser
是我们希望登录的用户 - 如果我进入调试器并检查用户,他们作为用户存在。在创建期间,创建的任何用户的密码都设置为 'test'.
推荐的解决方案
你写的测试也是在测试Django框架逻辑(即:Django admin登录)。我建议测试您自己的功能,这发生在 登录到 Django 管理员之后。 Django 的测试框架提供了一个登录管理员的帮助程序,client.login
。这让您可以专注于测试您自己的业务logic/not 需要维护内部django 身份验证业务逻辑测试,这可能会改变版本。
from django.test import TestCase, Client
def TestCase():
client.login(username=self.username, password=self.password)
备选方案
但是,如果您必须复制和管理client.login
正在做的事情的业务逻辑,这里有一些来自 Django 的业务逻辑:
def login(self, **credentials):
"""
Set the Factory to appear as if it has successfully logged into a site.
Return True if login is possible or False if the provided credentials
are incorrect.
"""
from django.contrib.auth import authenticate
user = authenticate(**credentials)
if user:
self._login(user)
return True
return False
def force_login(self, user, backend=None):
def get_backend():
from django.contrib.auth import load_backend
for backend_path in settings.AUTHENTICATION_BACKENDS:
backend = load_backend(backend_path)
if hasattr(backend, 'get_user'):
return backend_path
if backend is None:
backend = get_backend()
user.backend = backend
self._login(user, backend)
def _login(self, user, backend=None):
from django.contrib.auth import login
# Create a fake request to store login details.
request = HttpRequest()
if self.session:
request.session = self.session
else:
engine = import_module(settings.SESSION_ENGINE)
request.session = engine.SessionStore()
login(request, user, backend)
# Save the session values.
request.session.save()
# Set the cookie to represent the session.
session_cookie = settings.SESSION_COOKIE_NAME
self.cookies[session_cookie] = request.session.session_key
cookie_data = {
'max-age': None,
'path': '/',
'domain': settings.SESSION_COOKIE_DOMAIN,
'secure': settings.SESSION_COOKIE_SECURE or None,
'expires': None,
}
self.cookies[session_cookie].update(cookie_data)
参考文献:
Django client.login: https://github.com/django/django/blob/main/django/test/client.py#L596-L646