12

私は最近、RESTful Web API を実装するために、Django REST フレームワーク (および Django と Python - 私は RTOS/組み込みシステム担当者です!) を使い始めました。Google で解決できなかった問題はまだありませんが、この問題で数時間困惑しています。

私は、さまざまなデバイスに関連付けられているイベントをリッスンする組み込みシステムを持っています。これは、簡潔にするためにここで説明する通話を行う電話に似ています。電話には、番号と、それに関連付けられた (それが行った) 多数の通話があります。通話には、関連付けられた電話 (通話を行った電話) と作成時刻があります。呼び出しが発生すると、API に POST する必要があります。通話と発信元の電話番号をリッスンし、API に送信する組み込みシステムがあります。組み込みシステムは電話番号を知っているので、{"srcPhone":12345678}ではなく: を送信してほしいと思い{"srcPhone":"http://host/phones/5"}ます。これにより、組み込みシステムがすべての電話の主キーを知る必要がなくなります (または、電話を送信するたびに番号で電話を取得する必要がなくなります)。

Google と Django のドキュメントでは、自然キーを使用してこれを実現できることが示唆されました。私の試みは次のとおりです。

models.py

from django.db import models
from datetime import datetime
from pytz import timezone
import pytz
from django.contrib.auth.models import User

# Create your models here.
def zuluTimeNow():
    return datetime.now(pytz.utc)


class PhoneManager(models.Manager):
    def get_by_natural_key(self, number):
        return self.get(number=number)


class Phone(models.Model):
   objects     = PhoneManager()
   number      = models.IntegerField(unique=True)

   #def natural_key(self):
   #    return self.number

   class Meta:
      ordering = ('number',)


class Call(models.Model):
    created    = models.DateTimeField(default=zuluTimeNow, blank=True)
    srcPhone   = models.ForeignKey('Phone', related_name='calls')

    class Meta:
        ordering = ('-created',)

ビュー.py

# Create your views here.
from radioApiApp.models import Call, Phone
from radioApiApp.serializers import CallSerializer, PhoneSerializer
from rest_framework import generics, permissions, renderers
from rest_framework.reverse import reverse 
from rest_framework.response import Response
from rest_framework.decorators import api_view

@api_view(('GET',))
def api_root(request, format=None):
    return Response({
        'phones': reverse('phone-list', request=request, format=format),
        'calls': reverse('call-list', request=request, format=format),
    })


class CallList(generics.ListCreateAPIView):
    model = Call
    serializer_class = CallSerializer
    permission_classes = (permissions.AllowAny,)

class CallDetail(generics.RetrieveDestroyAPIView):
    model = Call
    serializer_class = CallSerializer
    permission_classes = (permissions.AllowAny,)

class PhoneList(generics.ListCreateAPIView):
   model = Phone
   serializer_class = PhoneSerializer
   permission_classes = (permissions.AllowAny,)

class PhoneDetail(generics.RetrieveDestroyAPIView):
   model = Phone
   serializer_class = PhoneSerializer
   permission_classes = (permissions.AllowAny,)

serializers.py

from django.forms import widgets
from rest_framework import serializers
from radioApiApp import models
from radioApiApp.models import Call, Phone

class CallSerializer(serializers.HyperlinkedModelSerializer):
   class Meta:
       model = Call
       fields = ('url', 'created', 'srcPhone')

class PhoneSerializer(serializers.HyperlinkedModelSerializer):
   calls = serializers.ManyHyperlinkedRelatedField(view_name='call-detail')
   class Meta:
      model = Phone
      fields = ('url', 'number', 'calls')

http://host/calls/テストするために、番号 123456 の電話を作成します。次に、(CallList ビューを実行するように urls.py で構成されている)に POST {"srcPhone":123456} を送信します。これにより、/calls/ で AttributeError が返されます - 「int」オブジェクトには属性「startswith」がありません。例外は、rest_framework/relations.py (355 行目) で発生します。役立つ場合は、トレース全体を投稿できます。Relations.py を読むと、REST フレームワークは電話を番号で検索するのではなく、srcPhone 属性を URL であるかのように処理しているように見えます。これは通常は当てはまりますが、URL を提供するのではなく、自然キーで Phones を検索するようにしたいと考えています。ここで何を見逃したのですか?

ありがとう!

4

2 に答える 2

13

あなたが探しているのはですSlugRelatedField。こちらのドキュメントをご覧ください。

ただし、srcPhone属性をURLであるかのように処理します。

その通り。を使用しHyperlinkedModelSerializerているため、srcPhoneキーはデフォルトでハイパーリンク関係を使用しています。

表示され'int' object has no attribute 'startswith'ている例外は、URL文字列を予期しているが、整数を受け取っているためです。本当にそれは記述的な検証エラーをもたらすはずなので、私はそのためのチケットを作成しました

代わりに次のようなシリアライザーを使用する場合:

class CallSerializer(serializers.HyperlinkedModelSerializer):
    srcPhone = serializers.SlugRelatedField(slug_field='number')

    class Meta:
        model = Call
        fields = ('url', 'created', 'srcPhone')

次に、'srcPhone'キーは、代わりに、関係'number'のターゲットのフィールドを使用して関係を表します。

近いうちに関係文書にさらに作業を加えることを計画しているので、これが将来もっと明らかになることを願っています。

于 2013-01-17T09:16:12.327 に答える
1

(これをコメントとして投稿することはできません。長すぎます)

上記のトムの答えは問題を解決しました。

ただし、電話リソースに戻るハイパーリンク フィールドも必要です。SlugRelatedField を使用すると、Phone に属する整数フィールドを送信できますが、結果の Call リソースを GET するときに、整数としてもシリアル化されます。これは意図された機能であると確信しています(整数からハイパーリンクにシリアル化するのはあまりエレガントではないようです)。私が見つけた解決策は、別のフィールドを CallSerializer:src = serializers.HyperlinkedRelatedField(view_name='phone-detail',source='srcPhone',blank=True,read_only=True)に追加し、そのフィールドを Meta クラスに追加することでした。次に、srcPhone (整数) のみを POST し、srcPhone と電話リソースへのハイパーリンクである src を GET します。

于 2013-01-17T22:53:52.317 に答える