Django model query tune up

时间:2016-07-11 19:58:20

标签: python django

I want to reduce the number of template filter calls in my_filter_function(). Because It's used inside two for-loops inside a template. Please see below for my code setup.

class ModelA(models.model):
    models.ForeignKey(OtherModel1)

class ModelB(models.model):
    models.ForeignKey(OtherModel2)

class ModelC(models.Model):
    a = models.ForeignKey(ModelA)
    b = models.ForeignKey(ModelB)

def my_views(request):
       return render(request, 'my_template.html', {
        'a_list': ModelA.objects.all(),
        'b_list': ModelB.objects.all(),
    })

and in my template, I have

{% for a in a_list %}
   {% for b in b_list %}
             {% with b|my_filter_function:a as my_val %}
                       Val: {{my_val}}
             {% endwith %}
   {% endfor %}
{% endfor %}

the above template will will call the my_filter_function filter function, I need to find another way to reduce the number of my_filter_function function calls, because the filter function is accessing the DBs several thousand times per template now.

@register.filter
def my_filter_function:(b, a):
        z = ModelC.objects.filter(a=a, b=b)
        if z.count() > 0:
            return "OK"
        else:
            return "Not OK"

1 个答案:

答案 0 :(得分:2)

Here's a faster alternative.

Fetch all the ids of A and B in C at once:

z = ModelC.objects.values_list('a_id', 'b_id')

a_related, b_related = zip(*z) # split into a and b ids

Pass these to your context:

def my_views(request):
       return render(request, 'my_template.html', {
        'a_list': ModelA.objects.all(),
        'b_list': ModelB.objects.all(),
        'a_related': a_related,
        'b_related': b_related,
    })

And then use if...in in your template. The custom template filter can now be discarded:

{% for a in a_list %}
   {% for b in b_list %}
             {% if a.id in a_related and b.id in b_related %}
                 "OK"
             {% else %}
                 "Not ok"
             {% endif %}
   {% endfor %}
{% endfor %}

That replaces all the multiple queries in your filter with just one.