【问题标题】:Splitting model instance for serializer into 3 different fields将序列化程序的模型实例拆分为 3 个不同的字段
【发布时间】:2013-09-27 18:05:03
【问题描述】:

Tom Christie 帮助我在正确的方向上使用 REST 框架,但我现在遇到了另一个问题:

注意:这是使用viewsets.ModelViewSet

在我的原始代码中,我可以在模型实例 xyz(它保存像“20x40x50”这样的坐标数据)上使用 zip() 和 split() 返回坐标 JSON 数据。 我调用了我自己的 toJSON() 函数来输出我需要的所有内容的 JSON 就绪输出。 结果是这样的:

[
  {
   "id" : "4"
   "x" : "500",
   "Y" : "80",
   "z" : "150"
   "color" : "yellow"
  },
  ...
]

使用 REST Framework 序列化程序的问题是我只知道如何执行 serializers.Field(source"xyz") 的事情。我不知道如何将“x”“y”“z”作为单独的字段返回,而不是“xyz”作为一个大字段返回。

这是我的代码:

serializers.py:
---------------
class NoteSerializer(serializers.ModelSerializer):
    owner = serializers.Field(source='owner.username')
    firstname = serializers.Field(source='owner.first_name')
    lastname = serializers.Field(source='owner.last_name')

    x = ???
    y = ???
    z = ???

