Недавно я начал использовать Django REST Framework (и Django и Python - я являюсь представителем RTOS/встроенных систем!), чтобы реализовать RESTful Web API. У вас еще не было никаких проблем, которые не могли быть решены с помощью Google, но сейчас у меня уже было несколько часов.
У меня есть встроенная система, которая прослушивает события, связанные с рядом устройств - аналогичные телефонным звонкам, что я и обсужу здесь для краткости. Телефон имеет номер и множество вызовов (которые он сделал), связанных с ним. Звонок имеет связанный телефон (телефон, который сделал звонок) и время создания. Когда происходит вызов, он должен быть отправлен в 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')
Чтобы проверить, я создаю телефон с номером 123456. Затем я отправлю POST { "srcPhone": 123456} на http://host/calls/
(который настроен в urls.py для запуска представления CallList). Это дает атрибут AttributeError at/calls/- 'int' не имеет атрибута 'startswith'. Исключение происходит в rest_framework/relations.py(строка 355). Можно опубликовать весь трассу, если это будет полезно. После чтения отношений .py, похоже, что REST Framework не ищет телефоны по номеру, а обрабатывает атрибут srcPhone, как если бы это был URL. Это, как правило, верно, но я хочу, чтобы он просматривал телефоны по естественному ключу, а не предоставлял URL-адрес. Что я пропустил здесь?
Спасибо!