Django: фильтр объекта Queryset по временному диапазону другого объекта

У меня есть 3 модели: run, параметр_датчика и данные. Между ними есть и другие отношения с внешним ключом, но run не имеет прямого внешнего ключа ни к sensor_parameter, ни к данным.

Запуск имеет время начала и время окончания и связан с камерой.

class Run(models.Model):
    start_time = models.DateTimeField(db_index=True)
    end_time = models.DateTimeField(db_index=True)
    chamber = models.ForeignKey(Chamber, on_delete=models.CASCADE)

Камера связана с датчиком, а датчик имеет набор параметров_датчика.

class SensorParameter(models.Model):
    sensor = models.ForeignKey(Sensor)
    parameter = models.ForeignKey(Parameter)

Наконец, точка данных "принадлежит" параметру датчика:

class Data(models.Model):
    time = models.DateTimeField(db_index=True)
    sensor_parameter = models.ForeignKey(SensorParameter, on_delete=models.CASCADE)
    parameter_value = models.FloatField()

Мне нужно отфильтровать список sensor_parameter(s), принадлежащих запуску, но моя единственная связь между ними – это значение времени. Поскольку данные имеют отметку времени, а выполнение имеет время начала и время окончания, я подумал, что могу отфильтровать список data.sensor_parameter в диапазоне периода времени.

Я не уверен, как построить этот фильтр набора запросов.

Я импортировал дату и время и имею доступ к django_filter.

Это то, что у меня есть до сих пор в моих views.py

import datetime    
import django_filters

def get(self, request):

    # Get a list of run objects, that are passed through the request
    run_ids = request.GET.getlist('id')
    runs = Run.objects.filter(pk__in=run_ids)

    # Get a list of all chambers that own those runs
    chamber = Chamber.objects.filter(run__in=runs).distinct()

    # Get a list of all sensors that belong to those chambers
    sensor = Sensor.objects.filter(chamber=chamber)

    # Looking around, I saw these two DateTimeFilter expressions from django_filters
    time__gte = django_filters.DateTimeFilter(name="time", lookup_expr='gte')
    time__lte = django_filters.DateTimeFilter(name="time", lookup_expr='lte')

    # Here I would have to determine which run.start_time is lower
    # And which run.end_time is higher, to get a valid time range
    # This part is not finished yet
    time_start = run.start_time
    time_end = run.end_time

    # This is the filter I'm having trouble implementing
    sensor_parameters = SensorParameter.objects.filter(sensor=sensor, data__time__gte=time_start, data__time__lte=time_end)

    return render(request, self.template_name, {'run': run, 'sensor_parameters': sensor_parameters})

По сути, я подумал, что если я извлеку start_time и end_time из своих прогонов (это может быть более одного прогона, мне нужно будет определить, какое start_time strong> меньше, а end_time больше), то я мог бы отфильтровать свой sensor_parameter по data.time, используя временной диапазон.

Я понятия не имею, как действовать дальше.

Кроме того, мы используем PostgreSQL, если это имеет значение.

Если вы видите какие-либо вопиющие ошибки или синтаксические ошибки, пожалуйста, не стесняйтесь исправлять и критиковать, я все еще новичок, и в Django, и в Python, но люблю каждую минуту этого.


person Mormoran    schedule 30.03.2018    source источник
comment
Пожалуйста, добавьте решение в качестве ответа в соответствии с форматом вопросов и ответов Stack Overflow — не редактируйте его в своем вопросе.   -  person EJoshuaS - Reinstate Monica    schedule 01.04.2018


Ответы (1)


Мне удалось сделать это самостоятельно, хотя я не уверен, что то, что я делаю, является лучшим способом. Я оставлю приведенный выше код нетронутым, чтобы продемонстрировать, в чем заключалась проблема, и предложить точку сравнения с найденным решением.

Идея диапазона дат была правильной, так как data.time была моей единственной ссылкой на run.start_time - run.end_time.

Итак, я определяю range_period, добавляя все run.start_time и run.end_time в пару списков, затем я вызываю min () и max() в этих списках для извлечения необходимых периодов, которые затем попадают в список, что и является (включенным) фильтром __range. требует в качестве входных данных.

Ниже представлен views.py с комментариями для ясности:

class ChartRunsView(generic.DetailView):
    model = Run
    template_name = 'chart_runs.html'

    def get(self, request):

        # Get a list of run objects, that are passed through the request
        run_ids = request.GET.getlist('id')
        runs = Run.objects.filter(pk__in=run_ids)

        # Get a list of all chambers that own those runs
        chamber = Chamber.objects.filter(run__in=runs).distinct()

        # Get a list of all sensors that belong to those chambers
        sensor = Sensor.objects.filter(chamber=chamber)

        # Initialize run_start and run_end lists
        run_start_list = []
        run_end_list = []

        # Append start_time(s) and end_time(s) for all runs
        for run in runs:
            run_start_list.append(run.start_time)
            run_end_list.append(run.end_time)

        # Determine the earliest and latest time stamps for all the extracted run times
        # Using Python's min() and max() list methods   
        runs_start = min(run_start_list)
        runs_end = max(run_end_list)

        # Build a new list with the earliest run_start and latest run_end
        range_period = [runs_start, runs_end]

        # Build query that will filter all SensorParameter objects against sensor, and data
        # Using the __range filter method, which takes a list as input.
        # This will return all SensorParameter objects in that time range, which could potentially
        # be thousands. Applying the .distinct() filter method on the resulting queryset gives us
        # only unique results
        sensor_parameters = SensorParameter.objects.filter(sensor__in=sensor, data__time__range=range_period).distinct()

        return render(request, self.template_name, {'runs': runs,
                                                    'sensor_parameters': sensor_parameters})
person Mormoran    schedule 30.03.2018