Q:

How to reduce the number of database hits while querying in Django

I have three tables 

User
Device
Log

I want to filter the logs based on devices and logs. I'm using the following querying which iterates over the users and devices in order to get the logs. I feel this will become a performance hit. How to reduce the number of database hits?
for user_obj in User.objects.all():
    device_qs = Device.objects.filter(user=user_obj)
    if device_qs.exists():
        for device_obj in device_qs:
            log_count = Log.objects.filter(user=user_obj, device=device_obj, created_at__range(from_date, to_date)).count()

A:

If you only need the log count per user and device (which is what you get from the code you posted), you can get that in just one query:
from django.db.models import Count

logs = (Log.objects
    .filter(created_at__range = (from_date, to_date))
    .values('user', 'device')
    .annotate(log_count=Count('device'))
)

You can modify the query to include any attributes of the user and device models that you need:
.values('user__last_name', 'device__name')  # etc.

You can also order the dataset by appending order_by() at the end to be able to iterate over it in the desired order:
.order_by('user__last_name', '-log_count')

