Only friends posts are not showing - python

I am building a BlogPost Webapp, AND i am stuck on an Error.
models.py
Post Model
class Post(models.Model):
post_owner = models.ForeignKey(User,default='',null=True,on_delete = models.CASCADE)
post_title = models.CharField(max_length=500,default='')
views.py
def friends_posts(request,user_id):
posts = Post.objects.filter(post_owner=user_id)
profiles = get_object_or_404(Profile,user_id=user_id)
p = request.user.profile
you = p.user
friends = p.friends.all()
context = {'posts':posts,'profiles':profiles,'friends':friends}
return render(request, 'friends_posts.html', context)
urls.py
path('friends_posts/<int:user_id>/',views.friends_posts,name='friends_posts'),
friends_posts.html
{% for user_p in friends %}
{{ user_p.posts.all }}
{% endfor %}
The Problem
friends_posts page is not showing Friends posts.
It is showing all the information of friends but NOT showing the posts of friends.
What i am trying to do
I am trying to show all the posts of friends of request.user.
I don't know what to do.
Any help would be appreciated.
Thank You in Advance.

Try:
{% for user_p in friends %}
{% for post in user_p.user.post_set.all %}
<p>Title: {{post.post_title}}
<br />
{% endfor %}
{% endfor %}

According to your models p.friends.all() return Profile model queryset. When you are trying to display user_p.friends.posts.all all posts from all user friends this fails because of friends is M2M field on Profile model and it doesn't have attribute called posts. If you really need all posts from all friends to be displayed, then you should iterate over frieds first:
{% for friend in user_p.friends.all %}
{% for post in fried.user.post_set.all %}
{{ post }}
{% endfor %}
{% endfor %}

Related

How to get different querysets in Django templates from Django views

In my blog app I want to allow unkown users to see articles, but I also want to allow logged users to see in the same page (somewhere else) their own articles; something like:
YOUR ARTICLES: list (only if user is logged)
ALL ARTICLES: list
Note that I need to show articles based on the user logged in because the url must be this:
path('<int:user_id>/', views.IndexView.as_view(), name='index'),
index.html:
{% if user.is_authenticated %}
Your articles:
<div class="container py-5">
{% if article_list %}
{% for article in article_list %}
<div class="container">
<div class="row">
<div class="col">
{{article.author}}
</div>
<div class="col">
{{article.title}}
</div>
<div class="col">
{{article.pub_date}}
</div>
<a href=" {% url 'blog_app:detail' user_id = user.id %} ">
<div class="col">
Open article
</div>
</a>
</div>
</div>
{% endfor %}
{% else %}
<b>No articles!</b>
{% endif %}
</div>
{% endif %}
views.py:
class IndexView(ListView):
model = Article
template_name = 'blog_app/index.html'
context_object_name = 'article_list'
#return articles of a particular author
def get_queryset(self):
self.article = get_object_or_404(Article, author_id=self.kwargs['user_id'])
return Article.objects.filter(
author = self.article.author
)
My question is: How can I get from IndexView two different querysets? One with all articles and one with articles filtered by author?
Bonus question:
Can I allow unkown users to reach the articles page if the url needs to specify the user id?
After answers, this is one possible correct solution (don't focus on year and month filters, I added them but obviusly aren't related to the solution):
class IndexView(ListView):
model = Article
template_name = 'blog_app/index.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_article_list'] = Article.objects.all()
context['author_article_list'] = Article.objects.filter(
pub_date__year = self.kwargs['year'],
pub_date__month = self.kwargs['month'],
author = self.kwargs['user_id']
).order_by('-pub_date')
return context
In django templates I used these context names to iter articles:
Author articles:
{% if user.is_authenticated %}
{% if author_article_list %}
{% for article in author_article_list %}
...
{% endfor %}
{% endif %}
{% endif %}
All articles:
{% if all_article_list %}
{% for article in all_article_list %}
...
{% endfor %}
{% endif %}
You need to specify:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['all_articles'] = Article.objects.all()
return context
Then you can also use an if statement in th template, to check if the {{all_articles}} exists.
"Can I allow unkown users to reach the articles page if the url needs to specify the user id?"
Unauthenticated users do not have an ID, this will result in an error. If you want users to go to the author of the current article being viewed, wouldn't it be {{article.author.id}}? (Not sure if this is what you want.)
Just use a standard context. Add this metod to you view (changing names, obviously):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['book_list'] = Book.objects.all()
return context
Bonus answer:
Well, anyone can enter every instance of such view. The only thing would be to change manually number in the browser, i.e. anyone can access this link:
http://example.com/1/
But if someone is not authenticated, that link: <a href=" {% url 'blog_app:detail' user_id = user.id %} "> would raise error, but of course cause of {% if user.is_authenticated %} it's not rendered anyway.
You need to set proper permissions to your view.
I think you can also override the get_queryset() method according to different conditions, so:
class IndexView(ListView):
model = Article
template_name = 'blog_app/index.html'
context_object_name = 'article_list'
def get_queryset(self):
qs=super().get_queryset()
if self.request.user.is_authenticated:
article=get_object_or_404(Article,author_id=self.kwargs['user_id'])
return qs.filter(author=article.author) #filtered queryset
else:
return qs #default queryset

