Django REST框架 - 发布包含自然键的外键字段?

Ewa*_*anC 12 django django-models django-rest-framework

我最近开始使用Django REST Framework(以及Django和Python - 我是一个RTOS /嵌入式系统人员!)来实现RESTful Web API.还没有任何问题,但谷歌无法解决这个问题,但是现在这个问题让我感到难过了几个小时.

我有一个嵌入式系统,可以监听与一系列设备相关的事件 - 类似于电话拨打电话,这是我为了简洁而在此讨论的内容.电话有一个号码和一大堆与之相关的呼叫(它已经制作).呼叫具有关联的电话(拨打电话的电话)和创建时间.发生调用时,应将其POST到API.我有一个嵌入式系统,可以监听呼叫及其始发电话号码,并将其提交给API.由于嵌入式系统知道电话号码,我希望它提交:{"srcPhone":12345678}而不是{"srcPhone":"http://host/phones/5"}.这避免了我的嵌入式系统需要知道每个电话的主键(或每次想要提交呼叫时按号码获取GET电话).

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',)
Run Code Online (Sandbox Code Playgroud)

views.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,)
Run Code Online (Sandbox Code Playgroud)

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')
Run Code Online (Sandbox Code Playgroud)

为了测试,我创建一个号码为123456的电话.然后我POST {"srcPhone":123456}到http://host/calls/(在urls.py中配置以运行CallList视图).这给出了一个AttributeError at/calls/ - 'int'对象没有属性'startswith'.rest_framework/relations.py中出现异常(第355行).如果它有用,可以发布整个跟踪.在阅读relations.py时,看起来REST框架没有按编号查找电话,而是处理srcPhone属性,就好像它是一个URL.这通常是正确的,但我希望它通过自然键查找电话,而不是提供URL.我错过了什么?

谢谢!

Tom*_*tie 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')
Run Code Online (Sandbox Code Playgroud)

然后,'srcPhone'密钥将代表使用关系'number'目标上的字段的关系.

我计划在不久的某个时候对关系文档进行更多的工作,所以希望将来会更加明显.