Django测试客户端不会自动序列化工厂

时间:2019-04-24 05:58:37

标签: python django django-models

这是我的代码:

# 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导入UserFactory 从blog.tests.factories.post导入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对象的视图处理程序。这种区别是解决我的问题的关键,因为我看到的错误与JsonResponse类应该解决的JSON序列化有关。

另外,其他问题不涉及工厂。工厂是我正在尝试做的另一个关键组成部分,它是针对工厂生成的数据运行集成测试。

3 个答案:

答案 0 :(得分:7)

  

如果我访问/ mypath,此代码将正常工作。

我复制并粘贴了您的代码,它显示了与该帖子Django object is not JSON serializable

完全相同的 TypeError

我认为您的测试客户端没有问题。如下更改您的views.py,然后重试。

from django.http.response import JsonResponse
from django.core import serializers
import json


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

此代码段将产生如下的JSON响应,

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

为什么json.loads() ??

序列化的输出将放置在 string 中,我们必须进行其他更改,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

详细了解Serialization of Django Objects/QuerySets



UPDATE-1

  

为什么开箱即用?使用起来似乎很麻烦。

我在这里看不到任何 开箱即用的方法 。因为一切都看起来不错,所以用Python方式和Django方式(我的回答)。

here,我们可以了解什么是纯JSON。

  

一个对象是一组无序的名称/值对。对象以{(左括号)开始,以}(右括号)结束。每个名称后跟:(冒号),名称/值对之间用,(逗号)分隔。

在您的OP中,您尝试返回的是 {"models": list(MyModel.objects.all())} ,它是dict,而不是JSON
是的...外层是字典,因此可以是 JSON数组 。但是数组内容是 QuerySets ,根据this content >


UPDATE-2

我找到了一种解决方法,可以将对象传递给序列化程序类。创建如下功能,

from django.core import serializers


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

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

并像往常一样使用此 serialize_dup() 功能 serializers.serialize()


Update-3

按照@fush的建议,如果使用 format='python'

序列化模型对象,则可以返回JSON响应。
# code sample
from django.http.response import JsonResponse
from django.core import serializers


def get_all_models(request):
    data = serializers.serialize('python', MyModel.objects.all(), fields=('user', 'name'))
    return JsonResponse({"models": data})

答案 1 :(得分:1)

您共享的代码假设JSONResponse将序列化一个ORM对象,但是根据Django文档,它不会:

https://docs.djangoproject.com/en/3.0/ref/request-response/#jsonresponse-objects

https://docs.djangoproject.com/en/3.0/topics/serialization/#djangojsonencoder

如果您在将Django ORM对象传递给JSONResponse

之前对其进行了序列化,则它将起作用

考虑执行以下操作:

from django.core import serializers
data = serializers.serialize("json", MyModel.objects.all())

https://docs.djangoproject.com/en/3.0/topics/serialization/

django-rest-framework是一个非常流行的库,用于您共享的场景中 https://docs.djangoproject.com/en/3.0/topics/serialization/#djangojsonencoder

答案 2 :(得分:0)

这是什么:

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

关键在这里:

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