Unable to render ForeignKey fields in my template - python

I'm making a comment system for my django app and i've been told it's best to make a seperate model for comment-voting. So i've done that and here's my models.py:
class Comment(models.Model):
user = models.ForeignKey(User, default=1)
destination = models.CharField(default='1', max_length=12, blank=True)
author = models.CharField(max_length=120, blank=True)
comment_id = models.IntegerField(default=1)
parent_id = models.IntegerField(default=0)
comment_text = models.TextField(max_length=350, blank=True)
timestamp = models.DateTimeField(default=timezone.now, blank=True)
def __str__(self):
return self.comment_text
class CommentScore(models.Model):
user = models.ForeignKey(User, default=1)
comment = models.ForeignKey(Comment, related_name='score')
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
def __str__(self):
return str(self.comment)
Here's my views.py where the comments are created:
def article(request, category, id):
name = resolve(request.path).kwargs['category']
for a, b in CATEGORY_CHOICES:
if b == name:
name = a
instance = get_object_or_404(Post, id=id, category=name)
allauth_login = LoginForm(request.POST or None)
allauth_signup = SignupForm(request.POST or None)
#comments
comment = CommentForm(request.POST or None)
ajax_comment = request.POST.get('text')
comment_length = len(str(ajax_comment))
comment_list = Comment.objects.filter(destination=id)
score = CommentScore.objects.filter(comment=comment_list)
if request.is_ajax():
if comment.is_valid():
comment = Comment.objects.create(comment_text=ajax_comment, author=str(request.user), destination=id)
print(comment)
comment.save()
score = CommentScore.objects.create(comment=comment)
score.save()
username = str(request.user)
return JsonResponse({'text': ajax_comment, 'text_length': comment_length, 'username': username})
else:
print(comment.errors)
context = {
'score': score,
'comment_list': comment_list,
'comment': comment,
'instance': instance,
'allauth_login': allauth_login,
'allauth_signup': allauth_signup
}
return render(request, 'article.html', context)
So the comment works fine, but as you can see a couple lines later i'm trying to then create a CommentScore instance to match with the comment. In my template, I've rendered each comment and it's fields (comment_text, author etc), but I want to render the upvotes field associated with that comment. How would I do this?
template
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
<p>{{ i.comment_text }}</p><br>
</div>
{% endfor %}
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = [
'comment_text',
'id',
'author',
'destination',
]
I've already tried the following and they haven't worked;
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
<p>{{ i.comment_text }}</p><br>
{% for i in comment_list.score_set.all %}
{{ i.upvotes }} #renders nothing
{% endfor %}
</div>
{% endfor %}
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
<p>{{ i.comment_text }}</p><br>
{% for j in i.score %}
{{ j.upvotes }} #Error: 'RelatedManager' object is not iterable
{% endfor %}
</div>
{% endfor %}
Having a lot of trouble so help is appreciated.

Changing "i.score" to "i.score.all" resolves the problem as the RelatedManaager error usually happens when you are trying to iterate over the manager and not the objects selected by that manager. - Solved by #joe-j
So it works now but if someone could explain the 2nd line of this syntax that would be great:
comment_list = Comment.objects.filter(destination=id)
score = CommentScore.objects.filter(comment=comment_list)
What exactly is happening when I assign comment=comment_list here? I copied this code from someone else but i'm still abit unsure how it's working.

Related

Check if logged in user is the author of a post | Django | If-else doesn't work

