So , I'm trying to write blog in Django and now I have problem with comments.
Below each post I have users comments , with comment author name , image and comment.
Comments class:
class Comments(models.Model):
body = models.TextField()
author = models.ForeignKey(User, related_name= 'upser')
image = models.OneToOneField(UserProfile , related_name='im')
timestamp = models.DateTimeField(auto_now=True)
post = models.ForeignKey(Post, related_name='comments')
class Meta:
ordering = ('-timestamp',)
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='oser')
picture = models.ImageField(upload_to='profile_images', blank=True)
def __unicode__(self):
return self.user
So , I have comments class connected with UserProfile class in which I have user's image.
That's what I have in template to display all comments:
{% for comment in comments %}
<li>
<div class="media" style="margin-top: 10px">
<a class="pull-left" href="#">
<img class="media-object" src={{ MEDIA_ROOT }}/{{ PLACE FOR USER'S PICTURE }} alt="" width="80px" height="80px" >
</a>
<div class="media-body">
<h4 class="media-heading">{{ comment.author}}
<small>{{ comment.timestamp }}</small>
</h4>
{{ comment.body }}
</div>
</div>
</li>
{% endfor %}
And question ,how can I access user image from template if I have Comments object there?
I don't know why you need the image attribute that is a foreign key to the UserProfile model.
You can just do that in the template:
{{comment.author.oser.picture.url}}
This does the following:
Get the comment, get its author, go to its "oser" attribute which is the corresponding UserProfile and then get the picture attribute's image url.
Hope that helps.
Your model looks somewhat strange to me, or it doesn't fit so much your description:
you have a "Comments" class (shouldn't it be "Comment", as it contains a single comment?) which has a body, author, timestamp, the post it belongs to... fine. But the field you named "image" is actually the user_profile, right? Also, a OneToOneField would imply a 1 to 1 relationship between a Comment(s) object and a UserProfile... most probably not what you want!
Actually you have the author already, so that is enough to get the picture... so author is a user -> user_profile -> picture. Or do i understand your Model & question incorrectly?
Related
I am trying to create a cartoon streaming website in which when a user clicks on their cartoon of choice(Eg:Pokemon), they get to see the seasons as a list as well as the detail of the cartoons.
from django.db import models
class Cartoon(models.Model):
name = models.CharField(max_length=200)
cover = models.ImageField()
description = models.TextField()
start_date = models.CharField(max_length=50)
end_date = models.CharField(max_length=50)
def __str__(self):
return self.name
class CartoonSeason(models.Model):
cartoon = models.ForeignKey(Cartoon, null=True, on_delete=models.SET_NULL)
number = models.IntegerField()
season_cover = models.ImageField(blank=True, null=False)
season_name = models.CharField(max_length=200, blank=True, null=True)
season_description = models.TextField(blank=True, null=False)
Here I have linked the Cartoon model with the CartoonSeason model using a Foreign Key so when a new season is to be added, it automatically gets linked with the corresponding Cartoon
from django.shortcuts import render
from django.http import HttpResponse
from django.views.generic import ListView, DetailView
from .models import CartoonSeason, Cartoon
class CartoonListView(ListView):
model = Cartoon
template_name = "index.html"
class CartoonSeasonView(DetailView):
queryset = CartoonSeason.objects.filter()
model = CartoonSeason
template_name = "season.html"
I am using a detail view to display the CartoonSeason model so that it displays the Cartoons details as well, but when I try to load the seasons, it only displays the season with the Primary Key of 1. I want it to display all of the seasons added to the cartoon.
Here's my seasons.html
{% extends 'base.html' %}
{% block title %}
test
{% endblock %}
{% block content %}
<main>
<section class="cartoon-description">
<div class="season_head">
<img src="{{object.cartoon.cover.url}}" width="260px" alt="">
<div class="cartoon-name">
<h1>{{object.cartoon.name}}</h1>
<small >{{object.cartoon.start_date}} - {{object.cartoon.end_date}}</small>
<br>
<div class="description">
<strong>Description:</strong>
<br>
<p>{{object.cartoon.description}}</p>
</div>
</div>
</div>
</section>
<hr>
</main>
{% endblock %}
Here is My urls.py
from .views import CartoonListView, CartoonSeasonView
urlpatterns = [
path('', CartoonListView.as_view(), name="home"),
path('cartoon/<int:pk>' , CartoonSeasonView.as_view(), name="cartoon"),
]
This is my main template
{% extends 'base.html'%}
{% block title %}
Home - CartoonsPalace
{% endblock %}
{% block content %}
<main>
<section class="cartoon-list">
<div class="select-text">
<h1>Pick Your Cartoon Series of Choice:-</h1>
</div>
{% for cartoon in object_list %}
<div class="list">
<a href="{% url 'cartoon' cartoon.pk %}"><div class="list-object">
<img src="{{cartoon.cover.url}}" alt="" width="184px">
<h3>{{cartoon.name}}</h3>
<span>{{cartoon.start_date}} - {{cartoon.end_date}}</span>
</div>
</a>
</div>
{% endfor %}
</section>
</main>
{% endblock %}
Any help would be appreciated.
Ok when you want to access all the objects related to another objects through foreign key , you have two simple options :
1.set query using the cartoon(reverse relation):
cartoon = Cartoon.objects.get(id = 'pk') # get the pk by passing in url
cartoon_season = cartoon.cartoonseason_set.all()
# object.(name of model CartoonSeason must be in lowercase )._set.all()
or set query using the CartoonSeason(recommended):
#before this you must have the id of the cartoon :
cartoon = Cartoon.objects.get(id = pk)
seasons = CartoonSeason.objects.filter(cartoon = cartoon)
I dont know about you cartoon template or urls, but I assume in you cartoon template where you show your cartoon detail you have a link to the season of the cartoon ,so there you should pass the id of the cartoon : {{cartoon.id}}
and in you urls you get this id to use in your season detail :
but note that when you are using DetailView and want to use that id passing by url you shoud define a get_queryset like this :
class CartoonSeasonView(DetailView):
model = CartoonSeason
template_name = "season.html"
def get_queryset(self, *args, **kwargs):
#before this you must have the id of the cartoon :
cartoon = Cartoon.objects.get(id = self.kwargs['pk'])
seasons = CartoonSeason.objects.filter(cartoon = cartoon)
and remember no need to user have query_set anymore.
by this you can show the name of the cartoon or the poster in the cartoonseason page.
and remember we user detail view in order to show detials of only one object not 7 ( like season of a cartoon ).You can use ListView or if you want to show different data in one template use TemplateView.
I am adding a like functionality in my website where users can like each others posts.
I have done this successfully, however have one issue. This is checking whether the user has already liked the post, which has to be performed specifically in my HOME view.
This is so I can render my home page. To encounter this issue, I perform a .annotate() on my posts when retrieving them, and see if a user has liked a post.
I then pass this onto my home template and check if the user exists within the posts likes property.
Here's the related code.
models.py:
class Post(models.Model):
file = models.ImageField(upload_to='images/')
summary = models.TextField(max_length=600)
pub_date = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
likes = models.ManyToManyField(User, through='Like', related_name='likes')
def __str__(self):
return self.user.username
def pub_date_pretty(self):
return self.pub_date.strftime('%b %e %Y')
def summary_pretty(self):
return self.summary[:50]
#property
def total_likes(self):
return self.likes.count()
class Like(models.Model):
status = models.BooleanField()
post = models.ForeignKey(Post, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
views.py:
def home(request):
posts = Post.objects.all()
liked_or_not = Post.objects.annotate(likes__user=request.user)
return render(request, 'posts/home.html', {'posts': posts, 'liked_or_not': liked_or_not})
home.html:
{% if liked_or_not == True %}
<button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Unlike {{ post.total_likes }} </button>
{% else %}
<button class="btn btn-primary btn-lg btn-block"><span class="oi oi-caret-top"></span> Like {{ post.total_likes }} </button>
{% endif %}
<form id="likepost{{ post.id }}" method="POST" action="{% url 'likepost' post.id %}">
{% csrf_token%}
<input type="hidden">
</form>
For those coming here for other possibilities in debugging the error message, I had made a mistake and entered in a query analogous to
Like.objects.filter(user=request.user).values('status', flat=True)
# instead of the correct
Like.objects.filter(user=request.user).values_list('status', flat=True)
I don't quite understand what do you want to do. If you want to check if the user liked at least one post, you can do it like this:
liked_or_not = Like.objects.filter(user=request.user).exists
If you want to check if a user liked a specific post, you can dot it like this:
liked_or_not = Likes.objects.filter(post_id=post_id, user=request.user).exists()
annotate has a different purpose. It annotates each object in the QuerySet with the provided list of query expressions. An expression may be a simple value, a reference to a field on the model (or any related models), or an aggregate expression (averages, sums, etc.) that has been computed over the objects that are related to the objects in the QuerySet. read more here https://docs.djangoproject.com/en/2.2/ref/models/querysets/#annotate
I encounterd similar problem and solved using .annotate
and below is my definition of Like in models.py
class Like(models.Model):
user = models.ForeignKey(
"users.User", on_delete=models.CASCADE, related_name="likes"
)
writing = models.ForeignKey(
"writings.Writing", on_delete=models.CASCADE, related_name="likes"
)
in views.py
filter_kwargs = {}
filter_kwargs["writing"] = OuterRef("pk")
filter_kwargs["user"] = request_user
subquery = Like.objects.filter(**filter_kwargs)
writing = (
Writing.objects.annotate(user_likes=Exists(subquery))
.get(id=id)
)
I am trying to create a template with Django that loops through Posts and for each Post loops through all Pictures.
I already looked at some answers to other Questions but I can not find my error.
Models:
class Post(models.Model):
Post_Title = models.CharField(max_length=200)
Post_pub_date = models.DateField('date published')
Post_Author = models.CharField(max_length=100)
Post_Text = models.TextField()
def __str__(self):
return self.Post_Title
class Picture(models.Model):
Post = models.ForeignKey(Post, on_delete=models.CASCADE)
Picture = models.ImageField()
Picture_Name = models.CharField(max_length=100, null=True, blank=True)
def __str__(self):
return self.Picture_Name
Views:
class PostView(generic.ListView):
template_name = 'myblog/index.html'
context_object_name = 'Post_List'
def get_queryset(self):
"""
Returns Posts
"""
return Post.objects.order_by('-Post_pub_date')
Template:
{% for Post in Post_List %}
<h1 class="mb-4">{{Post.Post_Title}}</h1>
<span class="category">{{Post.Post_Author}}</span>
<span class="mr-2">{{Post.Post_pub_date}}</span>
<div class="post-content-body"><p>{{Post.Post_Text}}</p>
{% for Picture in Post.Picture_set.all %}
<div class="col-md-12 mb-4 element-animate">
<h2>{{Picture.Picture_Name}}</h2>
<img class="col-md-12 mb-4 element-animate" src="{{ MEDIA_URL }}{Picture.Picture}}">
</div>
{% endfor %}
</div>
{% endfor %}
The Post_Title, Post_Author, Post_pub_date and Post_Text are displayed fine. Just the nested For loop is not producing any Picture_Name or Picture as if the Picture_set.all is empty.
As mentioned above I tried to find my error in different Posts like this but could not find it.
Thanks for your help.
Following relationship backward, you need to write related model name from a small letter, even if model name starts from large letter:
{% for Picture in Post.picture_set.all %}
This is how it works in Django shell and i suppose in templates it is the same.
The issue isn't the nested for loop it's the view.It only returns a query for your Post, you don't pass any Photos to your template.
I have 3 models. User, Post and Proof. Posts are added by a User to the website. When other users do what the post says. They upload 2 images to say they did the task. The template will show the 2 images they uploaded. I know I am making some mistake in the template calling the images. Does anyone know the correct way to use related_name to call for the images. FYI I am new to python and Django I am sorry if this question is too trivial or has wrong logic
Proof/models.py
User = get_user_model()
class Proof(models.Model):
user = models.ForeignKey(User, related_name='proofmade')
post = models.ForeignKey(Post, related_name='postproofmade')
made_at = models.DateTimeField(auto_now=True)
image_of_task= models.ImageField()
proof_you_made_it = models.ImageField()
suggestions = models.TextField(max_length=1000)
def __str__(self):
return self.post.title
Templates
{% for user in post.made.all %}
<div class="container">
<img src="{{ user.proofmade.image_of_task.url }}" height="150px"/>
<img src="{{ user.proofmade.proof_you_made_it.url }}" height="150px"/>
</div>
{% endfor %}
Post/views.py
class PostDetail(SelectRelatedMixin, DetailView):
model = Post
select_related = ('user', 'group')
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user__username__iexact=self.kwargs.get('username'))
A ForeignKey is a one-to-many relationship; the related_name therefore gives access to a queryset corresponding to many items. You need to iterate over that queryset to get the individual items, in this case the Proof instances - just as you do with the users for each post. So, something like:
{% for user in post.made.all %}
{% for proof in user.proofmade.all %}
<div class="container">
<img src="{{ proof.image_of_task.url }}" height="150px"/>
<img src="{{ proof.proof_you_made_it.url }}" height="150px"/>
</div>
{% endfor %}
{% endfor %}
Here's my Post model:
models
class Post(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
title = models.TextField(max_length=76)
content = models.TextField(null=True, blank=True)
...
class PostScore(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
post = models.ForeignKey(Post, related_name='score')
upvotes = models.IntegerField(default=0)
downvotes = models.IntegerField(default=0)
And here's my template. I want to do something like this... if the user has upvoted or downvoted the Post, then hide the upvote/downvote buttons:
{% if request.user in Post.has_answered %}
{% else %}
<img src="upvote.png" class="upvote" />
<img src="downvote.png" class="downvote" />
{% endif %}
I planned to do this by adding a ManyToManyField called has_answered to my Post model, but i'm unable to do that as I get this error:
post.Post.has_answered: (fields.E304) Reverse accessor for 'Post.has_answered' clashes with reverse accessor for 'Post.user'.
HINT: Add or change a related_name argument to the definition for 'Post.has_answered' or 'Post.user'.
post.Post.user: (fields.E304) Reverse accessor for 'Post.user' clashes with reverse accessor for 'Post.has_answered'.
HINT: Add or change a related_name argument to the definition for 'Post.user' or 'Post.has_answered'.
Any idea how I can fix this? I'm not too sure about the error message as I don't think I can alter my current user field.
You can change your models to like this. You might not need PostScore model.
class Post(models.Model):
# Other Fields i.e title, content, author ...
upvotes = models.ManyToMany(User)
downvotes = models.ManyToMany(User)
You can get upvotes on a Post using this.
upvotes = post_object.upvotes.count()
downvotes = post_object.downvotes.count()
To see whether user has upvoted or not,
if request.user in post_object.upvotes.all():
# This user has upvoted this post
Same for downvotes.
You can do similar thing in your template as well and hide/show buttons based on condition.
{% if request.user in post_object.upvotes.all %}
<!-- show upvote button highlighted -->
{% elif request.user in post_object.downvotes.all %}
<!-- show downvote button highlighted -->
{% else %}
<!-- Show both buttons (not highlighted) -->
{% endif %}
Hope this helps.
Have you tried send the flag from your django views to template like :
def myView(request):
parameters['is_user_voted'] = PostScore.objects.filter(user=self.request.user).exists()
.....
.....
send ```parameters``` to your template using render()
And change you template as:
{% if is_user_voted == 'True' %}
{% else %}
<img src="upvote.png" class="upvote" />
<img src="downvote.png" class="downvote" />
{% endif %}