Я намагаюся представити API моїй моделі Django за допомогою Django REST framework.
У мене є об'єкт Observation
. Спостереження може містити кілька речей, які були спостерігані. Тож я представив це так:
class Observation(models.Model):
photo_file = models.ImageField( upload_to=img_dir, blank=True, null=True )
titestamp = models.DateTimeField(blank=True, null=True)
latitude = models.FloatField()
longitude = models.FloatField()
class ObservedThing(models.Model):
thing = models.ForeignKey(Thing) # the thing being observed
observation = models.ForeignKey(Observation, related_name="observed_thing")
value = models.FloatField()
Як я розумію, це стосунки один до багатьох.
Тепер у мене є перегляд API:
class ObsvList(generics.ListCreateAPIView):
"""
API endpoint that represents a list of observations.
"""
model = Observation
serializer_class = ObsvSerializer
та відповідний серіалізатор:
class ObsvSerializer(serializers.ModelSerializer):
observed_thing = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Observation
Що мені потрібно зробити, щоб мати можливість опублікувати спостереження з кількома виявленими речами? Я не можу зрозуміти. Дуже дякую.
Відповіді:
8 для відповіді № 1(відповідь більш-менш скопійована з іншого подібне, але менш зрозуміле питання)
Для створення декількох пов'язаних об'єктів в одному POST потрібно записані вкладені серіалізатори які ще не доступні.
Повна підтримка робота в процесі, але тим часом одним (хакерським) рішенням є перевизначення create
у поданні в кожному випадку:
class FooListCreateView(ListCreateAPIView):
model = Foo
serializer_class = FooSerializer
def create(self, request, *args, **kwargs):
data=request.DATA
f = Foo.objects.create()
# ... create nested objects from request data ...
# ...
return Response(serializer.data,
status=status.HTTP_201_CREATED,
headers=headers)
Можливо, не ідеально, але це працює для мене, поки не з’явиться відповідний шлях.
Інший варіант - створити відповідний Observation
об'єкти окремо з окремими POST-адресами та використання PrimaryKeyRelatedField або HyperlinkedRelatedField зробити асоціації у фіналі ObservedThing
ПОСТ.
4 для відповіді № 2
Я знаю, що ця тема вже має відповідь, але япочав працювати над вирішенням цієї проблеми, і оскільки ця публікація стала одним із моїх натхненників, я хотів би поділитися своїм остаточним рішенням. Це може бути корисно комусь. У мене є моделі, тому батьківський клас:
#parent model class
class Parent(models.Model):
id = models.AutoField(primary_key=True)
field = models.CharField(max_length=45)
class Meta:
managed = False
db_table = "parent"
тоді клас дитини:
#child model class
class Child(models.Model):
id = models.AutoField(primary_key=True)
field = models.CharField(max_length=45)
parent = models.ForeignKey(Parent, related_name="children")
class Meta:
managed = False
db_table = "child"
Мені довелося визначити серіалізатори, оскільки я цього не зробивхочу створити URL-адресу, доступну для маршрутизатора, щоб безпосередньо керувати об’єктами Children, але я хотів створити їх через ModelViewSet батьківського ModelViewSet, ось що мені потрібно:
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = Child
read_only_fields = ("id",)
class ParentSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
read_only_fields = ("id",)
class ParentSerializerNested(ParentSerializer):
children = ChildSerializer(many=True)
Потім я був готовий створити ModelViewSet, перевизначивши / розширивши міксини create / update і зробити його загальним, щоб повторно використовувати його в інших випадках:
class ParentChildViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
serializer = self.serializer_parent(data=request.DATA,
files=request.FILES)
try:
if serializer.is_valid():
with transaction.commit_on_success():
self.pre_save(serializer.object)
parent = serializer.save(force_insert=True)
self.post_save(parent, created=True)
# need to insert children records
for child in request.DATA[self.child_field]:
child[self.parent_field] = parent.id
child_record = self.serializer_child(data=child)
if child_record.is_valid():
child_record.save(force_insert=True)
else:
raise ValidationError("Child validation failed")
headers = self.get_success_headers(serializer.data)
serializer.data[self.child_field] = self.serializer_child(
self.model_child.objects.filter(
**{self.parent_field: parent.id}).all(),
many=True).data
return Response(serializer.data,
status=status.HTTP_201_CREATED,
headers=headers)
except ValidationError:
pass
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Тож я можу використовувати його повторно для кожного випадку вкладених відносин, який я маю у своєму додатку, таким чином:
class ParentViewSet(ParentChildViewSet):
child_field = "children"
parent_field = "parent"
model = Parent
model_child = Child
serializer_class = ParentSerializerNested
serializer_parent = ParentSerializer
serializer_child = ChildSerializer
І зрештою, маршрутизація:
router = routers.DefaultRouter()
router.register(r"parents", ParentViewSet)
Це працює як шарм!
1 для відповіді № 3
thing = models.ManyToManyField("Thing")
вам потрібно використовувати взаємозв'язок "багато-багато" для створення тимчасової таблиці, яка автоматично зберігатиме ключі та асоціюватиме дані.