DRF - Объединение нескольких наборов запросов в один ModelViewSet с различными моделями, упорядоченными по времени

Я пытаюсь объединить три набора запросов в один, которые относятся к разным классам моделей, и пытаюсь получить результат в формате json, упорядоченный по времени. Каждая модель имеет поле time. Я хочу объединить все поля вместе и сделать один кверисет, который можно отправить в конечную точку api.

models.py

class DoseRate(models.Model):
    device = models.ForeignKey(Device, on_delete=models.CASCADE ,related_name='dose_rates')
    time = models.DateTimeField(_("Time"), default=timezone.now)
    cpm = models.IntegerField(_("Counts per minute"), default=0)
    uSv = models.FloatField(_("Dose rate in uSv"), default=0)
    time_diff = models.IntegerField(_("Time difference"), default=0)
    total_counts = models.IntegerField(_("Total counts"), default=0)
    total_time = models.IntegerField(_("Total time"), default=0)
    avg_count = models.FloatField(_("Average count"), default=0)
    std_dev = models.FloatField(_("Standard deviation"), default=0)
    
class GPS(models.Model):
    device = models.ForeignKey(Device, on_delete=models.CASCADE ,related_name='gps')
    uSv = models.FloatField(_("Dose rate in uSv"), default=0)
    time = models.DateTimeField(_("Time"), default=timezone.now)
    latitude = models.FloatField(_("Latitude"), default=0)
    longitude = models.FloatField(_("Longitude"), default=0)
    address = models.CharField(_("Address"), max_length=200, blank=True, null=True, default=None)
    
class DeviceStatus(models.Model):
    device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='device_status')
    time = models.DateTimeField(_("Time"), default=timezone.now)
    temprature = models.FloatField(_("Temprature"), default=0)
    frequency = models.IntegerField(_("Frequency"), default=0)
    voltage = models.FloatField(_("Voltage"), default=0)
    battery_level = models.IntegerField(_("Battery level"), default=0)

serializers.py

class AJAXSerializer(serializers.ModelSerializer): 
    class Meta:
        model = DoseRate # This only provide the data for the avilaible fields
        fields = ( 'cpm', 'uSv', 'time_diff', 'total_counts', 'total_time', 'avg_count', 'std_dev',)
    
    def __init__(self, *args, **kwargs):
        
        super(AJAXSerializer, self).__init__(*args, **kwargs)
        self.request = self.context['request']
        self.device = Device.objects.filter(user=self.request.user)
        self.gps = GPS.objects.filter(device__in=self.device)
        self.dose_rate = DoseRate.objects.filter(device__in=self.device)
        self.device_status = DeviceStatus.objects.filter(device__in=self.device)


class AJAXSerializer(serializers.Serializer): # Fields that are not avilable in the model gives an error
    device = serializers.CharField(max_length=100)
    cpm = serializers.IntegerField()
    uSv = serializers.FloatField()
    time_diff = serializers.FloatField()
    total_counts = serializers.IntegerField()
    total_time = serializers.FloatField()
    avg_count = serializers.FloatField()
    std_dev = serializers.FloatField()
    latitude = serializers.FloatField()
    longitude = serializers.FloatField()
    temprature = serializers.FloatField()
    frequency = serializers.IntegerField()
    voltage = serializers.FloatField()
    battery_level = serializers.FloatField()
    time = serializers.DateTimeField(read_only=True)
    
    def __init__(self, *args, **kwargs):
        super(AJAXSerializer, self).__init__(*args, **kwargs)
        self.request = self.context['request']
        self.device = Device.objects.filter(user = self.request.user)
    
    def create(self, validated_data):
        return AJAXSerializer(**validated_data)

views.py

class AJAXViewSet(viewsets.ModelViewSet):
    dose_rate = DoseRate.objects.filter()
    gps = GPS.objects.filter()
    device_status = DeviceStatus.objects.filter()
    result_list = sorted(chain(dose_rate, gps, device_status), key= attrgetter('time'))
    queryset =  result_list
    serializer_class = AJAXSerializer
    allowed_methods = ('GET',)
    authentication_classes = (SessionAuthentication,)
    permission_classes = (IsAuthenticated,)

    def get_queryset(self):
        return self.queryset

