How to pass multiple app's models in one views? [django] - django

Hei there,
I'm having difficulties passing two app models in one views. I have 2 apps :
Post
Author
What I expect is, I want to display Author's avatar in the Post views, so I can include it in posts loop.
Author models.py :
class Author(models.Model):
avatar = models.ImageField(upload_to='images/%Y/%m/%d', verbose_name=u'Author Avatar', validators=[validate_image], blank=True, null=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
location = models.CharField(max_length=30, blank=True)
...
Post views.py :
from app_author.models import Author
class PostList(ListView):
model = Post
template_name = 'app_blog/blog_homepage.html'
context_object_name = 'post_list'
paginate_by = 9
def get_context_data(self, **kwargs):
context = super(PostList, self).get_context_data(**kwargs)
context['post_hot'] = Post.objects.filter(misc_hot_post = True).order_by('-misc_created')[:1]
context['post_list'] = Post.objects.filter(misc_published = True).order_by('-misc_created')
context['author'] = Author.objects.all() # No need this line
return context
When I called something like this in templates, it doesn't work. The {{ author.avatar }} not showing :
{% for post in post_list %}
<ul>
<li>{{ post.title }}</li> # This is works just fine
<li>{{ author.avatar }}</li> # This does not work at all
<ul>
{% endfor %}
My Posts app urls.py :
from . import views
from django.conf import settings
from app_blog.views import PostList, PostDetailView
from django.conf.urls import include, url
from django.conf.urls.static import static
urlpatterns = [
url(r'^$', views.PostList.as_view(), name='post-list'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Any help would be greatly appreciated!
Thank you in advance!
Regards
--- UPDATE ---
Here is my Post models.py
import datetime
from django import forms
from django.db import models
from django.conf import settings
from autoslug import AutoSlugField
from django.forms import ModelForm
from app_author.models import Author
from taggit.managers import TaggableManager
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.template.defaultfilters import slugify
# CUSTOM FILE SIZE VALIDATOR
def validate_image(fieldfile_obj):
filesize = fieldfile_obj.file.size
megabyte_limit = 5
if filesize > megabyte_limit*1024*1024:
raise ValidationError("Max file size is %sMB" % str(megabyte_limit))
class Category(models.Model):
# PRIMARY DATA
id = models.AutoField(primary_key=True)
category_name = models.CharField(max_length=200, verbose_name=u'Category Name', blank=False, null=False)
def __str__(self):
return str(self.category_name)
class Post(models.Model):
# PRIMARY DATA
post_image = models.ImageField(upload_to='images/%Y/%m/%d', verbose_name=u'Post Featured Image', validators=[validate_image], blank=True, null=True)
post_author = models.ForeignKey(Author, related_name="user_posts", null=True, blank=True)
post_title = models.CharField(max_length=200, verbose_name=u'Post Title', blank=False, null=False)
post_sub_title = models.CharField(max_length=200, verbose_name=u'Post Sub-Title', blank=False, null=False)
post_category = models.ForeignKey('Category', verbose_name=u'Post Category', on_delete=models.CASCADE)
post_content = models.TextField()
post_tags = TaggableManager()
slug = AutoSlugField(populate_from='post_title')
# MISC
misc_created = models.DateTimeField(default=datetime.datetime.now, null=True, blank=True)
misc_modified = models.DateTimeField(default=datetime.datetime.now, null=True, blank=True)
misc_hot_post = models.BooleanField(default=False)
misc_published = models.BooleanField(default=False)
def __str__(self):
return str(self.post_title)
#models.permalink
def get_absolute_url(self):
return 'app_blog:post', (self.slug,)
Thank you
--- UPDATE 2 ---
SOLVED
#Gagik & #Daniel answer sloved my problem. I just have to use this tag instead :
{{ post.post_author.avatar }}
I did not change any code above. Except I dont need this line :
context['author'] = Author.objects.all()
Thank you

You need to related Author and Post models to each other by ForeignKey file, like:
class Post (models.Model):
author = models.ForeignKey(Author)
Then when you can access to your avatar through post.author.
Updated:
Updating answer after question updated.
I think you need to update your template as follows. use post.post_author.avatar to access the avatar you need:
{% for post in post_list %}
<ul>
<li>{{ post.title }}</li> # This is works just fine
<li>{{ post.post_author.avatar }}</li> # This does not work at all
<ul>
{% endfor %}
Considering this please note that you don't need to search Author's desperately in your view. So you don't need to have following line:
context['author'] = Author.objects.all() # This is where I expect the magic is
Because when you have ForeignKey then you already have access to Author through post object.

Gagik and JF's answers should have been enough for you to work out what to do. Your foreign key is called "post_author", so that's what you should use in the template:
{{ post.post_author.avatar }}
There's no need for you get all the authors and pass them to the template in your view as you are doing.

Related

Trying to delete a comment from a post in django

I am currently trying to delete a comment from my database via a button in django template.
Model looks like this
from django.db import models
from django.contrib.auth.models import User
from cloudinary.models import CloudinaryField
from profiles.models import UserProfile
class Post(models.Model):
user_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, related_name='user_posts')
title = models.CharField(max_length=220, unique=True)
location = models.CharField(max_length=220)
rating = models.DecimalField(
max_digits=6, decimal_places=2)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="activity_post")
updated_on = models.DateTimeField(auto_now=True)
description = models.TextField()
featured_image = CloudinaryField('image', blank=False)
created_on = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField(User, related_name='activity_likes', blank=True)
like_count = models.BigIntegerField(default='0')
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
def number_of_likes(self):
return self.likes.count()
def liked_by_user(self):
return self.likes.values_list('id', flat=True)
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="user_comment")
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_on']
def __str__(self):
return f"Comment {self.body} by {self.name}"
Delete function
def delete_comment(request, post_id):
users_comment = get_object_or_404(Comment, post=post_id)
users_comment.delete()
return redirect(reverse('activity'))
URLS
from . import views
from django.urls import path
urlpatterns = [
path('like/', views.like, name='like'),
path("add/", views.add_post, name="add_post"),
path('edit/<int:post_id>/', views.edit_post, name='edit_post'),
path('delete/<int:post_id>/', views.delete_post, name='delete_post'),
path('edit_comment/<int:id>/', views.edit_comment, name='edit_comment'),
path('delete_comment/<int:post_id>/', views.delete_comment, name='delete_comment'),
path("activity/", views.PostList.as_view(), name="activity"),
path('comment/<int:post_id>/', views.Comment.as_view(), name='comment'),
path('searched_posts/', views.search_posts, name='searched_posts'),
path('post/<int:post_id>/', views.post_detail, name='post_detail')
]
here is the comment part that is showing the button.
{%if comments %}
{% for comment in comments %}
{% if comment.user == request.user %}
{{comment.body}} :comment
{{comment.id}} id
<a class="btn tbn-success" href="{% url 'edit_comment' comment.id %}" aria-label="edit button">Edit</a>
<button class="btn btn-warning">Delete</button>
{% endif %}
{% endfor%}
{% endif%}
When I click delete i get an error
Error
Any help would be greatly appreciated, I have tried a ton of different ways from online but nothing seems to work. can anyone point me in the right direction
The first thing I can see is that your delete function uses post = post_id.
Every comment on a particular post will share that post foreign key, so if there is more than one comment on a post, you can't use get_or_404() - it's limited to returning 1 item.
The URL you create for your button is using comment.id so it makes sense to use that instead - this will make it easier to see what's happening.
urls.py
path('delete_comment/<int:comment_id>/', views.delete_comment, name='delete_comment'),
views.py
def delete_comment(request, comment_id):
users_comment = get_object_or_404(Comment, pk=comment_id)
users_comment.delete()
return redirect(reverse('activity'))