class Meta:
    model = Note
    fields = ('id','owner','firstname','lastname','text','color', 'x', 'y, 'z', 'time')

这是视图:

    views.py:
    ---------
    def list(self, request, format=None):
        if request.method == 'GET':
            queryset = Note.objects.filter(owner=request.user)
            serializer = NoteSerializer(queryset, many=True)
            if 'text' in request.GET:
                if self.is_numeric(request.GET['id']) and self.is_numeric(request.GET['x']) and self.is_numeric(request.GET['y']) and self.is_numeric(request.GET['z']):

                    serializer = NoteSerializer(data=request.QUERY_PARAMS)
                    intx = int(float(request.GET['x']))
                    inty = int(float(request.GET['y']))
                    intz = int(float(request.GET['z']))
                    serializer.object.xyz = str(intx) +'x'+ str(inty) +'x'+ str(intz)
                    serializer.save()

            return Response(serializer.data, status=status.HTTP_201_CREATED)

    def create(self, request, format=None):

        serializer = NoteSerializer(data=request.DATA)
        if serializer.is_valid():
            serializer.object.owner = request.user
            serializer.save()

            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

这是我的模型:

from django.db import models
import datetime
import json
from django.utils import timezone
from django.core.urlresolvers import reverse
from django.core import serializers
from django.contrib.auth.models import User

class Note(models.Model):
owner = models.ForeignKey('auth.User', null=True)
text = models.CharField(max_length=500)
color = models.CharField(max_length=20)
xyz = models.CharField(max_length=20)
time = models.DateTimeField((u"Note Creation Date and Time"), auto_now_add=True,  blank=True)

def __unicode__(self):
    return unicode(self.owner)

非常感谢您的帮助!我是 Python/Django/REST 的新手。这看起来很有趣,但是这几天让我很沮丧。

更新:

似乎我无法通过带有 serializer.object.xyz 的 views.py 访问 xyz。它说同样的错误“Nonetype has no attribute xyz”

serializer = NoteSerializer(data=request.QUERY_PARAMS)
intx = int(float(request.GET['x']))
inty = int(float(request.GET['y']))
intz = int(float(request.GET['z']))
serializer.object.xyz = str(intx) +'x'+ str(inty) +'x'+ str(intz)
serializer.save()

【问题讨论】:

  • 你打算用同样的方式用api写那些值吗?
  • ^ 好吧,我的意思是,我可以用另一种方式编写它,但我只是想知道 REST 框架是否可以,如果可以,过程会是什么?看来我需要使用自定义字段...但我不确定从哪里开始...

标签: python django json django-rest-framework


【解决方案1】:

您可以尝试使用get_serializer_context 将您的 x,y,z 发送到序列化程序,例如...

from core import models

from rest_framework import generics
from rest_framework import serializers

class BloopModelSerializer(serializers.ModelSerializer):
    x_coord = serializers.SerializerMethodField('get_x')

    def get_x(self, instance):
        return self.context['x']

    class Meta:
        model = models.Bloop

class BloopAPIView(generics.ListCreateAPIView):
    serializer_class = BloopModelSerializer
    queryset = models.Bloop.objects.all()

    def get_serializer_context(self):
        context = super(BloopAPIView, self).get_serializer_context()
        # you have access to self.request here
        context.update({
            'x': 1111,
            'y': 2222
        })

        return context

这样您就不必再重写列表并创建函数了。

附带说明,您还可以尝试将坐标放入其自己的序列化程序中,并将它们分组到序列化程序中。 rest_framework 可以嵌套序列化器,所以你可以添加一个CoordinatesSerializer 类,你的模型序列化器看起来像这样

class CoordinateSerializer(serializers.Serializer):
    x = models.Field()
    y = models.Field()
    # ...

class BloopModelSerializer(serializers.ModelSerializer):
    coordinates = CoordinateSerializer('get_coords')

    def get_coords(self, instance):
        return self.context['coords']

    class Meta:
        model = models.Bloop

【讨论】:

  • 嗨 jc555!感谢所有这些提示。我从来不知道其中一些。我会尝试一下,如果我能正常工作,我会回到这个问题上。顺便说一句,对于嵌套序列化程序,get_coords() 在这里做什么?
  • 我收到一个 KeyError,它从 return self.context['x'] 中只显示“X”。此外,对于嵌套的序列化程序,我似乎无法做你对 models.Field() 所做的事情。它只是说模型没有 Field()。
  • 嵌套序列化器有点棘手,它希望将一个对象传递给它。您需要传递一个对象,通常是模型对象,但如果您使用SerializerMethodField,dict 将起作用。您实际上并不需要嵌套序列化程序,这只是一个建议,因为我不确定您的项目的上下文以及您希望如何显示 JSON。
  • 感谢 jc555!我会尝试一下。我仍在尝试从两个答案中使其正常工作。我会尽快发布最好的解决方案!非常感谢你的帮助!这无疑将我引向了一个更好的方向。
【解决方案2】:

我的做法:

COORD = dict(x=0, y=1, z=2)

class CoordinateField(serializers.Field):
    def field_to_native(self, obj, field_name):
        # retrieve and split coordinates
        coor = obj.content.split('x')

        # get coordinate value depending on coordinate key (x,y,z)
        return int(coor[COORD[field_name]])

    def field_from_native(self, data, files, field_name, into):
        into['xyz'] = u'{x}x{y}x{z}'.format(**data)
        super(CoordinateField, self).field_from_native(data, files, field_name, into)

class BloopModelSerializer(serializers.ModelSerializer):
    x = CoordinateField()
    y = CoordinateField()
    z = CoordinateField()

    class Meta:
        model = Bloop

这就是我得到的结果:

{
    "x": 10,
    "y": 20,
    "z": 30,
    "content": "10x20x30"
},

编辑:

views.py

class BloopList(generics.ListCreateAPIView):
    queryset = Bloop.objects.all()
    serializer_class = BloopModelSerializer

bloop_list = Bloop.as_view()

urls.py

url(r'^api/bloops/$', 'myapp.views.bloop_list', name='bloop-list'),

建议

您不应该使用 list GET 方法来更改/添加对象,DRF 具有内置类,使其非常容易并让您遵循正确的 REST 标准。

例如,您的列表方法使用 GET 参数获取请求数据,这是一个坏主意,每当您更新或添加新对象时,您都应该使用 POST 或 PUT 在请求正文中提供数据。 DRF 假设,这就是提供数据的方式并负责处理所有事情。

【讨论】:

  • 啊!有趣的。在检查了 REST 框架中的自定义字段之后,我实际上正处于写一个非常相似的东西的边缘。我会继续检查一下。感谢您的帮助和指导!我正在制作这个网络应用程序来练习和学习它。我承认,我是菜鸟:P
  • 说,在我的模型中,我已经有一个名为“xyz”的实例,它包含像“10x20x30”这样的坐标数据。我如何使用“xyz”作为这种拆分的“输入”?
  • @KevinTomiyoshiYang 哦,你的意思是字段名称是“xyz”,然后coor = obj.content.split('x') 使用coor = obj.xyz.split('x')
  • 哦!我现在看到了一切之间的相关性。我无法真正理解自定义字段的概念。感谢您的超级快速响应!我马上去试试看。
  • 我现在收到一个错误:使用 COORD = dict 部分时,“dict 最多需要 1 个参数,得到 3 个”。我不太清楚这意味着什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-08-23
  • 2020-11-23
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多