Получение Typeerror: не удается соединить str с байтами при написании тестового примера для ввода файлов запроса в django rest framework? - PullRequest
1 голос
/ 11 апреля 2019

Я работал над проектом, в котором я мог бы добавить CSV для загрузки сведений о рубашках в базу данных.

Я написал набор, который принимает входные данные CSV-файлов рубашки из request.data в django rest framework, и янаписание теста для того же.Хотя во время написания тестового примера вместо создания отдельного файла CSV я создал CSV из фрейма данных pandas внутри тестового примера и преобразовал его в TextIOWrapper для передачи в качестве данных запроса.Это приводит к следующей ошибке:

  File "/Users/prasoon/projects/shirt_details/post_shirt_csv/tests/test_post_shirt_dataset.py", line 175, in test_shirt_dataset_post
    response = self.api_client.post(reverse('post-shirt-csv-dataset', kwargs={'session_id': self.session.id}), {'csv_files': [fp1, fp2]})
  File "/Users/prasoon/projects/venv/lib/python3.6/site-packages/rest_framework/test.py", line 300, in post
    path, data=data, format=format, content_type=content_type, **extra)
  File "/Users/prasoon/projects/venv/lib/python3.6/site-packages/rest_framework/test.py", line 212, in post
    data, content_type = self._encode_data(data, format, content_type)
  File "/Users/prasoon/projects/venv/lib/python3.6/site-packages/rest_framework/test.py", line 184, in _encode_data
    ret = renderer.render(data)
  File "/Users/prasoon/projects/venv/lib/python3.6/site-packages/rest_framework/renderers.py", line 920, in render
    return encode_multipart(self.BOUNDARY, data)
  File "/Users/prasoon/projects/venv/lib/python3.6/site-packages/django/test/client.py", line 198, in encode_multipart
    lines.extend(encode_file(boundary, key, item))
  File "/Users/prasoon/projects/venv/lib/python3.6/site-packages/django/test/client.py", line 247, in encode_file
    to_bytes(file.read())
  File "/Users/prasoon/projects/venv/bin/../lib/python3.6/codecs.py", line 320, in decode
    data = self.buffer + input
TypeError: can't concat str to bytes

Эта вещь работает, когда я открываю файл и передаю его вместо создания строки.Ниже будет более ясно:

Файл тестов:

class TestPostNewDataset(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.user = CurrentUserFactory()

        cls.test_data1 = {"no":[1,2,3],"colour":["Red","Blue","Gray"],"Size":["XL","L","M"],"Brand":["Nike","Polo","Adidas"],"Price":[800,600,1000]}
        cls.test_data2 = {"no":[1,2,3],"colour":["Black","Blue","Red"],"Size":["S","M","L"],"Brand":["Nike","Levis","Nike"],"Price":[2000,900,1000]}

        cls.test_df1 = pd.DataFrame(cls.test_data1)
        cls.test_df2 = pd.DataFrame(cls.test_data2)
        cls.session = CurrentSessionFactory(user=cls.user)

    def test_shirt_dataset_post(self):
        "Test request to post shirt dataset"

        fp1 = TextIOWrapper(StringIO(self.test_df1))
        fp2 = TextIOWrapper(StringIO(self.test_df2))

        response = self.api_client.post(reverse("post-shirt-csv-dataset", kwargs={'session_id':self.session.id}),{'csv_files':[fp1,fp2]})

        self.assertEqual(response.status_code,status.HTTP_200_OK)

Соответствующий файл представлений:

class CsvReadViewset(viewsets.ModelViewSet):
    authentication_classes = (BasicAuthentication,)
    permission_classes = (IsAuthenticated,)
    serializer_class = ShirtSerializer
    queryset = Shirt.objects.all()

    def addShirt(self,request,*args,**kwargs):
        csvs = request.data["csv_files"]
        current_session = CurrentSession.objects.get(session_id=kwargs["session_id"])
        for shirt_csv in csvs:
            shirt_df = pd.read_csv(shirt_csv)
            for index,row in short_df.iterrows():
                s = Shirt(colour=row["colour"],
                          brand=row["Brand"],
                          size=row["Size"],
                          price=row["Price"],
                          created_in=current_session)
                s.save()
        return Response(status=status.HTTP_200_OK)

Интересно то, что при тестированииcase принимает отдельный файл в качестве входных данных, он работает нормально.То есть, если я заменю test_shirt_dataset_post следующим:

    def test_shirt_dataset_post(self):
        with open(os.path.join(os.path.abspath(__file__ + "/../"), 'shirtcsv1.csv')) as fp1, open(os.path.join(os.path.abspath(__file__ + "/../"), 'shirtcsv2.csv')) as fp2:
             response = self.api_client.post(reverse("post-shirt-csv-dataset",kwargs={'session_id':self.session.id}),{'csv_files':[fp1,fp2]})

        self.assertEqual(response.status_code,status.HTTP_200_OK)

Ожидаемый результат - это тестовый пример для работы.Но здесь выдает ошибку, как показано.Причина добавления TextIOWrapper заключается в том, что тип fp в with open(....) as fp - это _io.TextIOWrapper.Хотя, тогда он выдает эту ошибку.Любая помощь будет оценена.

Ответы [ 2 ]

0 голосов
/ 18 апреля 2019

Вы можете использовать io.BytesIO для отправки закодированного потока байтов:

def test_shirt_dataset_post(self):
    "Test request to post shirt dataset"

    fp1 = BytesIO(self.test_df1.to_csv().encode('utf-8'))
    fp2 = BytesIO(self.test_df2.to_csv().encode('utf-8'))

    response = self.api_client.post(reverse("post-shirt-csv-dataset", kwargs={'session_id':self.session.id}),{'csv_files':[fp1,fp2]})

    self.assertEqual(response.status_code,status.HTTP_200_OK)
0 голосов
/ 11 апреля 2019

Нашел возможный ответ.Таким образом, один из способов передачи строкового файла как объекта файла - использование NamedTeoraryFile.Таким образом, измененный код для test_shirt_dataset_post будет:


from tempfile import NamedTemporaryFile

def test_shirt_dataset_post(self):
    self.test_df1.set_index("id",inplace=True)
    self.test_df2.set_index("id",inplace=True)

    df1_csv_str = self.test_df1.to_csv().encode('utf-8')
    df2_csv_str = self.test_df2.to_csv().encode('utf-8')

    tp1 = NamedTemporaryFile()
    tp1.write(df1_csv_str)
    tp1.flush()

    tp2 = NamedTemporaryFile()
    tp2.write(df2_csv_str)
    tp2.flush()

    with open(tp1.name,'r') as fp1, open(tp2.name,'r') as fp2:
        response = self.api_client.post(reverse("post-shirt-csv-dataset", kwargs={'session_id':self.session.id}),{'csv_files':[fp1,fp2]})

        self.assertEqual(response.status_code,status.HTTP_200_OK)


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...