Ошибка в 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.

Ну и импорт ещё поправил.

→ Ссылка