简体   繁体   English

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

[英]Django test client does not automatically serialize factories

Here's my code:这是我的代码:

# 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'),

This code works just fine if I visit /mypath .如果我访问/mypath ,这段代码就可以正常工作。 However, when I run an automated test using Django's test client, I get this error:但是,当我使用 Django 的测试客户端运行自动化测试时,出现以下错误:

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

this is my test: from django.test import TestCase, Client from blog.tests.factories.user import UserFactory from blog.tests.factories.post import PostFactory这是我的测试: from django.test import TestCase, Client from blog.tests.factories.user import UserFactory from 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

I suspect it has something to do with my factories:我怀疑这与我的工厂有关:

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')

How can I make my factories serializable?如何使我的工厂可序列化?

NOTE: This question is not a duplicate.注意:这个问题不是重复的。 The other questions linked have view handlers that return HttpResponse objects, instead of JsonResponse objects.链接的其他问题具有返回HttpResponse对象而不是JsonResponse对象的视图处理程序。 This distinction is key to my problem, because the error I'm seeing is related to JSON serialization that is supposed to be addressed by the JsonResponse class.这种区别是我的问题的关键,因为我看到的错误与应该由JsonResponse类解决的 JSON 序列化有关。

Also, the other questions do not involve factories.另外,其他问题不涉及工厂。 Factories are another key component of what I'm trying to do, which is run integration tests against data generated by factories.工厂是我正在尝试做的事情的另一个关键组成部分,即针对工厂生成的数据运行集成测试。

This code works just fine if I visit /mypath. 如果我访问/ mypath,这段代码就可以了。

I copy and paste your code and it raised a TypeError as exactly same as this post, Django object is not JSON serializable 我复制并粘贴你的代码,它引发了一个与这篇文章完全相同的TypeErrorDjango对象不是JSON可序列化的


I don't think this is something wrong with your test client. 我不认为你的测试客户端有问题。 Change your views.py as below and try again. 将您的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)})

This snippet will produce a JSON response as below, 此代码段将生成JSON响应,如下所示,

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

Why json.loads() ?? 为什么json.loads() ??

The serialized out put will be in string , we've to change it else, the Jsonresponse will be a string instead of Json 序列化的输出将是字符串 ,我们要改变它,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

Read more about Serialization of Django Objects/QuerySets 阅读有关Django对象/ QuerySet的序列化的更多信息



UPDATE-1 UPDATE-1

Why doesn't this work out of the box? 为什么这不是开箱即用的? It seems cumbersome to use. 使用起来似乎很麻烦。

I don't see any non-out of the box method here. 我在这里看不到任何非开箱即用的方法。 Because all things seem good, in Pythonic way and in Django way (my answer). 因为所有事情看起来都很好,以Pythonic方式和Django方式(我的回答)。

From here, we could understand what is a pure JSON. 这里,我们可以理解什么是纯JSON。

An object is an unordered set of name/value pairs. 对象是一组无序的名称/值对。 An object begins with { (left brace) and ends with } (right brace). 对象以{ (左括号)开头,以}结尾(右大括号)。 Each name is followed by : (colon) and the name/value pairs are separated by , (comma). 每个名称后面跟着: (结肠)和名称/值对被分离的, (逗号)。

In your OP, you are trying to return {"models": list(MyModel.objects.all())} which is a dict but not a JSON . 在你的OP中,你试图返回{"models": list(MyModel.objects.all())}这是一个dict但不是JSON
Yeah...the outer layers are dict so it's can be a JSON Array . 是的...外层是dict所以它可以是一个JSON数组 But the Array contents are QuerySets , which is not a value according to this content 但是Array内容是QuerySets ,它不是根据此内容


UPDATE-2 UPDATE-2

I found a workaround to pass objects to the serializer class. 我找到了一个将对象传递给序列化程序类的解决方法。 Create a function as below, 创建如下函数,

from django.core import serializers


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

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

and use this serialize_dup() funtion as usual serializers.serialize() 和通常的serializers.serialize()一样使用这个serialize_dup()函数


Update-3 更新3

As @fush suggested in comments, it's possible to return the JSON response if you serialize the model objects with format='python' 正如@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})

The code you shared assumes JSONResponse will serialize an ORM object, but according to Django documentation, it won't:您共享的代码假定JSONResponse将序列化一个 ORM 对象,但根据 Django 文档,它不会:

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

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

It will work if you serialize the Django ORM object before passing it to JSONResponse如果在将 Django ORM 对象传递给JSONResponse之前对其进行序列化,它将起作用

Consider doing the following:考虑执行以下操作:

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

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

django-rest-framework is a very popular lib used in scenarios like the one you shared https://docs.djangoproject.com/en/3.0/topics/serialization/#djangojsonencoder django-rest-framework 是一个非常流行的库,在您分享的场景中使用https://docs.djangoproject.com/en/3.0/topics/serialization/#djangojsonencoder

what about this:那这个呢:

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

the point is here:重点在这里:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM