简体   繁体   中英

Django: Queryset object filter, against another object's time range

I have 3 models, run , sensor_parameter and data . There are other ForeignKey relationships in between those, but run has no direct ForeignKey to either sensor_parameter or data .

A run has a start_time and an end_time , and is related to a chamber .

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)

A chamber has a relation to a sensor and a sensor has a set of sensor_parameter (s)

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

And a data point finally "belongs" to a sensor_parameter :

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

I need to filter a list of sensor_parameter (s), that belong to a run , but my only link between them is a time value. Since data has a time stamp, and a run has start_time and end_time , I thought I could filter a list of data.sensor_parameter in a time period range.

I'm not sure how to build that query set filter.

I have imported datetime, and have access to django_filter.

This is what I have so far in my 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})

Basically, I thought if I extracted a start_time and an end_time from my runs (it can be more than one run, I would have to determine which start_time is lower and which end_time is greater), then I could filter my sensor_parameter against data.time using a time range.

I've no idea how to proceed from here.

Also, we use PostgreSQL if that makes any difference.

If you see any glaring errors or syntax blunders, please feel free to correct and criticize, I'm still a novice and both Django and Python, but loving every minute of it.

I managed to do it on my own, though I'm not sure if the way I'm doing it is the best way. I'll leave the code above untouched, to demonstrate where the problem was and offer a point of comparisson against the solution I achieved.

The date range idea was on the right track, as data.time was my only link to run.start_time - run.end_time .

So I determine the range_period by adding all run.start_time and run.end_time to a couple of lists, then I call the min() and max() methods on those lists to extract the required periods, which then go into a list, which is what the (included) __range filter requires as input.

Below is the views.py with comments for clarity:

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})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM