Search code examples
pythondjangooptimizationquery-optimization

Speed Optimization in Django


I am building a Blog App and I am also thinking to implement video posts in it. I am trying to optimize the speed of the Web App in Development and also in Production.

I have deployed many sites build in Django in last several months on PythonAnywhere. I've noticed that most of site contains simple functionalities like :- Create, Edit, Image Upload, ListView, DetailView of Blogs , etc. With 500 to 600 lines of Code (python). BUT they are very slow even with 2 to 5 pages.

Talking about Development :-

When i open a page that contains 100 to 150 Blogs with Images, then it is taking 2 to 3 seconds to open the page AND then i think this may be Because of more blogs and queries then I checked the page's queries and query's Time using Django-Debug-Toolbar then I deleted all the posts except 1 post after that I saw in debug-toolbar, it is showing

  • Total time to load the Page is 600ms.

  • 4 queries in 6 to 8ms

I removed some queries but still same results.

I also searched about it then i found a Post of optimizing queries using select_related then I also used it BUT I didn't see any much improvement.

I used to make more and more for loops as a beginner in Template but then I read somewhere (sorry but I don't remember the source) "Use logical functionalities like for loop, if else, etc in views instead of templates".

Talking about Production :-

In my site in production - Logging In , Creating or Uploading File , Loading image in site seems pretty slow in site. So I think that may be because of the Database I am using , So I contacted them to ask if database slows down the site speed (performance) then they said :-

The speed of your site is very much dependent on the speed of the code that you have written to handle the requests that come in. The specific database probably will not have an effect on the speed of image downloading, but the fact that you're not using the static files system will.

BUT I just wrote code like anyone would do for show the lists of Blog Posts and I am using MEDIA ROOT and STATIC files after that images are still loading slowly.

AND I also worked according to Django-Documentation-About-Performance and Optimization like :- using counting objects using .count() will increase the speed.

After that I still didn't find any prefect solution for optimizing the site in Production and Development.

The Blog Web App's views and models that i am working on and trying to optimize the site

models.py

class BlogPost(models.Model):
    user = models.ForeignKey(User,default='',null=True,on_delete = models.CASCADE)
    title = models.CharField(max_length=500,default='',validators=[validate_is_profane])
    description = models.TextField()
    date = models.DateTimeField(null=True,default=timezone.now)
    image = models.ImageField(upload_to='blog_posts',blank=True,null=True)
    slug = models.SlugField(null=True)

views.py

def posts(request):
    blog_profile = get_object_or_404(Profile, user_id=request.user)
    now = timezone.now()
    posts = Post.objects.filter(date__lte=now).order_by('-date').exclude(user=request.user)

    if request.method == 'POST':
        new_blog_post = BlogPostForm(request.POST,
                                    request.FILES,
                                    instance=request.user.profile)

        if which_post_form.is_valid():
            new_blog_post = new_blog_post.save(commit=False)
            new_blog_post.save()
            return redirect('home')

    else:
        new_blog_post = BlogPostForm(instance=request.user.profile)

    context = {
        'posts':posts,
        'blog_profile':blog_profile,
        'new_blog_post':new_blog_form,
    }

    return render(request, 'post_list.html', context)

post_list.html

{% for topic in posts %}

<br>


<a href="{% url 'user_profile' user.id %}">{{ topic.user }}</a>

<p>{{ topic.title|truncatechars:10 }}</p>

{% if topic.image %}
<br>
<a><img src="{{ topic.image.url }}" id="myImg" width="300"></a>
<br>
{% endif %}


<div id="myModal" class="modal">
    <span class="close">&times;</span>
    <img class="modal-content" id="img01">
    <div id="caption"></div>
</div>



<a>{{ topic.description|slice:":10" }}</a>

<a href="{{ topic.get_absolute_url }}">More</a>

{% endblock content %}

If you have any further solution about , How you optimized your site . then I will really appreciate you Answers and suggestions.


Solution

  • I see two things that could cause bad performance if you have a lot of data, where the first is more likely to be a problem:

    Accesses to topic.user in the template will make an extra query for every item in posts by default, since that attribute access needs to load information for the User model from a different table. Doing a select_related('user') when getting your Post queryset will load user information in the same query as posts:

    Post.objects.filter(date__lte=now) \
        .order_by('-date') \
        .exclude(user=request.user) \
        .select_related('user')
    

    I strongly suspect avoiding a query per post this way will solve your performance issues, since if a typical query takes around 10ms and you need to make hundreds of them, that could easily blow your response time out to multiple seconds. Since you're not actually reading a lot of data, the time a query takes will be dominated by fixed overheads that you can avoid by simply doing fewer queries.


    While it's less likely to be a problem unless you have a large number of posts (tens of thousands or more), ordering your Posts by date will require the database to read every row of the table and sort them before returning any rows. With a few hundred objects this has negligible cost, but with many thousands it would be slower.

    If you add an index over the fields you query and sort by you can help the database do things efficiently:

    class Post(models.Model):
        ...
        class Meta:
            indexes = [
                models.Index(fields=['-date'], name='date_ordering'),
            ]