質問編集履歴

2 コードの修正

shun173

shun173 score 4

2020/07/12 12:36  投稿

Django REST frameworkで実装したAPIが400エラーを返す
### 前提・実現したいこと
DjangoのモデルにアクセスするAPIを作成しました。このAPIにpostメソッドでテストを実行すると400エラーが返ってきてしまいます。getメソッドによるテストは上手くいきました。また、ブラウザ上でpostを実行したときは上手くいきました。
### 発生している問題・エラーメッセージ
```
FAIL: test_post (ecapp.tests.test_api.ProductApiTest)
----------------------------------------------------------------------
Traceback (most recent call last):
 File "C:\Users\lion-\Desktop\portfolio\ecapp\tests\test_api.py", line 82, in test_post
   self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 400 != 201
```
### 該当のソースコード
test_api.py
```python
from django.core.files import File
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, Client
from django.test.client import encode_multipart
from django.urls import reverse
from django.utils.timezone import localtime
from faker import Faker
from rest_framework import status
from rest_framework.test import APIRequestFactory, force_authenticate, APIClient, APITestCase
from ..models import Product
from ..views import ProductViewSet
from .factories import ProductFactory, UserFactory
class ProductApiTest(APITestCase):
   def test_post(self):
       user = UserFactory()
       view = ProductViewSet.as_view({'post': 'create'})
       filename = 'test_img'
       file = File(open('static/default_icon.jpg', 'rb'))
       uploaded_file = SimpleUploadedFile(
           filename, file.read(), content_type='multipart/form-data')
       client = APIClient()
       client.force_authenticate(user=user)
       data = {
           'name': 'test',
           'description': 'test',
           'price': 1,
           'amount': 1,
           'image': uploaded_file,
           'owner': 1
           'owner': user.id
       }
       url = reverse('product-list')
       response = client.post(url, data, format='multipart')
       self.assertEqual(response.status_code, status.HTTP_201_CREATED)
       self.assertEqual(Product.objects.count(), 1)
       product = Product.objects.filter(name=data['name'])[0]
       self.assertEqual(product.name, data['name'])
       self.assertEqual(product.description, data['description'])
       self.assertEqual(product.price, data['price'])
       self.assertEqual(product.amount, data['amount'])
       self.assertEqual(product.image, data['image'])
       self.assertEqual(product.owner, data['owner'])
```
factories.py
```python
import factory.fuzzy
import random
import datetime
from django.contrib.auth import get_user_model
class UserFactory(factory.django.DjangoModelFactory):
   class Meta:
       model = get_user_model()
       django_get_or_create = ('username',)
   email = factory.Sequence(lambda n: f'person{n}@example.com')
   username = factory.Faker('name')
   address = factory.Faker('address')
   icon = factory.Faker('image_url')
   message = factory.Faker('word')
   last_login_date = datetime.date.today()
```
models.py
```python
from django.db import models
class Product(models.Model):
   """商品"""
   name = models.CharField(max_length=100)
   description = models.TextField(blank=True)
   price = models.PositiveIntegerField(default=0)
   amount = models.PositiveIntegerField(default=1)
   image = models.ImageField(upload_to='product')
   owner = models.ForeignKey('users.User', on_delete=models.CASCADE)
   created_at = models.DateTimeField(auto_now_add=True)
   def __str__(self):
       return self.name
```
views.py
```python
from rest_framework import viewsets
from rest_framework import permissions, authentication
from .models import Product, Sale
from .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet):
   """API endpoint"""
   queryset = Product.objects.all().order_by('-created_at')
   serializer_class = ProductSerializer
   permission_classes = [permissions.IsAuthenticated]
```
serializer.py
```python
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
   class Meta:
       model = Product
       fields = ('id', 'name', 'description', 'price',
                 'amount', 'image', 'owner', 'created_at')
```
### 試したこと
client.post()で送るdataのimageフィールドを、初めは
data={'image': 'static/default_icon.jpg'}
のように直接書いていたのですが、同様に400エラーが出たので原因はこの部分かと思い上記のように修正しました。
このAPIが201を返すようにして、テストを通過させるにはどのようにすればいいですか?
よろしくお願いします。
######参考にしたサイト
[How to generate a file upload (test) request with Django REST Framework's APIRequestFactory?](http://stackoverflow.com/questions/40453947/how-to-generate-a-file-upload-test-request-with-django-rest-frameworks-apireq)
[公式ドキュメント](http://www.django-rest-framework.org/api-guide/testing/#apiclient)
### 補足情報(FW/ツールのバージョンなど)
python==3.6.8
django==2.2.12
djangorestframework==3.11.0
pillow==7.0.0
  • Python

    25751 questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Django

    3440 questions

    DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

1 説明を追加

shun173

shun173 score 4

2020/07/12 08:24  投稿

Django REST frameworkで実装したAPIが400エラーを返す
### 前提・実現したいこと
DjangoのモデルにアクセスするAPIを作成しました。このAPIにpostメソッドでテストを実行すると400エラーが返ってきてしまいます。getメソッドによるテストは上手くいきました。また、ブラウザ上でpostを実行したときは上手くいきました。
### 発生している問題・エラーメッセージ
```
FAIL: test_post (ecapp.tests.test_api.ProductApiTest)
----------------------------------------------------------------------
Traceback (most recent call last):
 File "C:\Users\lion-\Desktop\portfolio\ecapp\tests\test_api.py", line 82, in test_post
   self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 400 != 201
```
### 該当のソースコード
test_api.py
```python
from django.core.files import File
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, Client
from django.test.client import encode_multipart
from django.urls import reverse
from django.utils.timezone import localtime
from faker import Faker
from rest_framework import status
from rest_framework.test import APIRequestFactory, force_authenticate, APIClient, APITestCase
from ..models import Product
from ..views import ProductViewSet
from .factories import ProductFactory, UserFactory
class ProductApiTest(APITestCase):
   def test_post(self):
       user = UserFactory()
       view = ProductViewSet.as_view({'post': 'create'})
       filename = 'test_img'
       file = File(open('static/default_icon.jpg', 'rb'))
       uploaded_file = SimpleUploadedFile(
           filename, file.read(), content_type='multipart/form-data')
       client = APIClient()
       client.force_authenticate(user=user)
       data = {
           'name': 'test',
           'description': 'test',
           'price': 1,
           'amount': 1,
           'image': uploaded_file,
           'owner': 1
       }
       url = reverse('product-list')
       response = client.post(url, data, format='multipart')
       self.assertEqual(response.status_code, status.HTTP_201_CREATED)
       self.assertEqual(Product.objects.count(), 1)
       product = Product.objects.filter(name=data['name'])[0]
       self.assertEqual(product.name, data['name'])
       self.assertEqual(product.description, data['description'])
       self.assertEqual(product.price, data['price'])
       self.assertEqual(product.amount, data['amount'])
       self.assertEqual(product.image, data['image'])
       self.assertEqual(product.owner, data['owner'])
```
factories.py
```python
import factory.fuzzy
import random
import datetime
from django.contrib.auth import get_user_model
class UserFactory(factory.django.DjangoModelFactory):
   class Meta:
       model = get_user_model()
       django_get_or_create = ('username',)
   email = factory.Sequence(lambda n: f'person{n}@example.com')
   username = factory.Faker('name')
   address = factory.Faker('address')
   icon = factory.Faker('image_url')
   message = factory.Faker('word')
   last_login_date = datetime.date.today()
```
models.py
```python
from django.db import models
class Product(models.Model):
   """商品"""
   name = models.CharField(max_length=100)
   description = models.TextField(blank=True)
   price = models.PositiveIntegerField(default=0)
   amount = models.PositiveIntegerField(default=1)
   image = models.ImageField(upload_to='product')
   owner = models.ForeignKey('users.User', on_delete=models.CASCADE)
   created_at = models.DateTimeField(auto_now_add=True)
   def __str__(self):
       return self.name
```
views.py
```python
from rest_framework import viewsets
from rest_framework import permissions, authentication
from .models import Product, Sale
from .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet):
   """API endpoint"""
   queryset = Product.objects.all().order_by('-created_at')
   serializer_class = ProductSerializer
   permission_classes = [permissions.IsAuthenticated]
```
serializer.py
```python
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
   class Meta:
       model = Product
       fields = ('id', 'name', 'description', 'price',
                 'amount', 'image', 'owner', 'created_at')
```
### 試したこと
client.post()で送るdataのimageフィールドを、初めは
data={'image': 'static/default_icon.jpg'}
のように直接書いていたのですが、同様に400エラーが出たので原因はこの部分かと思い上記のように修正しました。
このAPIが201を返すようにして、テストを通過させるにはどのようにすればいいですか?  
よろしくお願いします。
######参考にしたサイト
[How to generate a file upload (test) request with Django REST Framework's APIRequestFactory?](http://stackoverflow.com/questions/40453947/how-to-generate-a-file-upload-test-request-with-django-rest-frameworks-apireq)
[公式ドキュメント](http://www.django-rest-framework.org/api-guide/testing/#apiclient)
### 補足情報(FW/ツールのバージョンなど)
python==3.6.8
django==2.2.12
djangorestframework==3.11.0
pillow==7.0.0
  • Python

    25751 questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Django

    3440 questions

    DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る