Check User Perms Django - python

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' %}

Related

Objects have the same ID on production in Django App

I have project in Python Django. It has three models: Project, Files and Agreement.
class Project(models.Model):
project_name = models.CharField(max_length=50)
client_name = models.CharField(max_length=50)
agreement_number = models.CharField(max_length=20)
brief_status = models.CharField(max_length=20, choices=BRIEF_CHOICES, default='nieuzupelniony')
agreement_status = models.CharField(max_length=20, choices=AGREEMENT_CHOICES, default='niedostarczona')
resources_status = models.CharField(max_length=20, choices=RESOURCES_CHOICES, default='niedostarczone')
payment_status = models.CharField(max_length=20, choices=PAYMENT_CHOICES, default='nieoplacone')
message = models.CharField(max_length=200, default='Brak wiadomoĊ›ci')
project_date = models.CharField(max_length=10)
status = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="project")
modifications = models.CharField(max_length=15, default='2')
corrections = models.CharField(max_length=15, default='3')
def __str__(self):
return self.project_name
class Files(models.Model):
name = models.CharField(max_length=50)
upload = models.FileField(upload_to='uploads/', validators=[validate_file_extension])
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="files")
project = models.ForeignKey(Project, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Agreement(models.Model):
name = models.CharField(max_length=50)
upload = models.FileField(upload_to='uploads/', validators=[validate_file_extension])
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="agreement")
project = models.ForeignKey(Project, on_delete=models.CASCADE)
def __str__(self):
return self.name
In app we have an admin and users. Every user has welcome.html page which display every user project, every user agreement and objects from one additional model which isn't important. When user click on project (user goes to project_detail.html), he can see information about project and there are all files from this project.
def panel(request):
if request.user.is_authenticated:
queryset = Project.objects.filter(user=request.user)
queryset2 = Invoice.objects.filter(user=request.user)
queryset3 = Files.objects.filter(user=request.user)
queryset4 = Agreement.objects.filter(user=request.user)
return render(request, 'projects/welcome.html', {'project': queryset, 'invoice': queryset2, 'files': queryset3, 'agreement': queryset4})
else:
return render(request, 'projects/welcome.html')
Admin has user_detail.html page, and there are all files and projects and agreements for this user.
class UserDetailView(DetailView):
model = User
template_name = 'projects/user_detail.html'
context_object_name = 'user'
def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs)
context_data['invoice'] = Invoice.objects.all()
context_data['project'] = Project.objects.all()
context_data['files'] = Files.objects.all()
context_data['agreement'] = Agreement.objects.all()
return context_data
Here is a part of my template.
<div class="block bg-white rounded shadow-2xl text-indigo-800 m-2 p-4 w-full h-full">
<p class="text-3xl text-left mb-2 pb-2 border-b">Pliki</p>
{% for files in files.all %}
{% if files.user == user %}
<div class="flex justify-between mb-2 pb-2 border-b pr-32">
{{files.name}}
<p class="text-xl font-thin text-indigo-800 hover:text-indigo-600 text-left mb-2">{{files.project}}</p>
</div>
{% endif %}
{% endfor %}
</div>
Unfortunetly when I tried (as admin) edit any of file I've got an error:
Exception Type: MultipleObjectsReturned
Exception Value:
get() returned more than one Files -- it returned 8!
So to each project you can assign file and agreement. I have problem with my database. On my local environment everything is fine. On local SQlite3 every file object has unique ID, unfortunetly on my producton, on phpMyAdmin every object has the same ID = 0. Where I can find error? I tried working with my models and database, unfortunetly I can't find what is going on. So now I cannot update file for my user even by my Django Admin Dashboard, because I've got an error.
Hi you have a problem in your template you use files in files.all so when you do files.project you get more than one
{% for files in files.all %}
you need to do :
{% for file in files.all %}
....
{{file.project}}</p>

Django template user in queryset condition

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 %}

Django Forum App, comments don't update on user-side, but can be seen through admin

For reference, here are my models in my Forum app:
class Forum(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('forum-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
forum = models.ForeignKey(Forum, on_delete=models.CASCADE)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
To display the forum posts, I have a CBV 'ForumListView':
class ForumListView(ListView):
model = Forum
template_name = 'forum/forum.html'
context_object_name = 'forum_posts'
ordering = ['-created_at']
From this list, the user can click on any forum and it will lead them to 'forum-detail' with the CBV 'ForumDetailView':
class ForumDetailView(DetailView):
model = Forum
extra_context = {
'comments': Comment.objects.all().order_by('-created_at')}
Here is where I passed in the comments from my Comment model to be shown alongside the post. I think this is the reason why the comments don't update, but I'm not too sure how to fix this.
In the template for forum_detail.html, this is how I display all the comments made:
{% for comment in comments %}
{% if comment.forum == forum %}
<div class="content-section">
<p>{{ comment.description }}</p>
<small>{{ comment.user.username }}, on {{ comment.created_at|date:"F d, Y" }}</small>
</div>
{% endif %}
{% endfor %}
Note that the new comment made will be shown if I re-run
python manage.py runserver
and sometimes the new comment appears after a few minutes of waiting/refreshing the page.
Also, I think function-based views may fix this, however I have coded my entire app with CBVs and would love a fix that doesn't involve re-coding everything!
Any help is greatly appreciated, and can provide more info/code if needed!
Putting it in extra_context like this will cause the queryset to be evaluated when you define the view, and any values that it has at that time will be the only values that the view will get. That's why it is working when you restart the server. So it should be dynamic and fetched every time a new request comes. In that case you need to put it in get_context_data
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.all().order_by('-created_at')}
return context

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 %}

Getting the number of comments from a specific post in django

I have a little problem with a query. I work on a blog website with django. For posts I have the first page where i display all the posts as a list, with their details (title, date posted etc.) and I want to display the number of comments for each post along with title, date posted and tags. I'm not sure how to make that, I need to implement something on the model classes or in view function that renders the page ?
Here are the model classes.
class Post(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=500)
content = models.TextField()
tags = models.CharField(max_length=100)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment_text = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return f'{self.user.username} Comment ' + str(self.id)
and the view function
def blog(request):
context = {
'posts': Post.objects.all(),
'title': 'Blog',
'banner_page_title': 'Blog',
'page_location': 'Home / Blog'
}
return render(request, 'blog/blog.html', context)
Method 1:
You can use this in your template.
{% for post in posts %}
{{ post.comment_set.count }}
{% endfor %}
Method 2:
You can implement a model method like this:
class Post(models.Model):
....
def __str__(self):
return self.title
#property
def comment_count(self):
return self.comment_set.count()
And you can call the model method in your template like this:
{% for post in posts %}
{{ post.comment_count }}
{% endfor %}
Use a reverse lookup in the template and count to get the number of related objects:
{{ post.comment_set.count }}
Something like this?
template.html
...
{% for post in posts %}
<div> Title: {{post.title}} </div>
<div> Date Posted: {{post.date_posted}} </div>
<div> Number of Comments: {{post.comment_set.count}} </div>
{% endfor %}

Categories