Count number of replies on a particular post in Django - django

I want to count number of replies on a particular post in Django
View.py
def forum(request):
profile = Profile.objects.all()
if request.method=="POST":
user = request.user
image = request.user.profile.image
content = request.POST.get('content','')
post = Post(user1=user, post_content=content, image=image)
post.save()
messages.success(request, f'Your Question has been posted successfully!!')
return redirect('/forum')
posts = Post.objects.filter().order_by('-timestamp')
return render(request, "forum.html", {'posts':posts})
Reply code
def discussion(request, myid):
post = Post.objects.filter(id=myid).first()
replies = Replie.objects.filter(post=post)
if request.method=="POST":
user = request.user
image = request.user.profile.image
desc = request.POST.get('desc','')
post_id =request.POST.get('post_id','')
reply = Replie(user = user, reply_content = desc, post=post, image=image)
reply.save()
messages.success(request, f'Your Reply has been posted successfully!!')
return redirect('/forum')
return render(request, "discussion.html", {'post':post, 'replies':replies})
model.py
class Post(models.Model):
user1 = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
post_id = models.AutoField
post_content = models.CharField(max_length=5000)
timestamp= models.DateTimeField(default=now)
image = models.ImageField(upload_to="images",default="")
def __str__(self):
return f'{self.user1} Post'
class Replie(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
reply_id = models.AutoField
reply_content = models.CharField(max_length=5000)
post = models.ForeignKey(Post, on_delete=models.CASCADE, default='')
timestamp= models.DateTimeField(default=now)
image = models.ImageField(upload_to="images",default="")
def __str__(self):
return f'{self.user1} Post'
My Forum.html code:
{% for post in posts %}
<div class="container-fluid mt-10">
<div class="row">
<div class="col-md-12">
<div class="card mb-4 forumcardcss">
<div class="card-header forumcardheader">
<div class="media flex-wrap w-100 align-items-center imgcss"> <img src="/media/{{post.image}}"
class="d-block ui-w-40 rounded-circle" alt="profileimage"style="width: 40px;height: 40px;"> <p class="ml-4 usernamecss"> {{post.user1}} </p>
<div class="media-body ml-3"> <button class="btn btn-light" style="color:blue; font-size: 13px;">Add or See reply </button>
</div>
<div class="text-muted small ml-3">
<div class="px-4 pt-3">Nmber of reply {{post.timestamp}} </div>
</div>
{% if user.is_superuser or user.is_staff %}
<button class="btn btn-danger btn-sm" onclick="window.mytest()">Delete Post</button>
<script type="text/javascript">window.mytest = function() { var isValid = confirm('If you click ok then its delete this post and related reply on it. Are you sure to delete?');if (!isValid) { event.preventDefault(); alert("It wont delete. Yay!");}}</script>
{% endif %}
</div>
</div>
<div class="card-body forumcardbody">
<p>{{post.post_content}}</p>
</div>
<div class="card-footer d-flex flex-wrap justify-content-between align-items-center px-0 pt-0 pb-3">
</div>
</div>
</div>
</div>
</div>
{% endfor %}
I want to do like this
where on the place of Number of reply, I want to display the number of replies of the particular post
Is there any way to find if Question(Post) has been answered(reply) on my post page(forum.py)
I want to do it like this If the Question has been answered then it should show "Answered" else "Not answered yet"

#Eega suggested the right answer just some changes in the code will help you
class Post(models.Model):
user1 = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
post_id = models.AutoField
post_content = models.CharField(max_length=5000)
timestamp= models.DateTimeField(default=now)
image = models.ImageField(upload_to="images",default="")
#property
def count_replies(self):
return self.replie_set.count()
def __str__(self):
return f'{self.user1} Post'
post = Post.objects.filter(id=myid).first() to post = Post.objects.filter(id=myid).first().prefetch_related('replies_set') This will make your query optimized
Also accept #Eega answer only, I have just showed you the edited code
Now I am suggesting one good method here
Post.objects.get(id=myid).annotate(post_count=Count("replie"))
Simply use this in your views without changing #models.py and access it in your template as post.post_count in for loop.

To archive this you can use the related name of the Post model (have a look at the documentation). Django will create a field for every foreign key that allows you to access the related model. By default, this will be named replie_set on the Post model.
This field you can then use to get the number of replies to a post by calling the count() method of the replie_set queryset. I would also add a method to the Post model that does that for you as a convenience.
To bring this together, your Post model would look like this:
class Post(models.Model):
user1 = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
post_id = models.AutoField
post_content = models.CharField(max_length=5000)
timestamp= models.DateTimeField(default=now)
image = models.ImageField(upload_to="images",default="")
#property
def count_replies():
return self.replies_set.count()
def __str__(self):
return f'{self.user1} Post'
Assuming that your forum.html template iterates over the posts like that:
{% for post in posts %}
...
<p>Posts: {{ post.count_replies }}</p>
...
{% endfor %}
You get the number of replies by calling post.count_replies(). Of course, if you don't want to add a dedicated method to the model you can just use do post.replie_set.count() directly.
An alternative - and more efficient method - is to annotate your posts with the reply count as Abdul Aziz Barkat suggests. To do this you have to change your view like that:
from django.db.models import Count
def forum(request):
profile = Profile.objects.all()
if request.method=="POST":
user = request.user
image = request.user.profile.image
content = request.POST.get('content','')
post = Post(user1=user, post_content=content, image=image)
post.save()
messages.success(request, f'Your Question has been posted successfully!!')
return redirect('/forum')
posts = Post.objects.annotate(count_replies=Count("replie")).order_by('-timestamp')
return render(request, "forum.html", {'posts':posts})
I changed only the second to last line here:
posts = Post.objects.annotate(count_replies=Count("replie")).order_by('-timestamp')
This adds the aggregated count of replies to each post as count_replies.
Then this value is used in the forum.html template like that:
{% for post in posts %}
<div class="container-fluid mt-10">
<div class="row">
<div class="col-md-12">
<div class="card mb-4 forumcardcss">
<div class="card-header forumcardheader">
<div class="media flex-wrap w-100 align-items-center imgcss"> <img src="/media/{{post.image}}"
class="d-block ui-w-40 rounded-circle" alt="profileimage"style="width: 40px;height: 40px;"> <p class="ml-4 usernamecss"> {{post.user1}} </p>
<div class="media-body ml-3"> <button class="btn btn-light" style="color:blue; font-size: 13px;">Add or See reply </button>
</div>
<div class="text-muted small ml-3">
<div class="px-4 pt-3">Number of replies {{ post.count_replies }} </div>
</div>
{% if user.is_superuser or user.is_staff %}
<button class="btn btn-danger btn-sm" onclick="window.mytest()">Delete Post</button>
<script type="text/javascript">window.mytest = function() { var isValid = confirm('If you click ok then its delete this post and related reply on it. Are you sure to delete?');if (!isValid) { event.preventDefault(); alert("It wont delete. Yay!");}}</script>
{% endif %}
</div>
</div>
<div class="card-body forumcardbody">
<p>{{post.post_content}}</p>
</div>
<div class="card-footer d-flex flex-wrap justify-content-between align-items-center px-0 pt-0 pb-3">
</div>
</div>
</div>
</div>
</div>
{% endfor %}
So, only a single line changed here either:
<div class="px-4 pt-3">Number of replies {{ post.count_replies }} </div>

Related

Way to show latest 4 blog posts on my page

I want to be able to show the latest 4 blog posts only. I can't seem to get them to show. Any help would be greatly appreciated.
Here is my code:
Models.py
class BlogPost(models.Model):
blog_title = models.CharField(max_length=48)
blog_article = RichTextUploadingField(null=True, blank=True, default="ici")
blog_image = models.ImageField(null=True, blank=True, upload_to="images", default="default.png")
blog_date = models.DateField(auto_now_add=True)
blog_published = models.BooleanField(default=False)
blog_featured = models.BooleanField(default=False)
def __str__(self):
return self.blog_title
Views.py
def blogTest(request):
posts = BlogPost.objects.filter(blog_date__lte=timezone.now()).order_by('blog_date')
context_blog = {'posts': posts}
return render(request, 'blogtest.html', context_blog)
def latestPosts(request):
latest = BlogPost.objects.filter(blog_date__lte=timezone.now()).reverse()[:3]
return render(request, 'blogtest.html', {'latest': latest})
Template
<div class="blog-post-container">
<div class="row">
<h1 id="lastest-blogs-title" style="text-align: center;">Latest Blogs</h1>
{% for latestpost in latest %} {% if latestpost.blog_published is True %}
<div class="col-md-4" id="bloggrid1">
<hr>
<div class="blog-post">
<div class="blog-content">
<img class="blog-img"src="{{latestpost.blog_image.url}}"alt="My image"/>
<h2 class="blog-title">{{latestpost.blog_title}}</h2>
<hr id="blog-hr" style="width: 90%" />
<article class="blog-article">
<p>{{latestpost.blog_article|truncatechars_html:265|safe}}</p>
</article>
Read More...
<p class="blog-date">Posted on: {{latestpost.blog_date}}</p>
</div>
</div>
</div>
{% endif %} {% empty %}
<h3>No Blog Uploads</h3>
{% endfor %}
</div>
</div>
</div>
I have followed many other tutorials but I can't seem to see what I'm doing wrong here.
Try this if you want to get last 4 added entries based on primary key...
latest = BlogPost.objects.order_by('-pk')[:4]
Try this if you want to get last 4 added entries based on your date field
latest = BlogPost.objects.order_by('-blog_date')[:4]
Your template code is good to load entries passed through context
You need to sort queryset but not filter the queryset with blog_date
qs = BlogPost.objects.filter(blog_date__lte=timezone.now()).order_by('-blog_date')[:3]

Adding search bar function into a django project

I'm trying to add search bar in my application but I don't know how to query a database to gives the things that user's search for. I want when user search for a user in a post or category in a post of model to shows the result that user search for, like YouTube search and facebook search, How can i do this in django to give me what i want ?
this is my model:
class Photo(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.CharField(max_length=30,null=True, blank=False)
image = CloudinaryField(blank=False, null=False)
description = models.TextField(null=True)
date_added = models.DateTimeField(auto_now_add=True)
phone = models.CharField(max_length=12, null=False, blank=False)
price = models.CharField(max_length=30,blank=False)
location = models.CharField(max_length=20, blank=False)
def __str__(self):
return str(self.category)
my search form in dashboard template:
<div class="container">
<div class="row justify-content-center">
<form action="{% url 'search' %}" method="get">
<input class="form-control me-2" type="search" placeholder="Search" aria-
label="Search">
<br>
<button class="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
the post card in dashboard template:
<div class="container">
<div class="row justify-content-center">
{% for photo in photos reversed %}
<div class="col-md-4">
<div class="card my-2">
<img class="image-thumbail" src="{{photo.image.url}}" alt="Card image cap">
<div class="card-body">
<h2 style="color: yellowgreen; font-family: Arial, Helvetica, sans-serif;">
{{photo.user.username.upper}}
</h2>
<br>
<h3>{{photo.category}}</h3>
<h4>{{photo.price}}</h4>
</div>
<a href="{% url 'Photo-view' photo.id %}" class="btn btn-warning
btn-sm m-1">Buy Now</a>
</div>
</div>
{% empty %}
<h3>No Files...</h3>
{% endfor %}
</div>
</div>
the dashboard view:
def dashboard(request):
photos = Photo.objects.all()
context = {'photos': photos}
return render(request, 'dashboard.html', {'photos': photos} )
the search bar view:
def search(request):
return render(request, 'search.html')
urls:
path('', views.dashboard, name='dashboard'),
path('search/', views.search, name='search')
You can make it using filter method inside your view. Something like:
def dashboard(request):
photos_filter = request.GET.get('filtered[]', False)
photos = Photo.objects.all()
if photos_filter:
photos_filter = eval(photos_filter)
if photos_filter['id'] == 'category':
payments = payments.filter(
category__icontains=payments_filter['value'])
if photos_filter['id'] == 'user':
payments = payments.filter(
user__id=payments_filter['value'])
context = {'photos': photos}
return render(request, 'dashboard.html', {'photos': photos} )
And so on, you can add any filter you like. And in your URL you just add
/?filtered[]=%7B%22id%22:%22category%22,%22value%22:%22Nature%22%7D
Your code will see this filter like a dict obj: {'id': 'category', 'value': 'Nature'}. So after it, you'll get all photos with the category nature

django - submit multiple forms in one post request (different from one click cause I need to process the data of those two forms together!)

I have three models jobposition, wage, kitfee and they are related by a OneToMany relation.
class JobPosition(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.ForeignKey(Title, on_delete=models.CASCADE)
...
class Wage(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
amount = models.FloatField(blank=True,null=True)
type = models.CharField(choices=WAGETYPE, max_length=30)
position = models.ForeignKey(JobPosition, related_name='wages', on_delete=models.CASCADE, blank=True, null=True)
class KitFee(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
kit_name = models.CharField(max_length=20, blank=True, null=True)
amount = models.FloatField(blank=True, null=True)
type = models.CharField(choices=WAGETYPE, max_length=30)
position = models.ForeignKey(JobPosition, related_name='kit_fees', on_delete=models.CASCADE, blank=True, null=True)
I am building a user interface where a user can create a position with associated wage and kit_fee (let's say just one wage and one kit fee for now).
The problem I am facing is that I am not sure how to make a post request in the right way.
I created these two forms and served them to a django template using a templateview.
class JobPositionForm(forms.ModelForm):
class Meta:
model = JobPosition
fields = ['title','city','dept_head','files','department_budget']
...
class WageForm(forms.ModelForm):
class Meta:
model = Wage
exclude = ['id']
my view
class NewPosition(MyLoginRequiredMixin,TemplateView):
template_name = 'action/crew/position.html'
def get_context_data(self, **kwargs):
self.project = get_object_or_404(JobProject, id=kwargs['project_id'])
context = super().get_context_data(**kwargs)
context['position_form'] = JobPositionForm(project=self.project, user=self.request.user)
context['wage_form'] = WageForm()
context['kit_fee_form'] = KitFeeForm()
return context
def post(self,request, **kwargs):
print(request.POST)
self.project = get_object_or_404(JobProject, id=kwargs['project_id'])
position_form = JobPositionForm(request.POST, project=self.project, user=self.request.user)
wage_form = WageForm(request.POST)
kit_fee_form = KitFeeForm(request.POST)
print(kit_fee_form.data['amount'])
print(wage_form.data['amount'])
The page is rendered smoothly, however I am now realizing that I don't know how to submit those two forms together inside a single post request and also how to initialize the forms in the view when processing the post request.
I thought about using formset but I don't know how to use it with different forms.
I guess I can always do this manually using Javascript to parse each value and place it into the same POST... but I was hoping there was a work around it using some nice django functionality. Especially cause the manual approach doesn't scale well.
<form id="position_form">
<div id="title_div" style="margin-top: 20px;">
<label for="id_title" style="margin-bottom: 5px;" class="lead">Title</label><br/>
{{position_form.title}}
<div id="city_div" style="margin-top: 20px;" onfocus="$('#title_creation_div').hide();">
<label for="id_city" style="margin-bottom: 5px;" class="lead">City</label><br/>
{{position_form.city}}
</div>
<br>
<div class="inner" style="display: inline-block;margin-right: 5px;margin-left: 5px; text-align: center">
<label for="OtherComp">Department Budget</label><br/>
{{position_form.department_budget}}
</div>
<div class="inner" style="display: inline-block;margin-right: 5px;margin-left: 5px; text-align: center">
<label for="TeamInfo">Department Head</label><br/>
{{ position_form.dept_head }}
</div>
</form>
<form id="wage_form">
<div id="outer" style="width:100%; margin-top: 20px ">
<div class="inner" style="display: inline-block;margin-right: 5px;margin-left: 5px">
<label for="{{ wage_form.amount.id_for_label }}">Rate</label><br/>
{{wage_form.amount}}
</div>
<div class="inner" style="display: inline-block;margin-right: 5px;margin-left: 5px; text-align: center">
<label for="{{ wage_form.type.id_for_label }}">Rate Type</label><br/>
{{wage_form.type}}
</div>
</div>
</form>
Any idea?
Edit
I am using this javascript to send all forms into the same post request and it seems to be working ok. However, I am noticing that many fields have the same id so I can't initiate the forms properly. Also, the process doesn't seem to be scalable if I start adding additional wages/kitfee to the screen.
$("form").submit(function (e) {
e.preventDefault();
let data_array = $("form").map(function(){
return $(this).serializeArray();
});
data_array.push({csrfmiddlewaretoken: "{{ csrf_token }}"})
console.log(data_array)
$.ajax({
type: "POST",
url: "{% if source == 'edit' %}{% url 'action:edit_position' project.id position.id %}{% else %}{% url 'action:new_position' project.id %}{% endif %}",
data:data_array,
success:function(html){
}
});
});
Since you are using a model form, you can directly pass in the request into the form instance to process both the forms simultaneously... See a simple signup scenario below where two model forms ( User_form and User_profile_Form) are being processed,
Views.py
def signup(request):
registered = False
if request.method == "POST":
user_form = User_form(data=request.POST)
user_profileform = user_profile_form(data=request.POST)
if(user_form.is_valid() and user_profileform.is_valid()):
user = user_form.save()
user.set_password(user.password)
user.save()
profile = user_profileform.save(commit=False)
profile.user = user
if 'profile_picture' in request.FILES:
profile.profile_picture = request.FILES['profile_picture']
profile.save()
registered = True
else:
print(user_form.errors, user_profileform.errors)
UPDATE 1 Adding template part
<!DOCTYPE html>
{% extends "base.html" %}
{% load static %}
{% load bootstrap_tags %}
{% block title %}
Signup
{% endblock %}
{% block body %}
<div class="container">
{% if registered %}
<h1>Thankyou for REGISTERING with us</h1>
<br>
click here to continue
{% else %}
<h4 style="text-align: center;" > Already have an account ? </h4>
<h3>PLEASE fill the registration form below</h3>
<form enctype="multipart/form-data" method="POST">
<div class="container sign-form">
{% csrf_token %}
<h4>
<u>Account Information : </u>
{{ user_form |as_bootstrap }}
<br>
<hr>
<br>
<u><h4>Personal Information :</h4></u>
{{ user_profileform |as_bootstrap }}
</h4>
<input type="submit" class = "btn btn-primary" name="submit" value="Sign Up" style="margin: 0% 15%; width: 70%">
</div>
</form>
{% endif %}
{% endblock %}

when user reply to a comment it is showing time of comment. It should show actual time. How can I fix?

I am learning django. I am working on a blog. When user reply to a comment it is showing time of its parent comment. It should show actual time of reply. I attached a picture with this post.Please have a look , you will better understand my question. How can I fix? I tried but all in vain. may be it is a silly mistake from me or i am not getting it. Thanks in advance
view.py
def blogPost(request, slug):
post = Post.objects.filter(slug=slug).first()
comments = BlogComment.objects.filter(post=post, parent=None)
replies = BlogComment.objects.filter(post=post).exclude(parent=None)
replyDict = {}
for reply in replies:
if reply.parent.sno not in replyDict.keys():
replyDict[reply.parent.sno] = [reply]
else:
replyDict[reply.parent.sno].append(reply)
context = {'post':post, 'comments':comments, 'user': request.user, 'replyDict': replyDict}
return render(request, 'blog/blogPost.html',context)
def postComments(request):
if request.method == 'POST':
comment = request.POST.get('comment')
user = request.user
postSno = request.POST.get('postSno')
post = Post.objects.get(sno=postSno)
parentSno = request.POST.get('parentSno')
if parentSno == "":
comments = BlogComment(comment=comment, user=user, post=post)
comments.save()
messages.success(request, 'Your Comment has been posted Successfully')
else:
parent = BlogComment.objects.get(sno=parentSno)
comments = BlogComment(comment=comment, user=user, post=post, parent=parent)
comments.save()
messages.success(request, 'Your reply has been posted Successfully')
return redirect(f"/blog/{post.slug}")
models.py
class Post(models.Model):
sno = models.AutoField(primary_key=True)
title = models.CharField(max_length=200)
content = models.TextField(max_length=10000)
author = models.CharField(max_length=20)
region = models.CharField(max_length=20)
slug = models.CharField(max_length=50, default="")
timestamp = models.DateTimeField(blank=True)
thumbnail = models.ImageField(upload_to="images", default="")
def __str__(self):
return self.title + 'by' + self.author
class BlogComment(models.Model):
sno = models.AutoField(primary_key=True)
comment = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True) #it is pointing the value of blogcomment so we use 'self'
timestamp = models.DateTimeField(default=now)
replytimestamp = models.DateTimeField(default=now)
def __str__(self):
return self.comment[0:10] + "..." + "by " + self.user.username
blogPost.html
{% for comment in comments %}
<div class="row border border-dark mx-0 my-3">
<div class="col-md-1"><img src="/media/images/usr.png" height="55px" width="55px"></div>
<div class="col-md-11"><b> {{comment.user.username}} </b>
<span class="badge badge-secondary">{{comment.timestamp | naturaltime}}</span>
<!--this will show time in 2 hours ago or x hour ago like-->
<div>
<p class="font-italic">{{comment.comment}}</p>
</div>
<div class="reply mx-0">
<p>
{% if user.is_authenticated %}
<button class="btn btn-primary btn-sm" type="button" data-toggle="collapse"
data-target="#replyBox{{comment.sno}}" aria-expanded="false" aria-controls="collapseExample">
reply
</button>
</p>
<div class="collapse" id="replyBox{{comment.sno}}">
<div class="card card-body mb-2">
<form action="/blog/postComments" method="POST">{% csrf_token %}
<div class="form-group">
<label for="comment">Post a Reply</label>
<input type="text" class="form-control" id="comment" name="comment"
placeholder="Write a reply Here">
<input type="hidden" name="parentSno" value="{{comment.sno}}">
</div>
<input type="hidden" name="postSno" value="{{post.sno}}">
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
{% endif %}
<div class="replies my-2">
{% for reply in replyDict|get_val:comment.sno %}
<!-- this will return replies associated with comment.sno in the replyDict[] -->
<div class="row my-2">
<div class="col-md-1"><img src="/media/images/usr.png" height="35px" width="35px"></div>
<div class="col-md-11">
<b> {{comment.user.username}} </b><span
class="badge badge-secondary">{{comment.timestamp | naturaltime}}</span>
<div>{{reply.comment}}</div>
</div>
<br>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
commentand reply pictures

{{object.image.url}} and {{object.image.path}} show as unknown in template where as {{object.image]} shows the path [Django]

I dont know what is going on but this is bothering me a lot. As written in the question .url returns unknown in template.
but when i try Model.objects.get(username=1) and print the returned object chained with .url (like object.url) it works and it gives the /media/url/url.png
Model
class Consultant(models.Model):
username= models.OneToOneField(User, on_delete=models.CASCADE)
fname = models.CharField(max_length=150)
lname = models.CharField(max_length=150)
phone = models.BigIntegerField()
country = models.CharField(max_length=100)
state = models.CharField(max_length=100)
desc = models.CharField(max_length=600)
image = models.ImageField(upload_to='Avatar')
resume = models.FileField(upload_to='documents/%Y/%m/%d')
def __str__(self):
return str(self.username)
View function
def dashboard(request):
if request.method == "GET":
user = request.user
type = User_Type.objects.get(username=user)
if type.type:
con = Consultant.objects.filter().values()
job = Job.objects.filter(owner=user).values()
conss = Consultant.objects.get(pk=1)
print(conss.image.url)
return render(request,"client.html",{
"con" : con,
"job" : job
})
else:
return HttpResponse("<h4>Comingsoon</h4>")
template
{% for n in con %}
<div class="ui card">
<div class="content">
<img class="right floated mini ui image" src="/media/{{n.image.url}}">
<div class="header">
{{ n.fname }} {{ n.lname }}
</div>
<div class="meta">
<div class="ui star rating" data-rating="4"></div>
</div>
<div class="description">
{{ n.desc }}
</div>
</div>
<div class="extra content">
<div class="ui two buttons">
<div class="ui basic green button">Hire</div>
<div class="ui basic blue button">Profile</div>
</div>
</div>
</div>
{% endfor %}
What's happening is that you're using .values() which returns a dict where n["image"] is not an object, but rather a url.
In your view, where you wrote the small test code, you didn't use values(), so you got an object back where conss.image is an ImageFieldFile object where a url attribute is the path.
Thus the different behavior.