I want to check if the logged in user is the author of a post in my Forum. I have written some code to figure that out:
<div class="right-section-posts">
user: {{ user }} <!--Output: Admin-->
author: {{ post.author }} <!--Output: Admin-->
{% if user == post.author %}
<form action="DELETE">
{% csrf_token %}
<button type="submit" class="delete-btn" name="post-id" value="{{ post.id }}">Delete</button>
</form>
<button class="edit-btn">Edit</button>
{% endif %}
</div>
They both output the same but the statement returns false! Why?
Models.py
class Post(models.Model):
vote_count = models.IntegerField(default=0)
id = models.BigAutoField(primary_key=True)
created_at = models.DateField(default=date.today)
title = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
tags = models.CharField(max_length=200)
author = models.CharField(max_length=100, default="none")
def __str__(self):
return str(self.id) + ' ' + self.title
I tried different ways to get the Current user and the author. Doesn't work to.
(I think you might say that I should use ForeignKey instead of ´CharField´, when using that I get this Error:
ERROR: Column forum_app_post.author_id does not exist.
LINE 1: ...app_post". "description", "forum_app_post". "tags", "forum_app...
^
HINT: Perhaps the intention was to refer to the "forum_app_post.author" column.
)
The author field cannot be a CharField because it represents the user. You need to set author field as foreignkey.
You need to update your model like this:
from django.contrib.auth.models import User
class Post(models.Model):
vote_count = models.IntegerField(default=0)
id = models.BigAutoField(primary_key=True)
created_at = models.DateField(default=date.today)
title = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
tags = models.CharField(max_length=200)
author = models.ForeignKey(User,on_delete= models.CASCADE, verbose_name='Post Author')
def __str__(self):
return str(self.id) + ' ' + self.title
If you want to check the logged in user from all the registered posts, you should get all the posts first.
def get_all_posts(request):
posts = Post.objects.filter.all()
context = {
"posts" : posts,
}
return render(request,"my_page.html",context)
Then in the html page :
<div class="right-section-posts">
{% if posts %}
{% for post in posts %}
{% if request.user == post.author %}
<!--do what you want here-->
{% else %}
{% endif %}
{% endfor %}
{% else %}
<div class="alert alert-info">You have no registered post yet!</div>
<!-- /.container-fluid -->
</div>
{% endif %}
</div>
I also recommend using django-taggit for tags.

How to fix Order matching query doesn't exist in Django?

Unable to delete an object:
HViews is as follows
def deleteorder(request, pk):
order = Order.object.get(id=pk)
if request.method == 'POST':
order.delete()
return redirect()
context = {'item':order}
return render(request,'accounts/delet.html', context)
And
Urls.py is as
path('delete_order/<str:pk>/', views.deleteorder, name= 'delete_order')
models.py
class Order(models.Model):
STATUS = ( (' Pending', 'Pending'), ('Out for Delivery ', 'Out for Delivery '), ('Delivered ', 'Delivered ') , )
customers = models.ForeignKey(Customer, null=True, on_delete= models.SET_NULL)
products = models.ForeignKey( Product, null=True, on_delete = models.SET_NULL)
created_date = models.DateTimeField(auto_now_add=True, null=True)
status = models.CharField(max_length=100, null=True, choices = STATUS)
def __str__(self):
return self.products.name
Try this and see it it work for you. How to delete an order by ID or pk. For example you have order views like this:
def order_views(request):
my_order = OrderItem.objects.all()
#you can also filter through your order
context = {'my_order': my_order}
{% for order in my_order %}
#Your code here
Delete
{% endfor %}
def deleteorder(request, pk):
order = Order.object.get(id=pk)
order.delete()
return redirect()
path('delete_order/<pk>/', views.deleteorder, name= 'delete_order')
or
path('delete_order/<int:pk>/', views.deleteorder, name= 'delete_order')
Remember to pass the correct Order context with the pk in template, so you don't get reverse not found.
my code where POST is being called is given below.
{% extends 'accounts/main.html' %}
{% load static %}
{% block content %}
<form action="" method="POST">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{form}}
<hr>
{% endfor %}
<input type="submit" name="Submit">
</form>
{% endblock %}

django iterate over a list that is an item of a queryset

