Django template user in queryset condition - python

I have the following Post and Follow models:
class Post(models.Model):
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
date_modified = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
#property
def followers(self):
return self.follow_set.filter(post=self).values_list('user', flat=True)
class Follow(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
I tried the following code in shell to check for data in .followers property:
>>> from posts.models import Post
>>> x= Post.objects.get(id=22)
>>> x.followers
<QuerySet [1]>
What I ultimately want is to check if the authenticated user is among the followers. I have the following template code:
{% if user in object.followers %}
Unfollow
{% else %}
Follow
{% endif %}
The problem I have is that {% if user in object.followers %} always evaluates to False. P.S.: I always log-in the user id=1 (same as the queryset result from above).

Use
{% if user in object.followers.all %}

Nevermind. I just realized my mistake.
{% if user.id in object.followers %}

Related

Show all comments from a specific user in django

I am trying to consolidate all the comments, on various products, from the logged in user in an "Account" page.
My initial plan was to request all comments from the user id.
Because I created a Profile model, I thought the right way to approach this was to link it to the profile id, and not directly to the use id.
Obviously, it's not working.
Am I close to it? or should I think of it completely differently? (new to programming, as you can see on the code)
Starting with my models.py
class ReviewRating(models.Model):
user = models.ForeignKey(User,blank=True, on_delete=models.CASCADE)
product=models.ForeignKey(Product,related_name="comments", on_delete=models.CASCADE)
review=models.TextField(max_length=250)
def __str__(self):
return '%s - %s - %s'%(self.user, self.product, self.date_added)
class Profile(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
general_reviews = models.ForeignKey(ReviewRating,null=True, on_delete=models.CASCADE)
def str(self):
return str(self.user)
Views.py
def account(response, profile_id):
generalreviews_list = Profile.general_reviews.objects.all(pk=profile_id)
return render(response,"main/account.html", {'generalreviews_list':generalreviews_list})
URLS
path("account/<profile_id>/", views.account, name="account"),
Method - 1 (easiest and logical method)
profile.html:
{% for comment in request.user.reviewrating_set.all %}
{{ comment.review }}
{% endfor %}
Method - 2 (by setting a related_name what Django already defines one called foo_set )
You need to add a related_name attribute to the user field under ReviewRating
class ReviewRating(models.Model):
user = models.ForeignKey(User,blank=True, on_delete=models.CASCADE, related_name="comments")
That's It! You can use it on the profile HTML like this:
profile.html:
{% for comment in request.user.comments %}
{{ comment.review }}
{% endfor %}
The best way is to link it with the main user model in your case it will be best to link it with user_id. After that, you can use the following query to access all the comments made by the currently logged-in user.
Views.py
current_user = request.user
queryset_obj = Model.objects.filter(foreign_key_field_name=current_user)
context = {
'comments' = quesyset_obj,
}
Templates.py
{% for comment in comments %}
{{comment.review}}
{% endfor %}

Queryset filter by variable that's in another queryset

I am trying to filter a queryset by a variable in another queryset that hasn't been set yet. I know it sounds confusing so let me show you.
Views.py
def ViewThreadView(request, thread):
posts = Post.objects.filter(post_thread=thread)
thread = Thread.objects.get(pk=thread)
form_class = QuickReplyForm
thread_name = thread.name
return render(request, 'thread/viewthread.html',
{'thread': thread, 'posts': posts, 'thread_name': thread_name})
Post Model
class Post(models.Model):
post_body = models.TextField(blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post_date = models.DateTimeField(auto_now_add=True)
post_thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
def __str__(self):
return str(self.id) + ' | ' + str(self.author)
The User model is the standard Django model
As of now, if I want to access the post author in the template, I'd do this
{% for post in posts %}
post.author
{% endfor %}
My question is, how do I access the tables of post.author. So if I want to filter how many posts that author has, I want to do something like user_posts = Post.objects.get(author=post.author). But that can't work in the views because "posts" is a queryset and not a value. How can I do this?
In your template you can access your related objects with post_set:
{% for post in posts %}
{{ post.author.post_set.count }}
{% endfor %}
If you need more then the total number of posts, do you want filter your related objects or something else. You can always write a custom method for your model. See Model methods
For example:
from django.utils.functional import cached_property
class Post(models.Model):
post_body = models.TextField(blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post_date = models.DateTimeField(auto_now_add=True)
post_thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
def __str__(self):
return str(self.id) + ' | ' + str(self.author)
#cached_property
def count_by_author_and_thread(self):
return self.author.post_set.filter(post_thread=self.post_thread).count()
And then in your template simple use:
{% for post in posts %}
{{ post.count_by_author_and_thread }}
{% endfor %}

Check User Perms Django

I am trying to use Django's default permissions to only show the link to the blog post page if they have the blog post permissions.
The wonderful Django documentation provides the following:
Assuming you have an application with an app_label foo and a model named Bar,
to test for basic permissions you should use:
add: user.has_perm('foo.add_bar')
change: user.has_perm('foo.change_bar')
delete: user.has_perm('foo.delete_bar')
view: user.has_perm('foo.view_bar')
My app is named about
Here is my model:
# models.py
class BlogPost(models.Model):
title = models.CharField(max_length=120)
description = models.TextField(max_length=250, null=True)
image = models.ImageField(upload_to='blog/main')
content = HTMLField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
draft = models.BooleanField(default=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("post-detail", kwargs={"pk": self.id})
Here is my HTML where I am trying to filter
# base.html
{% if user.has_perm('about.add_blogPost') %}
<li {% if url_name == 'post' %}class="nav-item active"{% else %}class="nav-item"{% endif %}>Add Post</li>
{% endif %}
Whenever I follow it just as the documentation has written, I get:
Could not parse the remainder: '('about.add_blogPost')' from 'user.has_perm('about.add_blogPost')'
What am I doing wrong?
try
{% if user.has_perm 'about.add_blogPost' %}

Django Template - show many-to-many relationship

I'm learning Django after having built some basic apps in Flask. One thing I want to do is show users a list of all posts and whether or not they follow that given post. However, Jinja or Django is throwing some error that I don't quite know how to debug.
Models.py
class User(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(unique=True, max_length=120,blank=False)
password = models.CharField(max_length=120, blank=True, null=False)
class Record(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=120, unique=True, blank=True)
followers = models.ManyToManyField(User, through='Follow')
class Follow(models.Model):
id = models.AutoField(primary_key=True)
record = models.ForeignKey(Record)
user = models.ForeignKey(User)
date_followed = models.DateField(null=True, blank=True)
records.html
{% for i in records %}
{% if i.follow.filter(id='1').first() %}
DO SOMETHING
{% endif %}
{% endfor %}
error
TemplateSyntaxError at /records/
Could not parse the remainder: '(id='1').first()' from 'i.follow.filter(id='1').first()'
To test this out when I run the python manage.py shell and execute the following I have no issues:
>>> x = Record.objects.first()
>>> x.followers.filter(id='1').first()
<User: User object>
I had initially prototyped this app using Flask and had the following jinja template and never had an issue:
{% for i in accounts %}
{% if i.follow.filter_by(user_id='1').first() %}
DO SOMETHING
{% endif %}
{% endfor %}
You cannot do that logic in template. You can create a method in Record model that does it for you and you can call it in template
class Record(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=120, unique=True, blank=True)
followers = models.ManyToManyField(User, through='Follow')
def first_follower(self):
if self.follow_set.filter(user_id=1).exists():
return True
return False
and in template:
{% for i in records %}
{% if i.first_follower %}
DO SOMETHING
{% endif %}
{% endfor %}
This is by design https://code.djangoproject.com/ticket/1199
The idea is that a django template should focus on design, for designers, and let the more complex code run in Python, not when the template renders.
So if this is a single instance when you use this check, add it to the view:
def get_context_data(self,*arg,**kwargs):
context = super(MyRecordView,self).get_context_data(*args,**kwargs)
context[has_follow] = self.object.follow.filter_by(user_id='1').exists()
return context
In the template:
{% if has_follow %}
...
{% endif %}
However, if you use this check a lot, you can add it to your model:
def has_follow(self):
return self.follow.filter_by(user_id='1').exists()
And then you can access it in a template, w/o any changes to the view context, since it's a model attribute:
{% if i.has_follow %}
...
{% endif %}

How to display fields of a M2M relation

I have this Django model:
#encoding:utf-8
from django.db import models
from django.contrib.auth.models import User
from django.contrib import admin
# Create your models here.
class Profile(models.Model):
user = models.ForeignKey(User, unique=True, related_name='user')
follows = models.ManyToManyField(User, related_name='follows', symmetrical=False, blank=True)
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatar', blank=True)
website = models.CharField(max_length=100, blank=True)
place = models.CharField(max_length=100, blank=True)
def __unicode__(self):
return unicode(self.user)
admin.site.register(Profile)
admin.autodiscover()
Then, I have this view:
def profile(request, user_id):
user = get_object_or_404(Profile, user=user_id)
return render_to_response('user_view.html', {'user':user,})
Now, in user_view.html I display the 'User' data (username, avatar, website, etc):
{{user.user_id}}
{{user.user}}
{{user.website}}
{{user.avatar.url}}
But, also I need to display the username, avatar and website of the users I follow. I did this:
{% for element in user.follows.all %}
{{element.user}}
<img src="{{element.avatar.url}} />
{% endfor %}
But, instead of display the user's data, it shows this:
<django.db.models.fields.related.RelatedManager object at 0x022F34F0>
But if I do this:
{% for element in user.follows.all %}
<p>{{element}}</p>
{% endfor %}
It shows perfectly the user's name, but I still cannot display the website or the avatar. How can I do this?
I think you would try
{% for element in user_data.follows.all %}
<p>{{element.username}}</p>
<p>{{element.profile.website}}</p>
<p>{{element.profile.avatar}}</p>
{% endfor %}
element is an instance of User and not an instance of Profile.
But you can still retrieve the associated Profile instance by writing
element.profile
because it's a One-To-One relationship (OneToOneField or ForeignKey(unique=True))

Categories