Ошибка в pytest, помогите разобраться
У меня проект сайт с постами и комментариями к ним. Реализую апи. И все работает, но pytest не подходит. Ошибка следующаяя:
Пермишин:
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
return obj.author == request.user
Вьюха:
from django.shortcuts import get_object_or_404
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .permissions import IsOwnerOrReadOnly
from .models import Comment, Post
from .serializers import CommentSerializer, PostSerializer
class PostViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
queryset = Post.objects.all()
serializer_class = PostSerializer
def perform_create(self, serializer):
serializer.save(author=self.request.user)
class CommentViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
queryset = Comment.objects.all()
serializer_class = CommentSerializer
def get_queryset(self):
post = get_object_or_404(Post, id=self.kwargs['id'])
queryset = post.comments.all()
return queryset
def perform_create(self, serializer):
post = get_object_or_404(Post, id=self.kwargs['id'])
serializer.save(author=self.request.user, post=post)
Сериалайзер:
from rest_framework import serializers
from .models import Comment, Post
class PostSerializer(serializers.ModelSerializer):
author = serializers.SlugRelatedField(
read_only=True,
slug_field='username'
)
class Meta:
fields = ('id', 'text', 'author', 'image', 'pub_date')
model = Post
class CommentSerializer(serializers.ModelSerializer):
author = serializers.SlugRelatedField(
read_only=True,
slug_field='username',
)
post = serializers.SlugRelatedField(
read_only=True,
slug_field='pk',
)
class Meta:
fields = ('id', 'author', 'post', 'text', 'created')
model = Comment
И Settings:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'posts',
'rest_framework.authtoken',
'rest_framework',
'corsheaders',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'yatube_api.urls'
TEMPLATES_DIR = os.path.join(BASE_DIR, "templates")
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATES_DIR],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'yatube_api.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGE_CODE = 'ru'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
]
}
CORS_ORIGIN_ALLOW_ALL = True
CORS_URLS_REGEX = r'^/api/.*$'
Тесты:
Test_auth.py
import pytest
from django.conf import settings
class TestAuthAPI:
def test_settings(self):
assert hasattr(settings,
'REST_FRAMEWORK'), 'Проверьте, что добавили настройку `REST_FRAMEWORK` в файл `settings.py`'
assert 'DEFAULT_AUTHENTICATION_CLASSES' in settings.REST_FRAMEWORK, \
'Проверьте, что добавили `DEFAULT_AUTHENTICATION_CLASSES` в `REST_FRAMEWORK` файла `settings.py`'
assert 'rest_framework.authentication.TokenAuthentication' in \
settings.REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'], \
"Проверьте, что в списке значения `DEFAULT_AUTHENTICATION_CLASSES` в `REST_FRAMEWORK` " \
"содержится 'rest_framework.authentication.TokenAuthentication'"
assert 'DEFAULT_PERMISSION_CLASSES' in settings.REST_FRAMEWORK, \
'Проверьте, что добавили `DEFAULT_PERMISSION_CLASSES` в `REST_FRAMEWORK` файла `settings.py`'
assert 'rest_framework.permissions.IsAuthenticated' in settings.REST_FRAMEWORK['DEFAULT_PERMISSION_CLASSES'], \
"Проверьте, что в списке значения `DEFAULT_PERMISSION_CLASSES` в `REST_FRAMEWORK` " \
"содержится 'rest_framework.permissions.IsAuthenticated'"
@pytest.mark.django_db(transaction=True)
def test_auth(self, client, user):
response = client.post('/api/v1/api-token-auth/',
data={'username': user.username, 'password': '1234567'})
assert response.status_code != 404, \
'Страница `/api/v1/api-token-auth/` не найдена, проверьте этот адрес в *urls.py*'
assert response.status_code == 200, \
'Страница `/api/v1/api-token-auth/` работает не правильно'
auth_data = response.json()
assert 'token' in auth_data, 'Проверьте, что при POST запросе `/api/v1/api-token-auth/` возвращаете токен'
Test_post.py
import pytest
from posts.models import Post
class TestPostAPI:
@pytest.mark.django_db(transaction=True)
def test_post_not_found(self, client, post):
response = client.get('/api/v1/posts/')
assert response.status_code != 404, 'Страница `/api/v1/posts/` не найдена, проверьте этот адрес в *urls.py*'
@pytest.mark.django_db(transaction=True)
def test_post_not_auth(self, client, post):
response = client.get('/api/v1/posts/')
assert response.status_code == 401,\
'Проверьте, что `/api/v1/posts/` при запросе без токена возвращаете статус 401'
@pytest.mark.django_db(transaction=True)
def test_posts_get(self, user_client, post, another_post):
response = user_client.get('/api/v1/posts/')
assert response.status_code == 200, \
'Проверьте, что при GET запросе `/api/v1/posts/` с токеном авторизации возвращаетсся статус 200'
test_data = response.json()
assert type(test_data) == list, 'Проверьте, что при GET запросе на `/api/v1/posts/` возвращается список'
assert len(test_data) == Post.objects.count(), \
'Проверьте, что при GET запросе на `/api/v1/posts/` возвращается весь список статей'
post = Post.objects.all()[0]
test_post = test_data[0]
assert 'id' in test_post, 'Проверьте, что добавили `id` в список полей `fields` сериализатора модели Post'
assert 'text' in test_post, 'Проверьте, что добавили `text` в список полей `fields` сериализатора модели Post'
assert 'author' in test_post, \
'Проверьте, что добавили `author` в список полей `fields` сериализатора модели Post'
assert 'pub_date' in test_post, \
'Проверьте, что добавили `pub_date` в список полей `fields` сериализатора модели Post'
assert test_post['author'] == post.author.username, \
'Проверьте, что `author` сериализатора модели Post возвращает имя пользователя'
assert test_post['id'] == post.id, \
'Проверьте, что при GET запросе на `/api/v1/posts/` возвращается весь список статей'
@pytest.mark.django_db(transaction=True)
def test_post_create(self, user_client, user, another_user):
post_count = Post.objects.count()
data = {}
response = user_client.post('/api/v1/posts/', data=data)
assert response.status_code == 400, \
'Проверьте, что при POST запросе на `/api/v1/posts/` с не правильными данными возвращается статус 400'
data = {'author': another_user.id, 'text': 'Статья номер 3'}
response = user_client.post('/api/v1/posts/', data=data)
assert response.status_code == 201, \
'Проверьте, что при POST запросе на `/api/v1/posts/` с правильными данными возвращается статус 201'
test_data = response.json()
msg_error = 'Проверьте, что при POST запросе на `/api/v1/posts/` возвращается словарь с данными новой статьи'
assert type(test_data) == dict, msg_error
assert test_data.get('text') == data['text'], msg_error
assert test_data.get('author') == user.username, \
'Проверьте, что при POST запросе на `/api/v1/posts/` создается статья от авторизованного пользователя'
assert post_count + 1 == Post.objects.count(), \
'Проверьте, что при POST запросе на `/api/v1/posts/` создается статья'
@pytest.mark.django_db(transaction=True)
def test_post_get_current(self, user_client, post, user):
response = user_client.get(f'/api/v1/posts/{post.id}/')
assert response.status_code == 200, \
'Страница `/api/v1/posts/{id}/` не найдена, проверьте этот адрес в *urls.py*'
test_data = response.json()
assert test_data.get('text') == post.text, \
'Проверьте, что при GET запросе `/api/v1/posts/{id}/` возвращаете данные сериализатора, ' \
'не найдено или не правильное значение `text`'
assert test_data.get('author') == user.username, \
'Проверьте, что при GET запросе `/api/v1/posts/{id}/` возвращаете данные сериализатора, ' \
'не найдено или не правильное значение `author`, должно возвращать имя пользователя '
@pytest.mark.django_db(transaction=True)
def test_post_patch_current(self, user_client, post, another_post):
response = user_client.patch(f'/api/v1/posts/{post.id}/',
data={'text': 'Поменяли текст статьи'})
assert response.status_code == 200, \
'Проверьте, что при PATCH запросе `/api/v1/posts/{id}/` возвращаете статус 200'
test_post = Post.objects.filter(id=post.id).first()
assert test_post, 'Проверьте, что при PATCH запросе `/api/v1/posts/{id}/` вы не удалили статью'
assert test_post.text == 'Поменяли текст статьи', \
'Проверьте, что при PATCH запросе `/api/v1/posts/{id}/` вы изменяете статью'
response = user_client.patch(f'/api/v1/posts/{another_post.id}/',
data={'text': 'Поменяли текст статьи'})
assert response.status_code == 403, \
'Проверьте, что при PATCH запросе `/api/v1/posts/{id}/` для не своей статьи возвращаете статус 403'
@pytest.mark.django_db(transaction=True)
def test_post_delete_current(self, user_client, post, another_post):
response = user_client.delete(f'/api/v1/posts/{post.id}/')
assert response.status_code == 204, \
'Проверьте, что при DELETE запросе `/api/v1/posts/{id}/` возвращаете статус 204'
test_post = Post.objects.filter(id=post.id).first()
assert not test_post, 'Проверьте, что при DELETE запросе `/api/v1/posts/{id}/` вы удалили статью'
response = user_client.delete(f'/api/v1/posts/{another_post.id}/')
assert response.status_code == 403, \
'Проверьте, что при DELETE запросе `/api/v1/posts/{id}/` для не своей статьи возвращаете статус 403'
[1]: https://i.stack.imgur.com/X0FWE.jpg
Ответы (1 шт):
Автор решения: Lacoast
→ Ссылка
Разобрался, проблема была вот тут:
class PostViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
Нужно заменить IsAuthenticatedOrReadOnly на IsAuthenticated.
Ну и импорт ещё поправил.