Post filter on the basis of tags in django taggit(package)

I just installed django taggit by following official documentation. Every things work fine but when i click on the tags it doesn't filter post containing that tags here is my code.
Code containing models.py file
..............
from taggit.managers import TaggableManager
..................
class Post(models.Model):
STATUS_CHOICES = ((DRAFT, _('draft')),
(PUBLISHED, _('published')))
author = models.ForeignKey(get_user_model(), verbose_name=_(
"Author"), on_delete=models.CASCADE, related_name='blog_posts')
title = models.CharField(_("Title"), max_length=200, unique=True)
slug = models.SlugField(_("Slug"), unique_for_date='publish')
status = models.IntegerField(
_('status'), db_index=True,
choices=STATUS_CHOICES, default=DRAFT)
tags = TaggableManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("blog:post_detail", kwargs={
"pk": self.pk
})
code contain views.py file
from django.views.generic import ListView
class PostListView(ListView):
model = Post
queryset = Post.published.all().order_by('-publish')
context_object_name = 'post_list'
template_name = "client/pages/blog_list.html"
paginate_by = 7
urls.py file
from .views import PostListView,
..........
path('tag/<slug:tag_slug>/', PostListView.as_view(), name='post_list_by_tag'),
..........
and in the templates file
<ul class="tags-inline">
<li>Tags:</li>
{% for tag in post_detail.tags.all %}
<li>
{{ tag.name }}
,
</li>
{% endfor %}
</ul>
You need to change your queryset to filter based on the tag. This is bringing back everything based on all()
queryset = Post.published.all().order_by('-publish')
I'm not sure how you filter on Taggit

