I have 3 models. User, Post and Proof. Posts are added by a User to the website. When other users do what the post says. They upload 2 images to say they did the task. The template will show the 2 images they uploaded. I know I am making some mistake in the template calling the images. Does anyone know the correct way to use related_name to call for the images. FYI I am new to python and Django I am sorry if this question is too trivial or has wrong logic
Proof/models.py
User = get_user_model()
class Proof(models.Model):
user = models.ForeignKey(User, related_name='proofmade')
post = models.ForeignKey(Post, related_name='postproofmade')
made_at = models.DateTimeField(auto_now=True)
image_of_task= models.ImageField()
proof_you_made_it = models.ImageField()
suggestions = models.TextField(max_length=1000)
def __str__(self):
return self.post.title
Templates
{% for user in post.made.all %}
<div class="container">
<img src="{{ user.proofmade.image_of_task.url }}" height="150px"/>
<img src="{{ user.proofmade.proof_you_made_it.url }}" height="150px"/>
</div>
{% endfor %}
Post/views.py
class PostDetail(SelectRelatedMixin, DetailView):
model = Post
select_related = ('user', 'group')
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user__username__iexact=self.kwargs.get('username'))
A ForeignKey is a one-to-many relationship; the related_name therefore gives access to a queryset corresponding to many items. You need to iterate over that queryset to get the individual items, in this case the Proof instances - just as you do with the users for each post. So, something like:
{% for user in post.made.all %}
{% for proof in user.proofmade.all %}
<div class="container">
<img src="{{ proof.image_of_task.url }}" height="150px"/>
<img src="{{ proof.proof_you_made_it.url }}" height="150px"/>
</div>
{% endfor %}
{% endfor %}
Related
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
I'm building an instagram-ish clone in Django. I have the basic functionality working, where a user can post an image, and this is displayed on the main page. I would like to make a 'user page' that only displays posts from a user. For example, example.com/foobar would only display posts from the user 'foobar'.
I believe i have the urls.py and template working correctly, but I can not figure out how to only iterate through items and pull out ones of a specific user. I realize this should be a queryset of some kind, but other than that I'm stumped. Should this be its own class, or could I extend the existing PostList class to pull out a single author's posts?
post_detail.html - gets all the images stored in the database, this works fine.
{% for post in object_list %}
<td><img src="{{ post.image.url }}" width="300"></td>
{% if forloop.counter|modulo:4 %}
</tr><tr>
{% endif %}
{% endfor %}
profile.html - shows all posts from a user (as in example.com/foobar)
<table>
<tr>
{% for post in object_list %}
<td><img src="{{ post.image.url }}" width="300"></td>
{% if forloop.counter|modulo:4 %}
</tr><tr>
{% endif %}
{% endfor %}
</tr>
</table>
urls.py - I believe this works correctly.
urlpatterns = [
path('admin/', admin.site.urls),
path('', PostList.as_view(), name='list'),
path('<str:username>/', Profile.as_view(), name='user_profile'),
views.py:
from posts.models import Post
class PostList(ListView):
ordering = ['-created']
paginate_by = 12
model = Post
class Profile(ListView):
template_name = 'posts/profile.html'
UserName = self.kwargs.get("username")
queryset = PostList.queryset
.filter(author = UserName)
return queryset
models.py:
class Post(models.Model):
image = models.ImageField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
You can override get_queryset():
class ProfileView(ListView):
template_name = 'posts/profile.html'
model = Post
def get_queryset(self, **kwargs):
return super().get_queryset(**kwargs).filter(
author__username=self.kwargs['username']
)
Note: In Django, class-based views (CBV) often have a …View suffix, to avoid a clash with the model names.
Therefore you might consider renaming the view class to ProfileView, instead of Profile.
I am creating a blog application in django where i encountered this unusual issue. I am not being able to filter and display blog posts category wise. Please find my code below. Thanks in advance. I have spent two full days trying to figure this out and still
got nothing.
MODELS.py
This is the model which i have created for a blog post.
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
post_date = models.DateField(auto_now_add=True)
category = models.CharField(max_length=255, default="uncategorized")
def __str__(self):
return self.title + ' | ' + str(self.author)
def get_absolute_url(self):
#return reverse('article-detail', args=(str(self.id)))
return reverse('homepage')
VIEWS.py
This is the view that i have created for a category wise blog posts.
def CategoryView(request,cat):
category_posts = Post.objects.filter(category=cat)
return render(request,'categories.html',{'category_posts':category_posts})
URLS.py
The urls.py file.
path('category/<str:cat>/', CategoryView, name = 'category'),
CATEGORIES.html
This will be the final display page where the category_posts is displayed as an empty queryset. The for loop is unable to run because category_posts is an empty queryset. Single word categories are also empty(to rule out a slugify issue)
{% extends 'base.html' %}
{% load static %}
{% block content %}
<ul>
{% for post in category_posts %}
<li>
<div class="category">
{{ post.category }}
</div>
<a href="{% url 'article-detail' post.pk %}">
<h3><b>{{ post.title }} </b></h3>
<p class=card-date>
<big>{{ post.author }}</big>
<small>-{{ post.post_date }}</small>
</p>
<p class="excerpt-text">{{ post.body | slice:"100" |safe}}</p>
<div class="article-thumbnail">
<img src="{% static "images/bg.png" %}" alt="article-thumbnail" >
</div>
<div class="overlay">
</a>
</li>
{% endfor %}
{% endblock %}
I am having an issue while implementing greater than operator in my template. I have a post in homepage which users can like and I have my friends' profile images displayed beside like count who like the post. Now if 10 friends like my post, i want only five of my friends' profile images to be displayed, and there will be a "+" at the end of displayed images. The "+" signifies that there are more friends who like my post. I tried this but it doesn't work:
Model:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
profile_pic = models.ImageField(upload_to='ProfilePicture/', default="ProfilePicture/user-img.png", blank=True)
friends = models.ManyToManyField('Profile', related_name="my_friends",blank=True)
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
likes = models.ManyToManyField('Profile', related_name='image_likes', blank=True)
View:
def home(request):
#all post in homepage
posts = Post.objects.filter(poster_profile=request.user)
#Show friend who liked Post
friends_like_img = request.user.profile.friends.all().order_by('-id')
context = {'posts':posts,'friends_img':friends_img}
return render(request, 'template.html', context)
Template:
{% for post in posts %}
{% for img in friends_img %}
{% if img in post.likes.all > 20 %}
<img src="{{ img.profile_pic.url }}" height="25" width="25" alt="profile_pic">
{% else %}
<img src="{{ img.profile_pic.url }}" height="25" width="25" alt="profile_pic"> +
{% endif %}
{% endfor %}
{% endfor %}
Your code is a bit of a mess, but here are some pointers:
You only ever want five images, so take care of that in the view by slicing the queryset:
friends_like_img = request.user.profile.friends.all().order_by('-id')[:5]
Your template syntax is all off, you could do with reading the docs and getting used to some examples. In the context, you're using friends_img, not friends_like_img - the context is what the template cares about. Now, since we only ever have five images, we can do this in the template:
{% for img in friends_img %}
<img src="{{ img.profile_pic.url }}" ...>
{% endfor %}
{% if post.likes.count > 5 %}
+
{% endif %}
I am trying to create a template with Django that loops through Posts and for each Post loops through all Pictures.
I already looked at some answers to other Questions but I can not find my error.
Models:
class Post(models.Model):
Post_Title = models.CharField(max_length=200)
Post_pub_date = models.DateField('date published')
Post_Author = models.CharField(max_length=100)
Post_Text = models.TextField()
def __str__(self):
return self.Post_Title
class Picture(models.Model):
Post = models.ForeignKey(Post, on_delete=models.CASCADE)
Picture = models.ImageField()
Picture_Name = models.CharField(max_length=100, null=True, blank=True)
def __str__(self):
return self.Picture_Name
Views:
class PostView(generic.ListView):
template_name = 'myblog/index.html'
context_object_name = 'Post_List'
def get_queryset(self):
"""
Returns Posts
"""
return Post.objects.order_by('-Post_pub_date')
Template:
{% for Post in Post_List %}
<h1 class="mb-4">{{Post.Post_Title}}</h1>
<span class="category">{{Post.Post_Author}}</span>
<span class="mr-2">{{Post.Post_pub_date}}</span>
<div class="post-content-body"><p>{{Post.Post_Text}}</p>
{% for Picture in Post.Picture_set.all %}
<div class="col-md-12 mb-4 element-animate">
<h2>{{Picture.Picture_Name}}</h2>
<img class="col-md-12 mb-4 element-animate" src="{{ MEDIA_URL }}{Picture.Picture}}">
</div>
{% endfor %}
</div>
{% endfor %}
The Post_Title, Post_Author, Post_pub_date and Post_Text are displayed fine. Just the nested For loop is not producing any Picture_Name or Picture as if the Picture_set.all is empty.
As mentioned above I tried to find my error in different Posts like this but could not find it.
Thanks for your help.
Following relationship backward, you need to write related model name from a small letter, even if model name starts from large letter:
{% for Picture in Post.picture_set.all %}
This is how it works in Django shell and i suppose in templates it is the same.
The issue isn't the nested for loop it's the view.It only returns a query for your Post, you don't pass any Photos to your template.