Slicing the content of a TextField() in Django

Viewed 71

I'm working on a Django blog and there is a html page where blog entries are shown alongside their titles and date. But the blog entries on this page are supposed to be summaries (first 700 characters of the actual blog body). So, there's a model called 'Post' with 'title, 'date' and 'body' objects but the problem is getting the first 700 characters of the 'body' object to show in the view for all the blog entries. After iterating over Post.objects.all() only the last blog entry content (700 characters) is displayed in the view. There are 4 blog entries in my DB. Meanwhile, I get the exact result I want in the terminal when I run print(desc). Here is the code.

models.py

class Post(models.Model):
     title = models.CharField(max_length=140) 
     body = models.TextField()  
     date = models.DateTimeField()
 
     def __str__(self):
         return self.title 

views.py

def body_summary(request):
        gen = Post.objects.all().order_by("-date")[:25]
         for summary in gen:
               desc = summary.body[:700]
               print(desc)
         return render(request, 'blog/blog.html', {'gen': gen, 'desc': desc})

blog.html

{% for post in gen %}
<h5 > {{ post.date|date:"Y-m-d" }}  <a href="/blog/{{ post.id }}">{{ post.title }}</a> </h5>
 <h6 >{{ desc }} <a href="/blog/{{ post.id }}">[...]</a></h6>

{% endfor %}
2 Answers

The problem is you update your desc variable during every iteration. The desc variable only consists of the last blog entry's content as it is the last iterated element.

You should have a list and append the first 700 characters to it during every iteration and use it in the html or change the summary.body directly to summary.body[:700] (i.e) summary.body = summary.body[:700].

Change your views.py and blog.html to

views.py

def body_summary(request):
        gen = Post.objects.all().order_by("-date")[:25]
         for summary in gen:
               summary.body = summary.body[:700]
         return render(request, 'blog/blog.html', {'gen': gen})

blog.html

{% for post in gen %}
<h5 > {{ post.date|date:"Y-m-d" }}  <a href="/blog/{{ post.id }}">{{ post.title }}</a> </h5>
 <h6 >{{ post.body }} <a href="/blog/{{ post.id }}">[...]</a></h6>

{% endfor %}

No need to iterate in the view to do this you can simply use the truncatechars template filter [Django docs] to do this (or if the body is HTML you can use truncatechars_html):

def body_summary(request):
    gen = Post.objects.all().order_by("-date")[:25]
    return render(request, 'blog/blog.html', {'gen': gen})

Now in the template:

{% for post in gen %}
    {{ post.body|truncatechars:700 }}
{% endfor %}
Related