Django: Get all objects from a specific user - python

I have a problem when trying to display all the Announce objects from a user.
My problem is : Consider that I am logged in as a user with an id=1. When I go to /users/1/ it displays all my posts. But the problem is when I want to display all the posts from another user with id=2 by going to /users/2/, it still display all my Announce objects, and not the user with the id=2.
models.py
class Announce(models.Model):
owner = models.ForeignKey('auth.User', related_name='announces')
created_date = models.DateTimeField(auto_now_add=True)
body = models.TextField(max_length=1000)
views.py
class UserAnnouncesList(ListView):
model = Announce
template_name = 'myApp/user_announces_list.html'
context_object_name = 'all_announces_by_user'
def get_queryset(self):
return Announce.objects.filter(owner=self.request.user)
urls.py
urlpatterns = [
url(r'users/(?P<pk>[0-9]+)/$', views.UserAnnouncesList.as_view(), name='user_announces_list'),]
user_announces_list.html
{% extends "account/base.html" %}
{% block content %}
{% for announce in all_announces_by_user %}
<h1>{{announce.user.username}}</h1>
<p>{{announce.body}}</p>
{% endfor %}
{% endblock content %}
Do I have to use some kind of like : Announce.objects.get(pk=???) ?
I appreciate your help!

The request.user is the user that is logged in. You need to use the pk that is passed as url. This is stored in the kwargs dictionary of the listview:
class UserAnnouncesList(ListView):
model = Announce
template_name = 'myApp/user_announces_list.html'
context_object_name = 'all_announces_by_user'
def get_queryset(self):
return Announce.objects.filter(owner=self.kwargs['pk'])

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

Accessing profile info through user model Django

Hello I am a beginner with the django python framework. I need to display a user's image and bio on a file named user_posts.html in my blog app. I have it where I can access the user's image and bio by looping over the posts for the user. However, I need it so it only displays the bio and the image once. I have a separate profile.html in a users app. In that file, I can do just src="{{ user.profile.image.url }}" and {{ user.profile.bio }} to access the users information but that does not seem to work in my user_posts.html because of the structure of my project. I can't figure out how to tell the for loop to just go over once to access the users information.
users_post.html
{% extends "blog/base.html" %}
{% block content %}
<hr size="30">
<div class="row">
<div class="column left">
{% for post in posts %}
<img style= "float:left" src="{{ post.author.profile.image.url}}" width="125" height="125">
<h5 style="text-align: left;">{{ post.author.profile.bio }}</h5>
{% endfor %}
</div>
views.py
class UserPostListView(ListView):
model = Post
template_name = 'blog/user_posts.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
models.py
from django.db import models
from django.contrib.auth.models import User
from PIL import Image
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
bio = models.TextField(default='enter bio text here')
def __str__(self):
return f'{self.user.username} Profile'
This is what the problem looks like
Any help is appreciated
Here's how you might do it using get_context_data().
class UserPostListView(ListView):
model = Post
template_name = 'blog/user_posts.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
def get_context_data(self, **kwargs):
"""
Add User Profile to the template context.
"""
context = super().get_context_data(**kwargs)
profile_user = get_object_or_404(User, username=self.kwargs.get('username'))
context['profile_user'] = profile_user
return context
You'd then use {{ profile_user.profile.bio }} in your template instead of posts for the user profile info.
This is probably better than getting the first object in posts and getting the user profile information from that object in the case the user has no posts yet (but has a bio).
Note, we're fetching the User object both in get_queryset and in get_context_data so this is not super efficient. There are ways around this but I'll leave it to future editors or you to optimize :)
Probably Not Recommended, But Answering Question
Since you initially were just trying to get the first element, here's how I'd do it.
# Method 1
{% with first_post=posts|first %}
{{ posts.user.profile.bio }}
{{ posts.user.profile.image.url }}
{% endwith %}
# Method 2
{{ posts.0.profile.bio }}
{{ posts.0.profile.image.url }}

Django/Python - How to show "task" ONLY if the user is author or responsable of "task"

I'm new to python and django and I need some help, please.
What I'm trying to do is to only show a certain "task" if the user is responsable or author of the "task" in question.
I was trying to do that with a if statement in html template:
{% for task in task_list %}
<h2>title - {{task.title}}</h2>
{% endfor %}
{% endif %}
But does not return what I expected since:
{% for task in task_list %}
<h2>author --- {{task.author}}</h2>
<h2>responsable --- {{task.responsable}}</h2>
{% endfor %}
Returns me the same user... I think the problem is that when I refer user.username it goes to the db and returns a query, and when I user {{task.blablabla}} its a string, I'm right? How I can fix that?
models.py:
title = models.CharField(max_length=50)
content = models.TextField(blank=True)
date_created = models.DateTimeField(auto_now_add=True)
due_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE, default=User)
responsable = models.ForeignKey(User, on_delete=models.CASCADE, related_name="author", default=User)
STATUS_CHOICES = [('D', 'Done'),('P','Doing'),('N','Not done')]
Status = models.CharField(max_length=1,choices=STATUS_CHOICES, default='N')
IMPORTANCE_CHOICES = [('H', 'High'),('M','Medium'),('L','Low')]
importance = models.CharField(max_length=1,choices=IMPORTANCE_CHOICES, default='M')
DEPARTAMENT_CHOICES = [('D', 'Dev'),('M','Marketing'),('H','Human Resources'),('L','Legal'),('F','Financial'),('O','Others')]
departament = models.CharField(max_length=1,choices=DEPARTAMENT_CHOICES, default='M')
def __str__(self):
return self.title
views.py
def dashboard_taskapp(request):
task = Task.objects.all()
context = {
"task_list": task,
}
return render(request, "task_app/task_dashboard.html", context)
Thanks in advance and kind regards,
So the cleaner and safer solution here is to use Class Based Views (CBV). There is a generic CBV specially created for displaying lists of objects - ListView. To achieve exactly what you did the code would present as follows:
from django.views.generic.list import ListView
class DashboardTaskAppView(ListView):
template_name = "task_app/task_dashboard.html"
# we override this method to
# get the queryset of objects we want to display
# rest of the work will be taken care of by "djnago"
def get_queryset(self):
usr = request.user
return Task.objects.filter(Q(author=usr) | Q(responsible=usr) | Q(public=True))
Now to get the wanted result in template we simply invoke it like this
{% for task in object_list %}
<h2>title - {{task.title}}</h2>
{% endfor %}
when using CBV remember to user path in urls like this
path('your_url/', DashboardTaskAppView.as_view(), name='your_name'),
Please read the docs to discover full Django generic views potential it really speeds up your job and reformatting it later on takes seconds instead of hours.
I hope this is what you want to achieve. Firstly, modify task queryset so it only shows your chosen person's tasks. (You need to import Q for OR contidion)
from django.db.models import Q
def dashboard_taskapp(request):
usr = request.user
task = Task.objects.filter(Q(author=usr) | Q(responsible=usr))
context = {
"task_list": task,
}
return render(request, "task_app/task_dashboard.html", context)
Then there is no need to limit your template so in the end it will look like this
{% for task in task_list %}
<h2>title - {{task.title}}</h2>
... whatever you wanna show here ...
{% endfor %}

