Ich habe vor kurzem begonnen, das Django REST Framework (und Django und Python - ich bin ein RTOS/embedded Systems Person!) Zu verwenden, um eine RESTful Web API zu implementieren. Ich hatte noch keine Probleme, die mit Google nicht gelöst werden konnten, aber diese hat mich jetzt für ein paar Stunden ratlos gemacht.Django REST Framework - POST-Fremdschlüsselfeld mit natürlichem Schlüssel?
Ich habe ein Embedded-System, das auf Ereignisse abhört, die mit einer Reihe von Geräten verbunden sind - analog zu einem Telefon, das Anrufe tätigt, was ich hier kurz diskutieren werde. Ein Telefon hat eine Nummer und eine ganze Reihe von Anrufen (die es gemacht hat). Ein Anruf hat ein zugeordnetes Telefon (das Telefon, das den Anruf getätigt hat) und eine Erstellungszeit. Wenn ein Anruf auftritt, sollte er an die API gesendet werden. Ich habe ein eingebettetes System, das auf Anrufe und ihre Ursprungs-Telefonnummer wartet und sie an die API übermittelt. Da das Embedded-System die Telefonnummer kennt, möchte ich folgendes einreichen: {"srcPhone":12345678}
statt {"srcPhone":"http://host/phones/5"}
. Dies vermeidet die Notwendigkeit, dass mein eingebettetes System den Primärschlüssel jedes Telefons kennt (oder jedes Mal, wenn es einen Anruf einreichen möchte, Telefone nach Nummer abruft).
Google und die Django-Dokumente schlugen vor, dass ich dies mit natürlichen Schlüsseln erreichen könnte. Mein Versuch folgt:
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 test, ich erstelle ein Telefon mit der Nummer 123456. Dann POST {"srcPhone": 123456} zu http://host/calls/
(die in urls.py konfiguriert ist, um die CallList-Ansicht auszuführen). Dies ergibt einen AttributeError bei/calls/- 'int' Objekt hat kein Attribut 'startswith'. Die Ausnahme tritt in rest_framework/relations.py (Zeile 355) auf. Kann die gesamte Nachricht posten, wenn sie hilfreich ist. Beim Lesen von relations.py sieht es so aus, als ob REST Framework die Telefone nicht nach Nummer sucht, sondern das srcPhone-Attribut so verarbeitet, als wäre es eine URL. Das wäre normalerweise richtig, aber ich möchte, dass die Telefone nach dem natürlichen Schlüssel suchen, anstatt die URL anzugeben. Was habe ich hier verpasst?
Danke!
Danke für das Tom - jetzt kann ich Anrufe nur mit einem Integer srcPhone Nummer senden. – EwanC
Gerade gefunden diese Antwort und löste mein Problem auch! Sehr schön erklärt – zeroliu