【发布时间】:2016-02-23 03:40:51
【问题描述】:
TL;DR
我正在寻找一种方法来在请求后清除缓存,或者在运行测试时完全禁用它。 Django REST Framework 似乎缓存了结果,我需要一种解决方法。 p>
长版和代码
好吧,当我不断测试它时,结果证明它的行为非常奇怪。最后,我让它工作了,但我真的不喜欢我的解决方法,并且以知识的名义,我必须找出为什么会发生这种情况以及如何正确解决这个问题。
所以,我有一个 APITestCase 类声明如下:
class UserTests(APITestCase):
在这个类中,我的user-list 视图有一个测试函数,因为我有一个取决于权限的自定义查询集。澄清事情:
- 超级用户可以获得整个用户列表(返回 4 个实例),
- 员工看不到超级用户(返回 3 个实例),
- 普通用户只能得到 1 个结果,他们自己的用户(返回 1 个实例)
有效的测试功能版本:
def test_user_querysets(self):
url = reverse('user-list')
# Creating a user
user = User(username='user', password=self.password)
user.set_password(self.password)
user.save()
# Creating a second user
user2 = User(username='user2', password=self.password)
user2.set_password(self.password)
user2.save()
# Creating a staff user
staff_user = User(username='staff_user', password=self.password, is_staff=True)
staff_user.set_password(self.password)
staff_user.save()
# Creating a superuser
superuser = User(username='superuser', password=self.password, is_staff=True, is_superuser=True)
superuser.set_password(self.password)
superuser.save()
# SUPERUSER
self.client.logout()
self.client.login(username=superuser.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# All users contained in list
self.assertEqual(response.data['extras']['total_results'], 4)
# STAFF USER
self.client.logout()
self.client.login(username=staff_user.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Superuser cannot be contained in list
self.assertEqual(response.data['extras']['total_results'], 3)
# REGULAR USER
self.client.logout()
self.client.login(username=user2.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Only 1 user can be returned
self.assertEqual(response.data['extras']['total_results'], 1)
# User returned is current user
self.assertEqual(response.data['users'][0]['username'], user2.username)
如您所见,我按以下顺序测试用户权限:超级用户、员工、普通用户。这行得通,所以...
有趣的事:
如果我更改测试的顺序,从普通用户、员工、超级用户开始,测试会失败。第一个请求的响应被缓存,然后我以员工用户身份登录时得到相同的响应,因此结果数再次为1。
不起作用的版本:
和之前一模一样,只是测试顺序倒了
def test_user_querysets(self):
url = reverse('user-list')
# Creating a user
user = User(username='user', password=self.password)
user.set_password(self.password)
user.save()
# Creating a second user
user2 = User(username='user2', password=self.password)
user2.set_password(self.password)
user2.save()
# Creating a staff user
staff_user = User(username='staff_user', password=self.password, is_staff=True)
staff_user.set_password(self.password)
staff_user.save()
# Creating a superuser
superuser = User(username='superuser', password=self.password, is_staff=True, is_superuser=True)
superuser.set_password(self.password)
superuser.save()
# REGULAR USER
self.client.logout()
self.client.login(username=user2.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Only 1 user can be returned
self.assertEqual(response.data['extras']['total_results'], 1)
# User returned is current user
self.assertEqual(response.data['users'][0]['username'], user2.username)
# STAFF USER
self.client.logout()
self.client.login(username=staff_user.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Superuser cannot be contained in list
self.assertEqual(response.data['extras']['total_results'], 3)
# SUPERUSER
self.client.logout()
self.client.login(username=superuser.username, password=self.password)
response = self.client.get(url)
# HTTP_200_OK
self.assertEqual(response.status_code, status.HTTP_200_OK)
# All users contained in list
self.assertEqual(response.data['extras']['total_results'], 4)
我在 python 2.7 中使用以下软件包版本:
Django==1.8.6
djangorestframework==3.3.1
Markdown==2.6.4
MySQL-python==1.2.5
wheel==0.24.0
更新
我使用的是默认的 django 缓存,这意味着我没有在 django 设置中添加任何关于缓存的内容。
按照建议,我尝试禁用默认的 Django 缓存:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
)
}
问题仍然存在。
虽然我不认为问题出在此处,但这是我的 UserViewSet:
api.py(重要部分)
class UserViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet
):
queryset = User.objects.all()
serializer_class = UserExpenseSerializer
permission_classes = (IsAuthenticated, )
allowed_methods = ('GET', 'PATCH', 'OPTIONS', 'HEAD')
def get_serializer_class(self):
if self.action == 'retrieve':
return UserExpenseSerializer
return UserSerializer
def get_queryset(self):
if(self.action == 'list'):
return User.objects.all()
if self.request.user.is_superuser:
return User.objects.all()
if self.request.user.is_staff:
return User.objects.exclude(is_superuser=True)
return User.objects.filter(pk = self.request.user.id)
def list(self, request):
filter_obj = UsersFilter(self.request)
users = filter_obj.do_query()
extras = filter_obj.get_extras()
serializer = UserSerializer(users, context={'request' : request}, many=True)
return Response({'users' : serializer.data, 'extras' : extras}, views.status.HTTP_200_OK)
filters.py
class UsersFilter:
offset = 0
limit = 50
count = 0
total_pages = 0
filter_params = {}
def __init__(self, request):
if not request.user.is_superuser:
self.filter_params['is_superuser'] = False
if (not request.user.is_superuser and not request.user.is_staff):
self.filter_params['pk'] = request.user.id
# Read query params
rpp = request.query_params.get('rpp') or 50
page = request.query_params.get('page') or 1
search_string = request.query_params.get('search')
# Validate
self.rpp = int(rpp) or 50
self.page = int(page) or 1
# Set filter
set_if_not_none(self.filter_params, 'username__contains', search_string)
# Count total results
self.count = User.objects.filter(**self.filter_params).count()
self.total_pages = int(self.count / self.rpp) + 1
# Set limits
self.offset = (self.page - 1) * self.rpp
self.limit = self.page * self.rpp
def get_filter_params(self):
return self.filter_params
def get_offset(self):
return self.offset
def get_limit(self):
return self.limit
def do_query(self):
users = User.objects.filter(**self.filter_params)[self.offset:self.limit]
return users
def get_query_info(self):
query_info = {
'total_results' : self.count,
'results_per_page' : self.rpp,
'current_page' : self.page,
'total_pages' : self.total_pages
}
return query_info
更新 2
正如 Linovia 所指出的,问题不是缓存或任何其他 DRF 问题,而是过滤器。这是固定的过滤器类:
class UsersFilter:
def __init__(self, request):
self.filter_params = {}
self.offset = 0
self.limit = 50
self.count = 0
self.total_pages = 0
self.extras = {}
if not request.user.is_superuser:
# and so long...
【问题讨论】:
-
如果
self.client是django.test.Client并且您作为不同的用户进行交互,为什么不创建一个新的Client()?无论如何,这应该更接近于正在发生的事情的模型。Client实例是与您的站点的独立浏览器交互。也许这就是 Django 感到困惑的原因。 -
我确实试过了,同样的问题出现了。它与 DRF 缓存有关。另一个有趣的事情是,如果我在普通用户之前测试员工用户,则测试通过。让我大吃一惊。
-
您确定要限制您查询的
url的结果吗?你的queryset参数或get_queryset()函数在那个视图上是什么样子的? -
完全确定,在可浏览的 api 中检查了 50 次 :)。我现在将粘贴查询集。
-
事实上我做不到,它还包括一个数据过滤功能,用几个query_params自定义查询集。但是,这部分工作正常,因为我已经对其进行了很多次测试,并且该应用程序实际上可以按我的意愿工作。而且,正如我所说,如果我首先请求一个 staff_user ,则测试有效:)))
标签: python django unit-testing caching django-rest-framework