Я ищу такой вывод, при котором, если в каком-либо наборе запросов нет данных в то время, когда в других наборах запросов есть данные, он будет брать последние данные.

[
    {
        "time": "2019-01-01T00:00:00Z",
        "cpm": 100,
        "uSv": 0.57,
        "time_diff": 1200,
        "total_counts": 100,
        "total_time": 1200,
        "avg_count": 100,
        "std_dev": 0.57,
        "latitude": 0,
        "longitude": 0,
        "temperature": 0,
        "frequency": 0,
        "battery_level": 0,
        "device_id": "1",
    },
    {
        ...
    }
]

Но я получаю следующее сообщение.

[
    {
        "cpm": 40,
        "uSv": 0.228,
        "time_diff": 2200,
        "total_counts": 300,
        "total_time": 0,
        "avg_count": 0.0,
        "std_dev": 0.0
    },
    {
        "uSv": 0.228
    },
    {
        "cpm": 35,
        "uSv": 0.2,
        "time_diff": 1250,
        "total_counts": 600,
        "total_time": 0,
        "avg_count": 0.0,
        "std_dev": 0.0
    },
    {
        "cpm": 32,
        "uSv": 0.182,
        "time_diff": 1200,
        "total_counts": 500,
        "total_time": 0,
        "avg_count": 0.0,
        "std_dev": 0.0
    },
]

В итоге я решил эту проблему, добавив SerializerMethodField в ModelSerializer.

serializers.py

class AJAXSerializer(serializers.ModelSerializer): 

    def __init__(self, *args, **kwargs):
        super(AJAXSerializer, self).__init__(*args, **kwargs)
        self.request = self.context['request']
        self.device = Device.objects.filter(user=self.request.user)
        self.gps = GPS.objects.filter(device__in=self.device)
        self.dose_rate = DoseRate.objects.filter(device__in=self.device)
        self.device_status = DeviceStatus.objects.filter(device__in=self.device)

    class Meta:
        model = DoseRate # This only provide the data for the avilaible fields
        fields = "__all__"
    
    latitude = serializers.SerializerMethodField()
    longitude = serializers.SerializerMethodField()
    temprature = serializers.SerializerMethodField()
    frequency = serializers.SerializerMethodField()
    voltage = serializers.SerializerMethodField()
    battery_level = serializers.SerializerMethodField()
    
    def get_latitude(self, obj):
        return self.gps.filter(device=obj.device,time__lte = obj.time).order_by('-time').values_list('latitude', flat=True).first()
    def get_longitude(self, obj):
        return self.gps.filter(device=obj.device,time__lte = obj.time).order_by('-time').values_list('longitude', flat=True).first()
    def get_temprature(self, obj):
        return self.device_status.filter(device=obj.device,time__lte = obj.time).order_by('-time').values_list('temprature', flat=True).first()
    def get_frequency(self, obj):
        return self.device_status.filter(device=obj.device,time__lte = obj.time).order_by('-time').values_list('frequency', flat=True).first()
    def get_voltage(self, obj):
        return self.device_status.filter(device=obj.device,time__lte = obj.time).order_by('-time').values_list('voltage', flat=True).first()
    def get_battery_level(self, obj):
        return self.device_status.filter(device=obj.device,time__lte = obj.time).order_by('-time').values_list('battery_level', flat=True).first()

Однако я беру только модель DoseRate в моем ModelViewSet, чтобы увеличить производительность, беря только один кверисет времени.

views.py

class AJAXViewSet(viewsets.ModelViewSet):
    queryset = DoseRate.objects.filter()
    serializer_class = AJAXSerializer
    allowed_methods = ('GET',)
    authentication_classes = (SessionAuthentication,)
    permission_classes = (IsAuthenticated,)

    def get_queryset(self):
        return self.queryset
Вернуться на верх