I have a list that is generated by a method on one of my models. On the home page it works wonderfully, however when I go to a detail view of one project I can access all the parts of that project as they are direct fields of the Model, but I can't access the items in the list.
Model:
class Project(models.Model):
date_published = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
area = models.ForeignKey(Area, on_delete=models.PROTECT)
title = models.CharField(max_length=128, unique=True)
slug = models.SlugField(max_length=64)
summary = models.CharField(max_length=256)
others = models.CharField(max_length=128, blank=True)
deadline = models.DateField(null=True, blank=True)
priority = models.ForeignKey(Priority, on_delete=models.PROTECT)
closed = models.DateTimeField(null=True, blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Project, self).save(*args, **kwargs)
#property
def updates(self):
updates = []
sequence_id = 1
categories = set(self.update_set.all().values_list(
'category__id', flat=True))
for cat_id in categories:
a = Update.objects.filter(
project=self, category__id=cat_id).order_by('added').last()
if cat_id == sequence_id:
updates.append(a)
else:
for i in range(cat_id - sequence_id):
updates.append('null')
updates.append(a)
sequence_id = cat_id
sequence_id += 1
return updates
class Update(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
category = models.ForeignKey(UpdateCategory, on_delete=models.PROTECT)
update = models.TextField(max_length=240, blank=True)
added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.update
The view is simple:
class ProjectDetailView(DetailView):
template_name = 'project_portal/project_detail.html'
queryset = Project.objects.all()
and here is the dynamic url that I am using:
path('project/<int:pk>/',
ProjectDetailView.as_view(), name='project_detail'),
As for the template, I'm lost, here is one of the things I have tried:
<!DOCTYPE html>
{% extends "project_portal/base.html" %}
{% block home %}
<div id="main">
<div id="content">
<div>
<h1>{{ object.title }}</h1>
<h1>hello</h1>
{% if object_list %}
{% for item in updates %}
<p>{{ item }}</p>
{% endfor %}
{% else %}
<h2>No records found for this project</h2>
{% endif %}
</div>
</div>
</div>
{% endblock %}
What do I need to do to access the "updates" list that gets generated?
update is a property of the model instance, you need to access it from there like any other attribute. Also note, there is no object_list in a detail view.
<div>
<h1>{{ object.title }}</h1>
<h1>hello</h1>
{% for item in object.updates %}
<p>{{ item }}</p>
{% endfor %}
</div>

2 models in 1 view and 1 template in Django?

So i have 2 models,
class Post(models.Model):
topic = models.CharField(max_length=200)
description = models.TextField()
created_by = models.ForeignKey(User, related_name='posts')
created_on = models.DateTimeField()
def __str__(self):
return self.topic
class Comment(models.Model):
commented_by = models.ForeignKey(User, related_name='comments')
commented_on = models.ForeignKey(Post, related_name='comments')
commented_text = models.CharField(max_length=500)
commented_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.commented_text
and i want to display post and comment on single template. So, i created this view:
def post_desc(request, pk):
post = get_object_or_404(Post, pk=pk)
comment_list = Comment.objects.all()
return render(request, 'post_desc.html', {'post': post, 'comment_list':comment_list})
and try to display them in template:
{% if post.created_on %}
{{ post.created_on }}
{% endif %}
<h1>{{ post.topic }}</h1>
<p>{{ post.description }}</p>
<h4>Comments</h4>
{% if comment_list.commented_on == post.topic %}
{{ comment_list.commented_by }}{{ comment_list.commented_text }}
{% endif %}
But it is only displaying post and not comment section. What i did wrong here?
the template code is wrong so the view, your view should be like this:
def post_desc(request, pk):
post = Post.objects.get(id=pk)
return render(request, 'post_desc.html', {'post': post})
and the template should be
{% for comment in post.comments.all %}
<p>{{comment.text}}</p>
{% endfor %}
Where comments is the related name to access the Comment objects from Post.
You have to iterate the comments.
{% for comment in comment_list %}
<do your thing>
{% endfor %}
There is one notable issues in the code.
No need to pass all the comments to template. Only pass the comments that belong to the Post.

Foreign key not rendering in template

I'm building a commenting system, which is working fine but i'm now trying to integrate voting. So I made another model to handle that and I tried to pair it using ForeignKey. Not too familiar with ForeignKey but i've looked at some other answers here to see how to render it in the template. I tried that using the nested for loop in my template below but {{ j.user }} doesn't render anything. Can anyone tell me what I'm doing wrong?
models.py
class Comment(models.Model):
destination = models.CharField(default='1', max_length=12, blank=True)
author = models.CharField(max_length=120, blank=True)
comment_id = models.IntegerField(default=1)
parent_id = models.IntegerField(default=0)
comment_text = models.TextField(max_length=350, blank=True)
timestamp = models.DateTimeField(default=timezone.now, blank=True)
def __str__(self):
return self.comment_text
class CommentScore(models.Model):
user = models.ForeignKey(User)
comment = models.ForeignKey(Comment)
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
views.py
...
comment_list = Comment.objects.filter(destination=id)
score = CommentScore.objects.all()
context = {
'score': score,
'comment_list': comment_list,
}
return render(request, 'article.html', context)
template
{% for i in comment_list %}
<div class='comment_div'>
<h3>{{ i.author }}</h3>
{% for j in comment_list.score_set.all %}
{{ j.user }} #nothing comes up
{% endfor %}
<p>{{ i.comment_text }}</p>
</div>
{% endfor %}
when using _set, the reverse relationship lookup, you must provide the full model name, you must also specify which instance this list of related models this "set" is for so it should be
{% for j in i.commentscore_set.all %}
{% empty %}
No scores found
{% endfor %}
You may also wish to set a related_name for the foreign key
comment = models.ForeignKey(Comment, related_name='scores')
...
{% for j in i.scores.all %}

Categories