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 %}
Related
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
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 %}
I'm trying to print all the followers the person that owns a profile page has. Here is my following table that shows the following relationship:
class Following(models.Model):
target = models.ForeignKey('User', related_name='followers', on_delete=models.CASCADE, null=True)
follower = models.ForeignKey('User', related_name='targets', on_delete=models.CASCADE, null=True)
def __str__(self):
return '{} is followed by {}'.format(self.target, self.follower)
I am also using Django's auth User model.
views.py
class FollowersView(DetailView):
model = User
slug_field = 'username'
template_name = 'profile/followers.html'
def get_profile_followers(user):
return user.followers.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["follower_list"] = get_profile_followers(self.object) # self.object is user profile
return context
In the template, I am doing this:
{% for follower in follower_list %}
<h1>{{ follower }}</h1>
{% endfor %}
But, I get this instead: Chris is followed by John. This is correct, Chris is followed by John, however, I want to display only John's user and John's attributes like avatar, follower_count, etc. that are fields in the User table. How can I do this?
Based on text, I suppose you get Following instance as {{ follower }} value. So you can just use . syntax to get follower attributes like this:
{% for follower in follower_list %}
{{ follower.follower.username }}
{{ follower.follower.avatar }}
{% endfor %}
Note that accessing related objects through . required additional DB query. So you may use select_related in you queryset for optimization:
def get_profile_followers(user):
return user.followers.all().select_related('follower')
I am trying to build a simple web page that queries three tables. There is a Company table that has a one-to-many relationship with a Position table, as well as a one-to-many relationship with a Project table.
The goal is to have the page display a given company once, along with all positions and and projects associated with said company. Then, move on to display the next company, any positions held there and projects completed.
Below is the closest I've come to getting this right. But, the obvious problem is that if there is more than one project associated with a given company, you'll see that company listed more than once.
I'm new to Django, so in the interest of learning, I wanted to beat my own head sufficiently hard before asking for help; but I could really use some fresh ideas at this point.
Also: I can see how a nested for loop might work here, but I'm just not clear on how the mechanics of that would work with the query, and then within the template.
Models:
from django.db import models
class Company(models.Model):
company_id = models.AutoField(primary_key=True)
company_name = models.CharField(max_length=20)
company_logo = models.ImageField(upload_to='images/')
def __str__(self):
return self.company_name
class Position(models.Model):
position_id = models.AutoField(primary_key=True)
position_title = models.CharField(max_length=55)
company_id = models.ForeignKey('professional.Company',
on_delete=models.CASCADE,
blank=True,
null=True)
begin_date = models.DateField()
end_date = models.DateField()
def __str__(self):
return self.position_title
class Project(models.Model):
project_id = models.AutoField(primary_key=True)
project_name = models.CharField(max_length=55)
company_id = models.ForeignKey('professional.Company',
on_delete=models.CASCADE,
blank=True,
null=True)
project_description = models.CharField(max_length=500)
project_image = models.ImageField(upload_to='images/')
def __str__(self):
return self.project_name
View:
from django.views.generic import TemplateView, ListView
from professional.models import Company
class ProfessionalHome(TemplateView):
template_name = 'professional/professional_home.html'
class TechnologyListView(ListView):
template_name = 'professional/__technology.html'
context_object_name = 'technology_list'
def get_queryset(self):
return Company.objects.values('company_name','position__position_title', 'project__project_name')
HTML and template:
{% for job in technology_list %}
<h1>{{job.company_name}}</h1>
<h1>Position: {{job.position__position_title}}</h1>
<h1>project: {{job.project__project_name}}</h1>
{% endfor %}
Instead of values in get_queryset method, you can return the actual queryset and then iterate over it to build your view.
def get_queryset(self):
return Company.objects.all()
Then in your template:
{% for job in technology_list %}
<h1>{{job.company_name}}</h1>
{% for position in job.position_set.all() %}
<h1>Position: {{position.position_title}}</h1>
{% endfor %}
{% for project in job.position_set.all() %}
<h1>project: {{project.project_name}}</h1>
{% endfor %}
{% endfor %}
If you want to iterate over companies, then you should use the Company model as the basis for your view, not Technology. Also, you should avoid values and values_list unless you know you have a good reason, which you don't here. You can use prefetch_related() to reduce the number of reverse queries. So:
class TechnologyListView(ListView):
def get_queryset(self):
return Company.objects.all.prefetch_related('project','position')
...
{% for company in company_list %}
<h1>{{company.company_name}}</h1>
{% for position in company.position_set.all %}
<h1>Position: {{ position.position_title }}</h1>
{% endfor %}
{% for project in company.project_set.all %}
<h1>project: {{ project.project_name }}</h1>
{% endfor %}
{% endfor %}
(Note, you should avoid giving your ForeignKey fields names ending in "_id". The Django field refers to the entire Company, not the ID; the fields should be called just company. The underlying database will be suffixed with _id anyway. Also, you don't need to use model_name prefixes on all your fields; it will be obvious from the object they are accessed on.)
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'])