2013-01-16 89 views
12

爲了實現REST風格的Web API,我最近開始使用Django REST框架(以及Django和Python--我是一個RTOS /嵌入式系統人員!)。目前還沒有任何問題無法用Google解決,但是這個問題讓我現在陷入了幾個小時的困境。Django REST框架 - 發佈包含自然鍵的外鍵字段?

我有一個嵌入式系統,用於監聽與一系列設備相關的事件 - 類似於撥打電話的電話,這是我在這裏爲簡潔起見而討論的。一個電話有一個號碼和一大堆呼叫(它已經)與之相關聯。呼叫具有關聯的電話(發起呼叫的電話)和創建時間。發生呼叫時,應將其發送到API。我有一個嵌入式系統,用於監聽呼叫及其始發電話號碼,並將它們提交給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',) 

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

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

ŧ o測試,我創建了一個號碼爲123456的電話。然後我POST {「srcPhone」:123456}到http://host/calls/(在urls.py中配置它來運行CallList視圖)。這在/ calls/- 'int'對象的AttributeError中沒有屬性'startswith'。 rest_framework/relations.py中出現異常(第355行)。可以發佈整個跟蹤,如果它會有所幫助。在閱讀關係.py時,它看起來像REST框架沒有按數字查找電話,而是像處理URL一樣處理srcPhone屬性。這通常是真實的,但我希望它通過自然鍵來查找電話,而不是提供URL。我在這裏錯過了什麼?

謝謝!

回答

13

您要找的是SlugRelatedField。見docs here

但是像處理URL一樣處理srcPhone屬性。

沒錯。您正在使用HyperlinkedModelSerializer,因此srcPhone鍵默認使用超鏈接關係。

你看到的'int' object has no attribute 'startswith'異常是因爲它期待一個URL字符串,但接收一個整數。真的這應該導致一個描述性驗證錯誤,所以我created a ticket for that

如果改爲使用串行是這樣的:

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

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

然後'srcPhone'鍵將使用上的關係的目標'number'領域,而不是代表的關係。

我打算在不久的將來在關係文檔中增加更多的工作,所以希望這在未來會更加明顯。

+0

謝謝你湯姆 - 現在我只能提交一個整數srcPhone號碼的呼叫。 – EwanC

+0

剛剛找到了這個答案,並解決了我的問題!非常好解釋 – zeroliu

1

上面解決了這個問題(不能張貼此作爲一個評論,太長)

湯姆的回答。

但是,我也想要超鏈接字段回到電話資源。 SlugRelatedField允許我使用屬於電話的整數字段進行提交,但是當獲得生成的呼叫資源時,它也會作爲整數串行化。我確信這是一個預期的功能(將它從一個整數串行化到超鏈接似乎並不是很優雅)。我找到的解決方案是將另一個字段添加到CallSerializer:src = serializers.HyperlinkedRelatedField(view_name='phone-detail',source='srcPhone',blank=True,read_only=True)並將該字段添加到Meta類。然後,我只POST srcPhone(一個整數)和GET srcPhone plus src,這是一個超級鏈接到電話資源。

+0

我不確定你需要那個'blank = True',但是否則,看起來不錯。 –

相關問題