Render a Django view with data classified in a one to many relationship

Question:

I have a one to many relationshiop:

class SessionGPS(models.Model):

    start_timestamp = models.IntegerField()
    end_timestamp= models.IntegerField()



class GPSData(models.Model):
    longitude = models.DecimalField(max_digits=15, decimal_places=13)
    lat = models.DecimalField(max_digits=15, decimal_places=13)

    session_new = models.ForeignKey(SessionGPS, on_delete=models.CASCADE, related_name="sesion_gps")

Each SessionGPS entry has multiple GPSData entries. A session is composed of a set of GPS coordinates. This set is in the model GPSData.

I need to query SessionGPS based in start and end timestamps:

def date_search(request):
    data = request.body.decode("utf-8")

    start=int(datetime.datetime.strptime(request.POST['start'], '%Y-%m-%d').timestamp())
    end=int(datetime.datetime.strptime(request.POST['end'], '%Y-%m-%d').timestamp())
    res = GPSData.objects.filter(session_new_id__start_timestamp__gte=start,session_new_id__end_timestamp__lte=end)
    res = serializers.serialize("json", res)
    return HttpResponse(res, content_type='application/json')

In this way I get all GPSData between the timestamps but are not classified by session, they are merged.

I need to get the query like this:

session 1 ->> all GPSData of that session 1
session 2 ->> all GPSData of that session 2

So in the template I can render like this:

For GPSData in session 1 do something
For GPSData in session 2 do something
etc.

I tried to return multiple queries to the view but it didn’t worked.

Thanks for any help.

Asked By: Carlitos_30

||

Answers:

I don’t think there is a query option to what you want. So, the only way I could think of is to post process the data:

models.py:

class SessionGPS(models.Model):
    start_timestamp = models.DateTimeField(auto_now_add=True)
    end_timestamp = models.DateTimeField(null=True)
    
class GPSData(models.Model):
    longitude = models.DecimalField(max_digits=15, decimal_places=13)
    latitude = models.DecimalField(max_digits=15, decimal_places=13)
    session = models.ForeignKey(SessionGPS, on_delete=models.CASCADE, related_name="sesion_gps")

views.py:

def gps_timestamps(request):
    if request.method == 'POST':
        start = request.POST.get('start_date').split('-')
        end = request.POST.get('end_date').split('-')

        start_date = datetime.datetime(int(start[0]), int(start[1]), int(start[2]))
        end_date = datetime.datetime(int(end[0]), int(end[1]), int(end[2]))
        data_rows = GPSData.objects.filter(session__start_timestamp__gte=start_date,session__end_timestamp__lte=end_date)
        
        # Initialize dictionary
        # with unique sessions keys inside the queryset
        data = {}
        for row in data_rows:
            if row.session.id not in data:
                data[row.session.id] = {
                    'start_time': row.session.start_timestamp.strftime("%Y-%m-%d"),
                    'end_time': row.session.end_timestamp.strftime("%Y-%m-%d"),
                }
            else:
                pass
        
        # Populate
        for key, value in data.items():
            gps_data_list = []
            for row in data_rows:
                if row.session.id == key:
                    gps_data_list.append( {'latitude': str(row.latitude), 'longitude': str(row.longitude)} )
            data[key].update(data=gps_data_list)

        # If you really want JSON
        json_object = json.dumps(data, indent = 4) 
        print(json_object)

    context = {
        'data': data
    }
    return render(request, 'gps_timestamp.html', context)

gps_timestamp.html (I used bootstrap 5), two inputs with format yyyy-mm-dd:

<div class="container">
    <form action="{% url 'app:urlname' %}" method="post">
        {% csrf_token %}
        <div class="form-group mb-3">
            <label class="form-label" for="start_date">Start Date</label>
            <input id="start_date" name="start_date" type="text" class="form-control" placeholder="year-month-day" aria-label="start_date">
        </div>
        <div class="form-group mb-3">
            <label class="form-label" for="end_date">End Date</label>
            <input id="end_date" name="end_date" type="text" class="form-control" placeholder="year-month-day" aria-label="end_date">
        </div>
        <input type="submit" value="OK">
    </form>
</div>

<div class="container d-flex justify-content-center" style="flex-direction: column;">
    {% for key, obj in data.items %}
        <p>Session ID: {{key}}</p>
        <p>Start Time: {{obj.start_time}}</p>
        <p>End Time: {{obj.end_time}}</p>
        <p>GPS Data:</p>
        {% for row in obj.data %}
            {{row}}
            <br>
        {% endfor %}        
        <p>----------------------------------</p>
    {% endfor %}
</div>

JSON output of my test:

{
    "2": {
        "start_time": "2022-11-25",
        "end_time": "2022-11-26",
        "data": [
            {
                "latitude": "70.7762500000000",
                "longitude": "12.4800500000000"
            },
            {
                "latitude": "42.7545200000000",
                "longitude": "71.1392900000000"
            },
            {
                "latitude": "56.1794700000000",
                "longitude": "90.8119600000000"
            }
        ]
    },
    "3": {
        "start_time": "2022-11-27",
        "end_time": "2022-11-28",
        "data": [
            {
                "latitude": "10.1099500000000",
                "longitude": "-19.2024500000000"
            },
            {
                "latitude": "80.1405100000000",
                "longitude": "16.2555700000000"
            },
            {
                "latitude": "-16.1924200000000",
                "longitude": "-51.7266300000000"
            },
            {
                "latitude": "19.4745700000000",
                "longitude": "10.7191800000000"
            }
        ]
    }
}

By the way, I have to mention that I had some problems with your GPSData model, when either latitude or logitude had 3 digits. Like, 123.3123123.
It only accepts <100, but I didn’t get into this.

Just changed SessionGPS to better access data.

Answered By: Niko
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.