Filter and count QuerySet items with a variable

I am new to Django and am working on a very basic social media site as a practice project.
Right now, I am trying to figure out how to filter a QuerySet based on a variable and counting how many items in the QuerySet match the filter.
To demonstrate what I am trying to do, let's say I am looping through all the visible posts (like a Facebook post or something similar), and I am wanting to display the number of comments each post has.
This is how I would go about that:
{% post in all_posts %}
<h1> There are currently {{ HOW DO I FILTER AND COUNT? }} comments on this post</h1>
{% endfor %}
This is what the relevant section of my views.py file looks like:
def index(request):
all_posts = Posts.objects.order_by('-date_published')
all_comments = Comments.objects.order_by('-date_published')
context = {'all_posts': all_posts,
'all_comments': all_comments
}
return render(request, 'social/index.html', context)
The comments link to the posts through the postID variable. So, I know this doesn't work, but ideally I would like to replace my HOW DO I FILTER AND COUNT? part of the template with something like:
{{ all_comments.filter(postID=post).count }}
Is there an easy way to do this in my views.py or in the template itself? The main problem I have is that I do not know how I can pass the post variable in the template to some function that return the count I am looking for.
UPDATE:
Below are my Posts and Comments models:
class Posts(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
content = models.TextField()
date_published = models.DateTimeField('date posted')
class Comments(models.Model):
postID = models.ForeignKey(Posts, on_delete=models.CASCADE)
commenter = models.CharField(max_length=200)
email = models.EmailField(max_length=200)
content = models.TextField()
date_published = models.DateTimeField('date posted')
You can annotate the Posts model objects with the number of Comments with:
def index(request):
all_posts = Posts.objects.annotate(
ncomments=Count('comments')
).order_by('-date_published')
all_comments = Comments.objects.order_by('-date_published')
context = {
'all_posts': all_posts,
'all_comments': all_comments
}
return render(request, 'social/index.html', context)
In the template you can then render this with:
{% post in all_posts %}
<h1> There are currently {{ post.ncomments }} comments on this post</h1>
{% endfor %}
Note: normally a Django model is given a singular name, so Post instead of Posts.

Listing ForeignKey associated instances within template (queryset within a queryset)

I have a site which catalogs local hikes, and users can log that they have been on the hike. I have a search page which contains the hikes, and one of the fields I'm trying to display is a list of all the people who have been on the hike. I've got this figured out within the individual detail page of the hike, but can't figure out how to create a new queryset within the queryset which is printing the hikes, in order to display this info on a search page.
Here's some code:
models.py:
class Hike(models.Model):
name = models.CharField(max_length=255, unique=True)
slug = models.SlugField(unique=True)
...
class UserLog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
hike = models.ForeignKey(Hike, on_delete=models.CASCADE)
views.py:
def hike_list(request):
qs = Hike.objects.all()
... some other filters here
?-->users = UserLog.objects.filter(id=qs.id)
template:
{% for qs in qs %}
{{ hike.name }}{{ hike.other_details_and_stuff }}
?----> {% for users in hikes %}{{ user.name }}{% endfor %}
{% endfor %}
Here's the working code within the individual hike's detail page:
views.py:
def hike_detail (request, slug)
users = UserLog.objects.filter(hike__slug=slug)
How do I call on the slug from each individual item in the queryset, then run a queryset on that?
The easiest is to add a ManyToManyField to Hike:
class Hike(models.Model):
...
users = models.ManyToManyField(User, through='app.UserLog')
If you have no extra fields in UserLog, you can even remove the UserLog model and the through parameter alltogether. In the template you can do:
{% for hike in qs %}
{{ hike.name }}{{ hike.other_details_and_stuff }}
{% for user in hike.users.all %}{{ user.name }}{% endfor %}
{% endfor %}
In order avoid too many queries, you should prefetch the users in the Hike query in the view:
qs = Hike.objects.all().prefetch_related('users')
Without the ManyToManyField, you could add a property and user the same template, but the prefetch clause could not be used that easily:
class Hike(models.Model):
...
#property
def users(self):
return User.objects.filter(userlog__hike=self)

Categories