DRF выборка на основе поля другой модели
Необходим метод GET, куда я могу передать id объекта Client и получить все объекты Detection, связанные сним. Не могу понять как добитсья этого в рамках DRF. Detection относится к Stream, а Stream относится к Client связью многие к одному.
views.py
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework_api_key.permissions import HasAPIKey
from fire_smoke import models
from fire_smoke import serializers
class ClientViewSet(viewsets.ModelViewSet):
"""
Клиент
"""
queryset = models.Client.objects.all().order_by("-created_at")
serializer_class = serializers.ClientSerializer
permission_classes = [HasAPIKey | IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = "__all__"
class StreamViewSet(viewsets.ModelViewSet):
"""
Стрим
"""
queryset = models.Stream.objects.all().order_by("-created_at")
serializer_class = serializers.StreamSerializer
permission_classes = [HasAPIKey | IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = "__all__"
class DetectionViewSet(viewsets.ModelViewSet):
"""
Детекция
"""
queryset = models.Detection.objects.all().order_by("-created_at")
serializer_class = serializers.DetectionSerializer
permission_classes = [HasAPIKey | IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_fields = ["stream_id", "count_smoke", "count_fire", "created_at"]
serializers.py
from rest_framework import serializers
from fire_smoke import models
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = models.Client
fields = "__all__"
class StreamSerializer(serializers.ModelSerializer):
class Meta:
model = models.Stream
fields = "__all__"
class DetectionSerializer(serializers.ModelSerializer):
class Meta:
model = models.Detection
fields = "__all__"
models.py
from django.core.validators import MaxValueValidator
from django.core.validators import MinValueValidator
from django.db import models
class Client(models.Model):
"""
Клиент(пользователь)
"""
username = models.CharField(max_length=30, unique=True)
created_at = models.DateTimeField(
auto_now=False,
auto_now_add=True,
verbose_name="Дата создания",
blank=True,
)
updated_at = models.DateTimeField(
auto_now=True, auto_now_add=False, verbose_name="Дата изменения"
)
def __str__(self):
return f"{self.username}({self.id})"
class Meta:
verbose_name = "Клиент"
verbose_name_plural = "Клиенты"
class Stream(models.Model):
"""
Поток
"""
class ProtocolType(models.TextChoices):
RTSP = "RTSP", ("RTSP")
RTMP = "RTMP", ("RTMP")
class DeviceTypeForPredict(models.TextChoices):
CUDA = "cuda", ("cuda")
CPU = "cpu", ("cpu")
client = models.ForeignKey(
Client,
on_delete=models.CASCADE,
null=False,
verbose_name="Клиент"
)
stream_url = models.CharField(
verbose_name="Ссылка на поток",
help_text="Пример ссылки: rtsp://admin:[email protected]:6551/live/main",
unique=True,
max_length=256,
)
vid_stride = models.IntegerField(
verbose_name="Частота опорных кадров",
help_text="Если указать 1, то нейросетью будет анализироваться каждый кадр в потоке, если указать 50, то будет анализироваться каждый 50-ый кадр в потоке.",
validators=[MaxValueValidator(3000), MinValueValidator(1)],
default=1,
)
skip_frames = models.IntegerField(
verbose_name="Задержка после детекции",
help_text="Сколько кадров пропустить после того как произойдет регистрация любой детекции. Полезно для того чтобы бы не спамило дубликатами детекций. Если выставить 50, то при детекции пропустится 50*{Частота опорных кадров} кадров.",
default=80,
)
device_uuid = models.CharField(
max_length=86,
unique=False,
verbose_name="ID устройства",
help_text="ID устройства с которого получаем поток",
)
device_name = models.CharField(
max_length=100,
default="",
verbose_name="Имя устройства",
help_text="Имя устройства с которого получаем поток",
blank=True,
)
description = models.CharField(
max_length=200,
default="",
verbose_name="Описание",
help_text="Дополнительное описание",
blank=True,
)
is_run = models.BooleanField(
verbose_name="Распознование запущено",
help_text="Работает или нет распознование по данному потоку в данный момент. Можно изменить эту опцию вручную, чтобы отключить/включить анализ потока. Этот флажок снимется самостоятельно, если произайдет какая то ошибка и анализ прервется.",
default=True,
)
must_work = models.BooleanField(
verbose_name="Должен работать",
help_text='Должен ли работать поток в данный момент. Если True и "Распознование запущено" установлено в False, то система автоматически время от времени будет пытаться поднять соединение с потоком.',
default=True,
)
conf = models.FloatField(
verbose_name="Коэффицент детекций",
help_text="Если установить 0.4, то кадры будут помечаться как детекции только в случае если нейросеть уверенна минимум на 40% что это действительно дым/огонь",
validators=[MaxValueValidator(0.99), MinValueValidator(0.01)],
default=0.4,
)
protocol = models.CharField(
verbose_name="Протокол",
choices=ProtocolType.choices,
help_text="Протокол потока",
)
device_for_predict = models.CharField(
verbose_name="Устройство для распознавания",
choices=DeviceTypeForPredict.choices,
help_text="Через видеокарту распознаем или через процессор?",
default=DeviceTypeForPredict.CPU,
)
save_fire = models.BooleanField(
verbose_name="Распознавать огонь",
help_text="Регистрировать детекцию только в случае если на кадре обнаружен хотя бы 1 истолчник огня.",
default=True,
)
save_smoke = models.BooleanField(
verbose_name="Распознавать дым",
help_text="Регистрировать детекцию только в случае если на кадре обнаружен хотя бы 1 истолчник дыма.",
default=False,
)
created_at = models.DateTimeField(
auto_now=False,
auto_now_add=True,
verbose_name="Дата создания",
blank=True,
)
updated_at = models.DateTimeField(
auto_now=True, auto_now_add=False, verbose_name="Дата изменения"
)
process_id = models.CharField(
null=True,
blank=True,
max_length=36,
verbose_name="ID процесса",
help_text="ID фонового процесса Celery",
)
last_error = models.TextField(
verbose_name="Ошибка",
help_text="Если работа потока аварийно завершится, то тут будет ошибка",
default='',
max_length=255,
)
def __str__(self):
return f"{self.id} | {self.device_name}"
class Meta:
verbose_name = "Поток"
verbose_name_plural = "Потоки"
class Detection(models.Model):
"""
Детекции
"""
stream_id = models.ForeignKey(
Stream,
on_delete=models.CASCADE,
verbose_name="Поток",
)
img = models.ImageField(verbose_name="Снимок", null=True, blank=True)
count_smoke = models.IntegerField(
verbose_name="Детекций дыма",
help_text="Счетчик детекций дыма на снимке",
default=0,
)
count_fire = models.IntegerField(
verbose_name="Детекций огня",
help_text="Счетчик детекций огня на снимке",
default=0,
)
created_at = models.DateTimeField(
auto_now=False,
auto_now_add=True,
verbose_name="Дата создания",
blank=True,
)
def __str__(self):
return f"{self.id} | {self.stream_id}"
class Meta:
verbose_name = "Детекция"
verbose_name_plural = "Детекции"