Тестовый клиент Django автоматически не сериализует фабрики - PullRequest
11 голосов
/ 24 апреля 2019

Вот мой код:

# models.py
class MyModel(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=10)
    ...

# views.py
def get_all_models(request):
    return JsonResponse({"models": list(MyModel.objects.all())})

# urls.py
path('/mypath', views.get_all_models, name='get_all_models'),

Этот код прекрасно работает, если я посещаю /mypath. Однако, когда я запускаю автоматический тест с использованием тестового клиента Django, я получаю эту ошибку:

*** TypeError: Object of type MyModel is not JSON serializable

это мой тест: из django.test импорт TestCase, клиент из blog.tests.factories.user import UserFactory из blog.tests.factories.post import PostFactory

class MyModelTest(TestCase):
    def setUp(self):
        self.user = UserFactory.create()
        self.post = MyModelFactory.create(user=self.user)
        self.client = Client()

    def test_get_all_models(self):
        response = self.client.get("/mypath")
        pass

Я подозреваю, что это как-то связано с моими фабриками:

import factory
from .models import User, MyModel

class UserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User

    username = factory.Faker('word')
    email = factory.Faker('email')

class MyModelFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = MyModel

    user = factory.SubFactory(UserFactory)
    name = factory.Faker('name')

Как я могу сделать мои фабрики сериализуемыми?

Примечание: Этот вопрос не является дубликатом. Другие связанные вопросы имеют обработчики представления, которые возвращают HttpResponse объекты вместо JsonResponse объектов. Это различие является ключевым в моей проблеме, потому что ошибка, которую я вижу, связана с сериализацией JSON, которая должна быть устранена классом JsonResponse.

Кроме того, другие вопросы не касаются заводов. Фабрики являются еще одним ключевым компонентом того, что я пытаюсь сделать, а именно запускать интеграционные тесты с данными, сгенерированными фабриками.

Ответы [ 2 ]

7 голосов
/ 03 мая 2019

Этот код прекрасно работает, если я захожу в / mypath.

Я копирую и вставляю ваш код, и он поднял TypeError точно так же, как этот пост, Объект Django не сериализуем в JSON


Я не думаю, что это что-то не так с вашим тестовым клиентом.Измените views.py, как показано ниже, и попробуйте снова.

<b>from django.http.response import JsonResponse
from django.core import serializers
import json</b>


def get_all_models(request):
    <b>data = serializers.serialize('json', MyModel.objects.all(), fields=('user', 'name'))</b>
    return JsonResponse({"models": <b>json.loads(data)</b>})

Этот фрагмент даст ответ JSON, как показано ниже:

{
    "models": [
        {
            "model": "account.mymodel",
            "pk": 1,
            "fields": {
                "user": 74,
                "name": "jerin"
            }
        },
        {
            "model": "account.mymodel",
            "pk": 2,
            "fields": {
                "user": 66,
                "name": "peter"
            }
        }
    ]
}

Почему json.loads() ??

Сериализованный выходной файл будет в строке , мы должны изменить его иначе, Jsonresponse будет строкой вместо Json

In [1]: from account.models import MyModel                                                                                                                                                                         

In [2]: from django.core import serializers                                                                                                                                                                        

In [3]: data = serializers.serialize('json', MyModel.objects.all(), fields=('user', 'name'))                                                                                                                       

In [4]: data                                                                                                                                                                                                       
Out[4]: '[{"model": "account.mymodel", "pk": 1, "fields": {"user": 74, "name": "jerin"}}, {"model": "account.mymodel", "pk": 2, "fields": {"user": 66, "name": "peter"}}]'

In [5]: type(data)                                                                                                                                                                                                 
Out[5]: str

In [6]: import json                                                                                                                                                                                                

In [7]: data_new = json.loads(data)                                                                                                                                                                                

In [8]: data_new                                                                                                                                                                                                   
Out[8]: 
[{'model': 'account.mymodel',
  'pk': 1,
  'fields': {'user': 74, 'name': 'jerin'}},
 {'model': 'account.mymodel',
  'pk': 2,
  'fields': {'user': 66, 'name': 'peter'}}]

In [9]: type(data_new)                                                                                                                                                                                             
Out[9]: list

Подробнее о Сериализация объектов Django / QuerySets



UPDATE-1

Почему это не работает из коробки?Кажется неудобным в использовании.

Я не вижу здесь никакого нестандартного метода.Потому что все кажется хорошим, как в Pythonic, так и в Django (мой ответ).

От здесь, мы могли бы понять, что такое чистый JSON.

Объект - это неупорядоченный набор пар имя / значение.Объект начинается с { (левая скобка) и заканчивается } (правая скобка).Каждое имя сопровождается : (двоеточие), а пары имя / значение разделяются , (запятая).

В вашем операторе вы пытаетесь вернуть {"models": list(MyModel.objects.all())}, что dict, но не JSON.
Ага ... внешние слои являются точными, поэтому это может быть JSON Array .Но содержимое массива: QuerySets , что не является значением в соответствии с этим содержимым


UPDATE-2

Я нашел обходной путь для передачи объектов в класс сериализатора.Создайте функцию, как показано ниже,

from django.core import serializers


def serialize_dup(format, queryset, **options):
    <b>try:
        iter(queryset)
    except TypeError:
        queryset = [queryset]</b>

    return serializers.serialize(format, queryset, **options)

и используйте эту функцию serialize_dup() как обычно serializers.serialize()


Update-3

Как указано в комментариях @ fush , можно вернуть ответ JSON, если сериализовать объекты модели с format='python'

# code sample
from django.http.response import JsonResponse
from django.core import serializers


def get_all_models(request):
    data = serializers.serialize(<b>'python',</b> MyModel.objects.all(), fields=('user', 'name'))
    return JsonResponse({"models": data})
0 голосов
/ 07 мая 2019

что по этому поводу:

def get_all_models(request):
    return JsonResponse({"models": list(MyModel.objects.all().values())},safe=False)

Дело здесь:

MyModel.objects.all().values()
safe=False
...