I have 2 tables (posts, upvoted) that I am working within and am looking to see if a post has been upvoted already and if it has, replace the upvote arrow with a filled arrow. In my view, I am already sending over the Upvote object to my template and am trying to check if the post.id exists within the upvoted table. I tried the code below but it didn't work. How would I do it?
Model:
class Post(models.Model):
user = models.ForeignKey(User, related_name="posts", on_delete=models.DO_NOTHING)
body = models.CharField(max_length=255)
image = models.ImageField(
default="onebyone.png", upload_to="images/", blank=True, null=True
)
created_at = models.DateTimeField(auto_now_add=True)
post_karma = models.IntegerField(default=0, blank=True, null=True)
comment_count = models.IntegerField(default=0, blank=True, null=True)
def __str__(self):
return f"{self.user.username} - {self.created_at.strftime('%Y-%m-%d')} - {self.body[:30]}"
class Upvoted(models.Model):
user = models.ForeignKey(User, related_name="upvoted", on_delete=models.CASCADE)
post = models.ForeignKey(Post, related_name="upvoted", on_delete=models.CASCADE)
upvoted_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return (
f"{self.user.username} - {self.post.user.username} - {self.post.body[:30]}")
View:
#login_required
def dashboard(request):
# form = PostForm(request.POST or None)
# if request.method == "POST":
form = PostForm(request.POST, request.FILES)
if form.is_valid():
img = form.cleaned_data["image"]
body = form.cleaned_data["body"]
if img is None:
img = "images/onebyone.png"
post = Post.objects.create(user=request.user, image=img, body=body)
post.save()
return redirect("posty:dashboard")
sorting = request.GET.get("sort")
ordering = {
"date_sort": "-created_at",
"karma_sort": "-post_karma",
"comment_sort": "-comment_count",
None: "-created_at",
}
# followed_posts = Post.objects.filter(
# user__profile__in=request.user.profile.follows.all()
# ).order_by("-created_at")
followed_posts = Post.objects.filter(
user__profile__in=request.user.profile.follows.all()
).order_by(ordering[sorting])
# get current date_time
now = datetime.now()
return render(
request,
"posty/dashboard.html",
{"form": form, "posts": followed_posts, "comment": Comment, "now": now, "upvote" : Upvoted, "downvote" : Downvoted},)
{% if post.id in upvote.post_id.all %}
First, don't send your Model objects in context, you must do the queries in your view, then send the results to context.
Second:
You can use annotate() and Exists() for that.
from django.db.models import Exists, OuterRef
followed_posts = (
Post.objects.filter(
user__profile__in=request.user.profile.follows.all()
)
.annotate(
is_liked_by_user=Exists(
Upvoted.objects.filter(post_id=OuterRef('pk'), user=request.user)
)
)
.order_by(ordering[sorting])
)
Then in your template:
{% if post.is_liked_by_user %}
... put whatever you want here.
{% endif %}
Related
views.py
def post_details(request,pk):
post = Post.objects.get(id=pk)
# next_post = Post.objects.filter(id=pk)
context={'post':post,'next':next_post}
return render(request, 'blog/post_detail.html', context)
blog-detail
<div class="s-content__pagenav group">
<div class="prev-nav">
<a href="#" rel="prev">
<span>Previous</span>
Tips on Minimalist Design
</a>
</div>
<div class="next-nav">
<a href="#" rel="next">
<span>Next</span>
Less Is More
</a>
</div>
</div>
models
# this is my model
class User(AbstractUser):
# pass
name = models.CharField(max_length=200)
bio = models.TextField(null=True)
email = models.EmailField(unique=True, null=True)
avatar = models.ImageField( null=True, upload_to='blog_media', default="images/avatar.svg")
facebook = models.URLField(blank=True, null=True)
twitter = models.URLField(blank=True, null=True)
dribbble = models.URLField(blank=True, null=True)
instagram = models.URLField(blank=True, null=True)
class Category(models.Model):
name = models.CharField(max_length=20)
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
title = models.CharField(max_length=200, blank=False);
description = models.TextField(null=True,blank=True)
image = models.ImageField(upload_to='blog_media')
url = models.URLField(null=True, blank=True)
body = HTMLField()
created = models.DateTimeField(auto_now=True)
updated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Based on your comments, I'm assuming that you would like to get two related posts that have the same category as the current post.
If I'm correct, then one method you could use is to filter the queryset for the same category belonging to the current post then you could choose the next and previous posts of the current post from the retrieved queryset. For example:
def post_details(request, pk):
current_post = Post.objects.get(pk=pk) # retrieving the current post...
# filtering for related posts only by using the category of the current post
# using -> category_in=post.category.all() since it's a ManyToMany field
related_posts = Post.objects.filter(category_in=current_post.category.all())
# next -> get posts with id greater than the current post id, then get the first instance 'next post'
# previous -> get posts with id less than the current post id, then get the first instance 'previous post'
context = {
'post': current_post,
'next': related_posts.filter(id__gt=current_post.id).order_by('id').first(),
'previous': related_posts.filter(id__lt=current_post.id).order_by('-id').first()
}
return render(request, 'blog/post_detail.html', context)
Ideally, that should work.
A quick recommendation here as well... Instead of using Post.objects.get(pk=pk), I'd suggest using get_object_or_404() as this will handle any potential error that Post.objects.get(pk=pk) will throw. So a small update...
from django.shortcuts import get_object_or_404
def post_details(request, pk):
current_post = get_object_or_404(Post, pk=pk) # retrieving the current post...
# the rest of the code follows...
I do what I want to do
I want to display user comment and data that is searched by tv_id in comment object.
Each comment objects has tv_id.
I want to get data by TV_id of each comment object and display it in HTML with the comment. However, I can't find a way to display the comment object and TV data at the same time. How can I do this?
Current state
class TV(models.Model):
id = models.CharField(primary_key=True, editable=False,
validators=[alphanumeric],max_length = 9999)
stars = models.FloatField(
blank=False,
null=False,
default=0,
validators=[MinValueValidator(0.0),
MaxValueValidator(10.0)]
)
def get_comments(self):
return Comment_tv.objects.filter(tv_id=self.id)
def average_stars(self):
comments = self.get_comments()
n_comments = comments.count()
if n_comments:
self.stars = sum([comment.stars for comment in comments]) / n_comments
else:
self.stars = 0
return self.stars
class Comment_tv(models.Model):
class Meta:
unique_together = ('user', 'tv',)
comment = models.TextField(max_length=1000)
stars = models.FloatField(
blank=False,
null=False,
default=0,
validators=[MinValueValidator(0.0),
MaxValueValidator(10.0)]
)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
tv = models.ForeignKey(TV, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ('user', 'tv')
indexes = [
models.Index(fields=['user', 'tv']),
]
data = requests.get(f"https://api.themoviedb.org/3/tv/{tv_id}?api_key={TMDB_API_KEY}&language=en-US")
class Comment_List_TV(ListView):
template_name = 'account/user_comment_list_tv.html'
def get_queryset(self):
Comment_list_query = Comment_tv.objects.none()
if self.request.user.is_authenticated:
Comment_list_query = Comment_tv.objects.filter(user=self.request.user)
return Comment_list_query
It's a good practice to add context_object_name in your generic views, so you will have a more recognizable name for the queryset inside your templates.
class Comment_List_TV(ListView):
###
context_object_name = "comment_tv_list" # This is what the list will be called in the template
###
template_name = 'account/user_comment_list_tv.html'
def get_queryset(self):
Comment_list_query = Comment_tv.objects.none()
if self.request.user.is_authenticated:
Comment_list_query = Comment_tv.objects.filter(user=self.request.user)
return Comment_list_query
Then in your template:
{% for comment_tv in comment_tv_list %}
{{ comment_tv.user }}
{{ comment_tv.comment }}
{{ comment_tv.stars }}
{{ comment_tv.tv.id }}
{% endfor %}
I just lost myself a little, and I'm stuck on this one.
I have a model which has a group field :
class CalendarGroups(models.Model):
GRP_CALS = (
('Grp1', 'Grp1'),
('Grp2', 'Grp2'),
('Test', 'Test'),
)
name = models.CharField(max_length=155, choices=GRP_CALS, blank=True, null=True)
def __str__(self):
return self.name
class Meta:
...
class CalendarMaster(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
date_created = models.DateTimeField(auto_now_add=True)
group = models.ForeignKey(CalendarGroups, on_delete=models.CASCADE)
date_valid = models.DateTimeField(null=True, blank=True)
I just want to check, if the User's group matches the Calendar group - some context will be rendered.
My views :
#login_required(login_url='registration/login')
def add_event(request, pk):
opp = get_object_or_404(OpportunityList, pk=pk)
opp_locked = get_object_or_404(Locked, pk=pk)
user = User.objects.get(username=request.user.username)
...
user_groups = request.user.groups.values_list('name', flat=True)
events_all = Events.objects.all()
calendars = Calendar.objects.all()
form = ...
if request.method == 'POST':
form = ...(request.POST or None)
if form.is_valid():
event_form = form.save(commit=False)
event = Events.objects.create(
event_name=opp_locked.type + '/' + str(opp.oppc_place) + '/' + opp.oppc_client_name,
event_comment=form.cleaned_data['event_comment'],
...
)
...
event.save()
messages.success(request, '...' + ...)
return redirect('...')
context = {
'form': form,
'opp': opp,
'events': events_all,
"calendars": calendars,
"today": datetime.now().date(),
"user": user,
"user_groups": user_groups,
}
return render(request, '...', context)
I need something like :
{% if user_group == calendar_group %}
But somehow, I cant manage it -.-
PS. User groups are the same as CalendarMaster's
I just added another field which is linking those two tables. Since there wasn't any relation to those two tables, it was impossible to link them by any chance.
it's been a few hours since I tried to retrieve a list of users with the information of an intermediate table.
So I have a workspace model that is a manytomanyfield with users
There is also an intermediary table to differentiate the classic users and the workspace manager
I would like to display the list of users and add a small icon symbolizing the managers in the list.
But unfortunately it seems difficult for Django, to display both the list of users of the workspace with the information of the intermediate table.
In any case I look at the documentation of Django I have not managed to find how to do.
models.py
class Workspace(models.Model):
name = models.CharField(max_length=250, verbose_name="Nom du workspace")
members = models.ManyToManyField(User, through='Membership', verbose_name="Membres du workspace")
token = models.CharField(max_length=500) # token statique
join_token = models.CharField(max_length=500) # token dynamique
join_token_date = models.DateTimeField(auto_now_add=False, null=True, blank=True)
payday = models.DateField(max_length=10, verbose_name="Jour de paye", null=True, blank=True)
planning_image = ProcessedImageField(upload_to='planning',
null=True,
blank=True,
processors=[ResizeToFill(1299, 937)],
format='JPEG',
options={'quality': 100})
planning_thumbnail = ImageSpecField(source='planning_image',
processors=[ResizeToFill(280, 202)],
format='JPEG',
options={'quality': 100})
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('create-workspace')
class Membership(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
workspace = models.ForeignKey(Workspace, on_delete=models.CASCADE)
is_manager = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
views.py
#login_required
def workspace_detail(request, token):
ins_workspace = get_object_or_404(Workspace, token=token)
list_members = ins_workspace.members.all()
for member in list_members:
if member == request.user:
current_user = Membership.objects.get(workspace=ins_workspace, user=request.user)
context = {
'name': ins_workspace.name,
'token': ins_workspace.token,
'list_members': list_members,
'payday': ins_workspace.payday,
'is_manager': current_user.is_manager,
}
return render(request, 'workspace/workspace_detail.html', context)
else:
return HttpResponseForbidden()
template.html
{% for item in list_members %}
{{ item.username }}
{% endfor %}
This is what I want:
template.html
{% for item in list_members %}
{% item.is_manager %}
{{ item.username }} (♔)
{% else %}
{{ item.username }}
{% endfor %}
You can do it like this:
Update Membership model with related name:
class Membership(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="membership")
workspace = models.ForeignKey(Workspace, on_delete=models.CASCADE)
is_manager = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
Then you can update your view like following:
from django.db.models import F
#login_required
def workspace_detail(request, token):
ins_workspace = get_object_or_404(Workspace, token=token)
list_members = ins_workspace.members.all().annotate(is_manager=F('membership__is_manager'))
context = {
'name': ins_workspace.name,
'token': ins_workspace.token,
'list_members': list_members,
'payday': ins_workspace.payday,
'is_manager': request.user.membership.get(workspace=ins_workspace).is_manager,
}
return render(request, 'workspace/workspace_detail.html', context)
That should do the trick.
Here what I have done is that, I am using a reverse relation to get is_manager value from membership model. I am annotating that value in the queryset using F.
I try to create a new article with model data:
class Article(models.Model):
STATUS = (
(0, 'normal'),
(-1, 'deleted'),
)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
block = models.ForeignKey(Block, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
content = models.TextField() # set the widget
status = models.IntegerField(choices=STATUS)
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
However, when I submitted data from browser, it prompted that comment field is required thought it's not one of Article's fields.
I add a test command print(form.errors.as_data()) in CBV class ArticleCreateView(View):
Starting development server at http://127.0.0.1:8001/
Quit the server with CONTROL-C.
{'comment': [ValidationError(['This field is required.'])]}
[09/Jun/2018 22:50:16] "POST /article/create/1 HTTP/1.1" 200 3694
I have other table Comment whose ForeignKey is article
class Comment(models.Model):
STATUS = (
(0, 'normal'),
(-1, 'deleted'),
)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
comment = models.TextField() # set the widget
status = models.IntegerField(choices=STATUS)
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.comment
The views.py
class ArticleCreateView(View):
template_name = "article/article_create.html"
def get(self, request, block_id):
block = Block.objects.get(id=block_id)
context = {'b':block}
return render(request, self.template_name,context)
def post(self, request, block_id):
block = Block.objects.get(id=block_id)
form = CommentForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.owner = request.user
article.block = block
article.status = 0
article.save()
return redirect(f"/article/list/{ block_id }")
else:
print(form.errors.as_data())
context = {'b':block,
"form":form}
return render(request, self.template_name, context)
I had no idea why it throw such an error?
In your view, it says CommentForm.
class ArticleCreateView(View):
def post(self, request, block_id):
...
form = CommentForm(request.POST)
Maybe you wanted to use something like ArticleForm or whatever you have in your code?