Render relationship model in template

I am trying to render a model with a relationship but I am not able to do so.
class LogBook(models.Model):
name = models.CharField(max_length=50, verbose_name="Nom du registre de maintenance")
members = models.ManyToManyField(User)
class LogMessages(models.Model):
logbook = models.ForeignKey(LogBook)
message = models.CharField(max_length=200, verbose_name="Détail du problème")
class LogDone(models.Model):
logmessage = models.ForeignKey(LogMessages)
message = models.CharField(max_length=200)
My view:
log = get_object_or_404(LogBook, pk=log_id)
logmessages = LogMessages.objects.filter(logbook=log_id)
My template
{% for logmessage in logmessages.all %}
{{logmessage.logdone.message}}
{{% endfor %}}
But the logdone object is not showing, any idea ?
Since your LogMessage model has a foreign key to log done, it's not a One to One relation and you have to access the related LogDone objects using the _set notation. There's also a slight typo, I believe. It should logmessages and not logmessages.all
{% for logmessage in logmessages %}
{% for done in logmessage.logdone_set.all %}
{{ done.message }}
{% endfor %}
{% endfor %}
I forgot that I added a related_name equal to "logdones" so I did the following :
{% for logmessage in logmessages %}
{% for done in logmessage.logdones.all %}
{{ done.message }}
{% endfor %}
{% endfor %}
And now it is working, thanks to #Vishal

Django: How to query based on a ForiegnKey and display related data for each ForeignKey?

