Niedawno zacząłem używać Django REST Framework (i Django, i Python - jestem osoba z systemem RTOS/embedded systems!) W celu implementacji RESTful Web API. Nie miałem jeszcze żadnych problemów, które nie mogłyby zostać rozwiązane przez Google, ale ten sprawił, że przez kilka godzin nie mogłem się z tym pogodzić.Django REST Framework - POSTing pole klucza obcego zawierające klucz naturalny?
Mam wbudowany system, który nasłuchuje zdarzeń, które są powiązane z szeregiem urządzeń - analogicznie do telefonu wywołującego połączenia, o czym będę tu dyskutować ze zwięzłością. Telefon ma przypisaną liczbę i wiele połączeń (wykonanych). Połączenie ma powiązany telefon (telefon, który wykonał połączenie) i czas utworzenia. Gdy wystąpi połączenie, należy je wysłać do interfejsu API. Mam wbudowany system, który nasłuchuje połączeń i ich początkowy numer telefonu i przesyła je do interfejsu API. Ponieważ system wbudowany zna numer telefonu, chciałbym go przesłać: {"srcPhone":12345678}
zamiast {"srcPhone":"http://host/phones/5"}
. Dzięki temu mój system wbudowany nie musi znać klucza podstawowego każdego telefonu (lub telefonów GET według numeru za każdym razem, gdy chce przesłać połączenie).
Dokumenty Google i Django sugerują, że mogę to osiągnąć za pomocą naturalnych kluczy. Moja próba następująco:
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')
T o przetestuj, tworzę telefon z numerem 123456. Następnie POST {"srcPhone": 123456} do http://host/calls/
(który jest skonfigurowany w urls.py, aby uruchomić widok Lista połączeń). To daje atrybut AttributeError at/calls/- 'int' obiekt nie ma atrybutu "startswith". Wyjątek występuje w rest_framework/relations.py (linia 355). Możesz opublikować cały ślad, jeśli będzie to pomocne. Po przeczytaniu relations.py wygląda na to, że REST Framework nie szuka telefonów po numerze, ale przetwarza atrybut srcPhone tak, jakby był adresem URL. Normalnie byłoby to prawdą, ale chcę, aby wyszukiwał telefony za pomocą klucza naturalnego, zamiast podawać adres URL. Czego tu brakowało?
Dzięki!
Dziękuję za to Toma - teraz mogę przesyłać połączenia z całkowitą liczbą srcPhone. – EwanC
Właśnie znalazłem tę odpowiedź i rozwiązałem również mój problem! Bardzo ładnie wyjaśnił – zeroliu