#python #django
Вопрос:
У меня есть пара представлений в моем приложении Django, которые выполняют и действия, и записывают, кто совершил действие в БД, что-то вроде:
def my_view(request):
# do some stuff here first
current_user = MyCustomUserObject.objects.filter(django_user_id=request.user.id).first()
model_i_did_something_to_above.last_modified_by = current_user
model.save()
И это на самом деле работает нормально, если я запускаю сервер и вызываю его через почтальона. Однако, когда я провожу модульный тест:
from datetime import datetime
class MyTests(TestCase):
def setUp(self):
now = datetime.utcnow()
django_user = User.objects.create_user(username='myusername',
email='test@test.com',
password='abcd')
self.user = MyCustomUserObject(user_name='myusername', email_address='test@test.com', created_datetime=now,
last_modified_datetime=now, is_admin=True, django_user=django_user)
self.user.save()
self.tokens = json.loads(self.client.post(reverse('authenticate'),
data={'username': 'myusername',
'password': 'abcd'},
content_type='application/json').content)
def test_stuff(self):
self.client.delete(reverse('nameoftheurl'),
{data: 'stuff'},
content_type='application/json',
**{'HTTP_AUTHORIZATION': self.tokens['access']})
И это доходит до взгляда, он говорит, что request.user.id это Не Так. Почему это происходит? Есть ли способ обойти это?
Редактировать
Представление проверки подлинности выглядит следующим образом:
from rest_framework_simplejwt.views import TokenObtainPairView
urlpatterns = [
path('authenticate/', TokenObtainPairView.as_view(), name='authenticate'),
]
Я также обновил приведенный выше тестовый пример, чтобы показать, как создается пользователь.
Комментарии:
1. Можете ли вы поделиться тем, как выглядит представление аутентификации? И как вы создали пользователя перед аутентификацией в тестах?
2. @bdbd пожалуйста, посмотрите мою правку, чтобы ответить на эти вопросы!
3. @rodrigocf Вы можете попробовать добавить
self.user.refresh_from_db()
сразу послеself.user.save()
в функции настройки в модульном тесте.4. Пожалуйста, добавьте код (может быть промежуточным программным обеспечением), который отвечает за настройку
request.user
изHTTP_AUTHORIZATION
заголовка.
Ответ №1:
Проблема, по-видимому, заключается в том, что в заголовке авторизации отсутствует тип авторизации в test_stuff
тестовом случае.
Это должно быть:
headers={'Authorization': f"Bearer {self.tokens['access']}"}
Так:
def test_stuff(self):
self.client.delete(reverse('nameoftheurl'),
{data: 'stuff'},
content_type='application/json',
**{'HTTP_AUTHORIZATION': f"Bearer {self.tokens['access']}")
# ^^^ Add this
Комментарии:
1. Вот оно. Я не могу поверить, что все было так просто. Поскольку срок действия награды истек, я начал ее снова и начислю очки завтра вечером. Спасатель жизни, по-крупному. @bdbd
Ответ №2:
Вы забыли войти в систему своего пользователя:
class MyTests(TestCase):
...
def test_stuff(self):
self.client.force_login(self.user)
self.client.delete(
reverse('nameoftheurl'),
{data: 'stuff'},
content_type='application/json',
HTTP_AUTHORIZATION=self.tokens['access'],
)
Отрывок из authentication docs
:
Если текущий пользователь не вошел в систему, этот атрибут будет установлен как экземпляр пользователя-анонима, в противном случае это будет экземпляр Пользователя.
AnonymousUser
У него id
установлено значение None
, следовательно, ваша ошибка.
Обратите внимание, что это также указывает на ошибку в вашем представлении удаления. Вы должны проверить, что пользователь вошел в систему, прежде чем пытаться выйти из нее, в противном случае в какой-то момент (если еще нет) вы вернете ошибку 500.