I am brand new to Django and to programming and I'm trying to make a page that will display the Workout with all the associated Exercises listed under each work out.
For example:
Chest
Chest Press
Incline Press
Flat Flyes
Shoulders
Shoudler Press
Arnold Press
Back/Legs
Wide Grip Pull Up
Neutral Grip Pull Up
Bent Over Row
Here is my code:
models.py
class Workout(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
class Exercise(models.Model):
workout = models.ForeignKey(Workout, on_delete=models.CASCADE, related_name='workouts')
title = models.CharField(max_length=100)
weight = models.DecimalField(decimal_places=0, max_digits=10000)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
views.py
def home(request):
context = {
'workouts': Workout.objects.all()
}
return render(request, 'workouts/home.html', context)
def workout(request):
context = {
'workouts': Workout.objects.all(),
'exercises': Exercise.objects.all()
}
return render(request, 'workouts/workout.html', context)
workout.html
{% extends 'workouts/base.html' %}
{% block content %}
{% for workout in workouts %}
<h1>{{ workout.title }}</h1>
{% for exercise in exercises %}
<h3>{{ exercise.title }}</h3>
<p>{{ exercise.weight }}</p>
{% endfor %}
{% endfor %}
{% endblock content %}
In my view.py I have it set to Exercise.objects.all() which just displays all of the exercises under each Workout title, I can figure out how to get only the exercises that are associated with the Workout.
Like I said I am brand new to all of this and I'd appreciate any help! Thanks!
You can define property to Workout model to retrieve exercises related to the instance. Just add a new method with property decorator to retrieve Exercises related with that Workout.
class Workout(models.Model):
title = models.CharField(max_length=100)
def __str__(self):
return self.title
#property
def exercises(self):
return self.workouts.all()
Then you can use exercises in your html file like:
{% extends 'workouts/base.html' %}
{% block content %}
{% for workout in workouts %}
<h1>{{ workout.title }}</h1>
{% for exercise in workout.exercises %}
<h3>{{ exercise.title }}</h3>
<p>{{ exercise.weight }}</p>
{% endfor %}
{% endfor %}
{% endblock content %}
Another approach may be grouping Exercises with workouts in your view and pass them to template. For more information, you can check regroup
When you define FK relationship between two models, Django adds a manager to target model to query related objects. When you use this manager, you basicly run a query something like Exercise.objects.filter(workout_id=workout_instance_id). This is a queryset, so you can join with features of queryset features like filter, first and extra. You can use a plenty of queryset features on related objects. This is basicly the power of Django ORM mechanism. You can find a bit more in documentation if you need to.

How do I iterate through a ManyToManyField in django-jinja project?

I am trying to create a blog app which has posts and each posts have title, date, link and tags.
This is my models.py
# models.py
from django.db import models
class Tag(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class Post(models.Model):
title = models.CharField(max_length=300)
date = models.DateTimeField()
link = models.URLField()
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.title
#property
def tags_name(self):
return [x.name for x in self.tags]
class Meta:
ordering = ('date',)
This is my views.py
# views.py
from django.conf.urls import url, include
from django.views.generic import ListView
from blog.models import Post
urlpatterns = [
url(r'^$', ListView.as_view(queryset=Post.objects.all().order_by("-date"), template_name="blog/blog_list.html")),
]
This is my blog_list.html
<!-- blog_list.html -->
{% extends "mysite/layout.html" %}
{% block content %}
<h1>my blog posts</h1>
<ul>
{% for post in object_list %}
<li><span class="title">{{ post.title }}</span></li>
<p>{{ post.date|date:"d-m-Y" }}</p>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
<h4 id="sidenav">tags</h4>
{% for post in object_list %}
<ul>
<!-- I want to show the tags here -->
</ul>
{% endfor %}
{% endblock %}
In the blog_list.html, I am showing all the post details and on the sidebar, I want to show all the tags present from all the blog posts available. Since post.tags is ManyToManyField, how can I iterate through it?
You want to use .all in the template to get all the elements in the relationship:
{% for tag in post.tags.all %}
{{ tag }}
{% endfor %}
Thanks to #hansTheFranz for correcting my bracket issue.
Regarding not repeating tags, this would be very difficult with the current context. You might want to look into instead getting the posts in your View and extracting the tags there, where you have more freedom to check for duplicates. Something like this:
def tags(request):
posts = Post.objects.all()
tag_list = []
for post in posts:
tags = post.tags.all()
for tag in tag:
if not (tag in tag_list):
tag_list.append(tag)
context_dict = { "tags": tag_list, "posts": posts }
return render(request, 'blog/blog_list.html', context_dict)
urlpatterns = [
url(r'^$', tags, name="tags"),
]
And then change your template to be more like:
{% block sidebar %}
<h4 id="sidenav">tags</h4>
<ul>
{% for tag in tags %}
<li>{{ tag }}</li>
{% endfor %}
</ul>
{% endblock %}
Additionally, instead of referencing object_list you can now access the list of posts by referencing posts, because we have defined the list of posts as such in our context dictionary, which is being passed to the template.
I'm afraid I have not tested this and it may not be very efficient, but roughly speaking it should work. A lecturer at my university wrote this book: http://www.tangowithdjango.com/book17/, which encourages more of a style of writing views as I have done: separate from the URLs. If anything I've done seems unclear or contrary, you may want to have a look at the book and see if anything there makes more sense.

Django: How to display author of query of posts?

I'm trying to make individual pages for each author showing their name and posts. I can't seem to get the username displayed.
views.py
class UserProfileView(generic.ListView):
template_name = 'howl/user-profile.html'
context_object_name = 'user_howls'
def get_queryset(self):
author = self.request.user
u = User.objects.get(username=author)
return Howl.objects.filter(author=u)
models.py
class Howl(models.Model):
author = models.ForeignKey(User, null=True)
content = models.CharField(max_length=150)
Here is where I'm stuck.
user-profile.html
{% extends 'howl/base.html' %}
{% block content %}
<h1>User: {{user_howl.author}}</h1>
{% for user_howl in user_howls %}
<ul>
<li>{{user_howl.content}}</li>
</ul>
{% endfor %}
{% endblock %}
The content is displayed just fine, but the heading just says "User: ", how do I give it a context without using a for loop?
I've tried:
{% for author in user_howls.author %}
<h1>User: {{author}}</h1>
{% endfor %}
and
{% if user_howls.author %}
<h1>User: {{user_howl.author}}</h1>
{% endif %}
Still the same outcome, displaying "User: "
user_howls is a queryset so it won't have an author attribute, you need to get the author of the iterated object
{% for howl in user_howls %}
<h1>User: {{ howl.author}}</h1>
{% endfor %}
More to the point though, it doesn't make sense to start from a Howl list, when you are just returning the results for the user_profile, nor does it make sense to use a ListView. so instead, start from the user and then look up its howls
user_obj.howl_set.all()
Since your queryset is based on the posts belonging to the current user, you can shortcut all of this and just show the user directly:
User: {{ user }}

Categories

Resources