django 2.0 how to show the filtered list of products

I am new to python and django. I am trying to create a shopping site. I have a few categories and each category has subcategories. From the home page, when i click on a category, i want to see the subcategory related only to that category. Currently i am seeing a list of all subcategories (not just one category's subcategory but all the subcategories). Please guide me how to achieve this.
My models.py is as follows:
from django.db.models import CASCADE
class Category(models.Model):
image = models.ImageField(upload_to='images/', blank=True)
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True, unique=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def get_absolute_url(self):
return '/category/{slug}/'.format(slug=self.slug)
def __str__(self):
return self.name
class Subcategory(models.Model):
category = models.ForeignKey(Category, related_name='subcategory', on_delete=CASCADE)
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True, unique=True)
class Meta:
ordering = ('name',)
verbose_name = 'subcategory'
verbose_name_plural = 'subcategories'
def __str__(self):
return self.name
My admin.py is as follows:
from django.contrib import admin
from .models import Category, Subcategory
admin.site.register(Category)
class SubcategoryAdmin(admin.ModelAdmin):
list_display = ['__str__', 'slug']
prepopulated_fields = {'slug':('name',)}
class Meta:
model = Subcategory
admin.site.register(Subcategory, SubcategoryAdmin)
My views.py is as follows:
from django.views.generic import ListView
from .models import Category, Subcategory
class CategoryListView(ListView):
queryset = Category.objects.all()
template_name = 'category/home.html'
class SubcategoryListView(ListView):
model = Subcategory
queryset = Subcategory.objects.filter(name='Jeans')
print(queryset)
template_name = 'category/detail.html'
In the above code i am trying to filter with a subcategory name called Jeans. But what i want is when i click on a category say Men, my url will display localhost:8000/category/Men
So i want to display all the subcategory under Men category. I don't know how to achieve this.
My urls.py (main) is as follows:
from django.contrib import admin
from django.urls import path, include
from category.views import CategoryListView
urlpatterns = [
path('admin/', admin.site.urls),
path('', CategoryListView.as_view()),
path('category/', CategoryListView.as_view()),
path('category/', include('category.urls')),
]
The category/urls.py is as follows:
from django.conf.urls import url
from .views import SubcategoryListView
urlpatterns = [
url(r'^(?P<slug>[\w-]+)/$', SubcategoryListView.as_view()),
]
My home.html is as follows:
{% for object in object_list %}
{{ object.name }}</br>
{% endfor %}
My detail.html is as follows:
{% for object in object_list %}
{{ object.slug }}</br>
{% endfor %}
Highly appreciate all the help and guidance.
Rather than defining a queryset attribute in your SubcategoryListView class, you need to override the get_queryset method. This should use the slug to filter by the parent category.
class SubcategoryListView(ListView):
template_name = 'category/detail.html'
def get_queryset(self):
return Subcategory.objects.filter(category__slug=self.kwargs['slug])

How to display comments and it's replies in django templates?

I want to display comment and it's replies in the template. But there is an issue, every reply may have some other replies. The below snippet is my Comment and CommentReply model:
class Comment(models.Model):
author = models.ForeignKey(Profile, related_name="c_sender", on_delete=models.CASCADE, unique=False)
comment = models.CharField(max_length=500, unique=False)
created_date = models.DateTimeField(auto_now_add=True)
edited_date = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.comment
#property
def replys(self):
return CommentReply.objects.filter(comment_id=self)
class CommentReply(models.Model):
comment_id = models.ForeignKey(Comment, related_name='sender', on_delete=models.CASCADE)
reply_id = models.ForeignKey(Comment, related_name='reply', on_delete=models.CASCADE)
Updated:
Also I have a model for WorksComments that every comments that related to Work model saved there.
class WorkComment(models.Model):
work_id = models.ForeignKey(Work, on_delete=models.CASCADE, related_name='e_exercise', unique=False)
comment_id = models.ForeignKey(Comment, related_name='e_comment', unique=False)
The below snippet is my view:
comments = WorkComment.objects.filter(work_id=work).all()
return render(request, 'work.html', {'comments': comments})
My question is how to display comments and it's replies under it, and every reply may have some other replyies that I want to display them too.
First things first... put this in your bookmarks; https://ccbv.co.uk/
You're going to want a Detail View here I suspect in order to display the details of an instance.
Setup URLs...
from django.conf.urls import url
from work.views import WorkDetailView
urlpatterns = [
url(r'^(?P<id>[-\d]+)/$', WorkDetailView.as_view(), name='work-detail'),
]
And a view;
from django.views.generic.detail import DetailView
from django.utils import timezone
from work.models import Work
class WorkDetailView(DetailView):
model = Work
def get_context_data(self, **kwargs):
context = super(WorkDetailView, self).get_context_data(**kwargs)
context['comments'] = WorkComment.objects.filter(work_id=self.object.id).all()
return context
Then a simple view might be work/work_detail.html:
<h1>{{ object.title }}</h1>
<p>{{ object.content }}</p>
<h2>Comments</h2>
{% for comment in comments %}
{{ comment }}
{% endfor %}

How to retrieve data from one to many relations in django?

I am making my personal website using django 1.10
Here is models of skill app:
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Skill(models.Model):
name = models.CharField(max_length=256)
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, auto_now_add=False)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
class Subskill(models.Model):
skill = models.ForeignKey(Skill, on_delete=models.CASCADE)
name = models.CharField(max_length=256)
link = models.CharField(max_length=256)
created_at = models.DateTimeField(auto_now=False, auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, auto_now_add=False)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
And view:
from django.shortcuts import render
from skill.models import Skill,Subskill
# Create your views here.
def home(request):
skill = Skill.objects.all()
subskill =Subskill.objects.all()
context = {'skills':skill,
'subskills':subskill}
return render(request, 'skill.html', context)
This is my template page:
skill.html
{% block skill %}
{% for subskill in subskills %}
{{subskill.skill.name}}
{{subskill.name}}
{% endfor %}
{% endblock skill %}
Let assume, there is a skill named web design which has two subskill named html and css.
I want to render in view page as like as skill name and it's two child name:
Web design
Html
CSS
But it renders as like Web design Html Web design CSS
Please help me about this issue.
You can do realted query on skill itself
https://docs.djangoproject.com/en/1.10/topics/db/queries/#backwards-related-objects
# example
skill_obj = Skill.objects.all()[0]
subskills = skill_obj.subskill_set.all()
Or in your case
def home(request):
skills = Skill.objects.all().prefetch_related('subskill_set') # optimizing
context = {'skills':skills}
return render(request, 'skill.html', context)
In template
{% for skill in skills %}
{{skill.name}}
{% for subskill in skill.subskill_set.all %}
{{subskill.name}}
{% endfor %}
{% endfor %}