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