I am learning to build a Post-comment app on Django. I understand how and why the current user can be assigned as author in the Comment form. But I do not understand how the post_id can be assigned to connect the comment with a particular post.
Tried using various methods obtained from searching the web. I keep getting various errors- TypeError, KeyError, ValueError etc.
My models.py is set up like this:
class Post(models.Model):
pid = models.AutoField(primary_key=True)
title = models.CharField(max_length=1000)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog-home')#, kwargs={'pk':self.pk})
class Comment(models.Model):
cid = models.AutoField(primary_key=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment = models.TextField()
comment_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.comment
def get_absolute_url(self):
return reverse('blog-home')
def save(self, *args, **kwargs):
super(Comment, self).save(*args, **kwargs)
The views.py with the comment create view is as follows:
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
fields = ['comment']
def form_valid(self, form,**kwargs):
form.instance.author = self.request.user
form.instance.post_id = self.kwargs['post_id']
# IS THE ABOVE LINE CORRECT?
return super().form_valid(form)
The Answer button is on the home page included in every post block. The html for the same with the url for it is as below:
{% extends "blog/base.html" %}
{% block content%}
{% for post in posts%}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{post.author.profile.image.url}}">
<div class="media-body mb-8">
<div class="article-metadata mb-4">
<a class="mr-4" href="{% url 'user-posts' post.author.username %}">{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted|date:"F d, Y P e" }}</small>
</div>
<h2><a class="article-title" href="{% url 'post-detail' post.pid %}">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content }}</p>
<div class="border border-top-0">
<a class="btn float-right btn-info mb-0 mt-1" href="{% url 'comment-create' post.pid %}">Answer</a>
$$$$$ HERE IS THE URL FOR THE ANSWER BUTTON $$$$
</div>
</div>
</article>
{% endfor %}
{% endblock %}
I do not know how to pass the post_id or the id of the post on which the user clicks. Please help!!
Related
I am trying to create a comment creation page related to a single post on a blog-style site using Django.
The home page has a list of posts, each with a "comment" button. Ideally this "comment" button would then take you to a page that would have the original post listed at the top with the comment creation form underneath it.
I've been trying to figure out a way to access the data I'm looking for using primary keys but am not sure how to tie everything together.
Here are the 2 models I am trying to access (Post and Comment):
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=128)
content = models.TextField()
image = models.ImageField(upload_to='post_pics', blank=True)
date_posted = models.DateTimeField(default=timezone.now)
def get_absolute_url(self):
return reverse('home')
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.TextField()
date_created = models.DateTimeField(default=timezone.now)
And the urls.py:
from django.urls import path
from .views import PostCreateView, PostListView, VehicleListView, CommentCreateView
from . import views
urlpatterns = [
path('', PostListView.as_view(), name='home'),
path('comment/new/<int:pk>', CommentCreateView.as_view(), name='comment-create'),
]
This is my current HTML for the home page (currently adds the post id to the end of the HTML on linked "comment-create" page):
{% for post in posts %}
<div class="infinite-item">
<div class="card m-3">
<div class="card-body" style="background-color:#bdcade; padding-bottom:0px">
<div class="media mb-3">
<img src="{{ user.profile.image.url }}" class="d-block ui-w-40 rounded-circle" style="width:40px;height:auto;" alt="">
<div class="media-body ml-3">
<h5 style="color:#ffffff">{{ post.user.username }}</h5>
<div class="small text-muted">Yesterday</div>
</div>
</div>
</div>
<div class="card-body">
<h5 class="card-title">{{ post.title }}</h5>
<p class="card-text">{{ post.content }}</p>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary">Like</button>
<a class="btn btn-secondary" href="{% url 'comment-create' post.id %}" role="button">Comment</a>
</div>
</div>
</div>
</div>
This is my current HTML for the comment-creation page:
{% block content %}
<div class="card m-3">
<div class="card-body" style="background-color:#bdcade; padding-bottom:0px">
<div class="media mb-3">
<img src="{{ post.user.profile.image.url }}" class="d-block ui-w-40 rounded-circle" style="width:40px;height:auto;" alt="">
<div class="media-body ml-3">
<h5 style="color:#ffffff">{{ post.user.username }}</h5>
<div class="small text-muted">{{ post.title }}</div>
</div>
</div>
</div>
<div class="card-body">
<h5 class="card-title">{{ post.title }}</h5>
<p class="card-text">{{ post.content }}</p>
<div class="btn-group" role="group">
<button type="button" class="btn btn-secondary">Like</button>
<button type="button" class="btn btn-secondary">Comment</button>
</div>
</div>
</div>
<br>
<div class="container primary-segments">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post Comment</button>
</div>
</form>
</div>
{% endblock content %}
Here is the view for comment creation page (using get_context_data to access the Post model):
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
template_name = 'home/comment-form.html'
fields = ['comment',]
def get_context_data(self, **kwargs):
context = super(CommentCreateView, self).get_context_data(**kwargs)
pk = self.kwargs['pk']
context['post'] = Post.objects.filter(id=pk)
return context
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
Currently, I can't access any of the data for the post in the comment creation page. I think it has to do with how I'm trying to tie the pk to the post within the -get_context_data` function. The primary key for the desired post is showing up in the URL, just not sure how to get at the right data.
Any help would be much appreciated. Thank you!
So it looks to me like you're using pk wrong here.
The pk in this code segment here:
def get_context_data(self, **kwargs):
context = super(CommentCreateView, self).get_context_data(**kwargs)
pk = self.kwargs['pk']
is the pk of the comment, however you're trying to use it to lookup a Post with context['post'] = Post.objects.filter(id=pk). Remember that pk stands for primary key and references the object that you declared as the model for your view. If you want the Post associated with that Comment you'll need to search for it using Comment.post since that's the declared foreign key inside your model.
After a lot of Googling/trial-and-error, I found a solution that got me what I was looking for.
Updated my View from THIS (note: I was trying to force a specific PK to test if it would filter to the correct data, but for some reason I still couldn't access anything):
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
template_name = 'home/comment-form.html'
fields = ['comment',]
def get_context_data(self, **kwargs):
context = super(CommentCreateView, self).get_context_data(**kwargs)
pk = 7
context['post'] = Post.objects.filter(id=pk)
return context
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
To THIS:
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
template_name = 'home/comment-form.html'
fields = ['comment',]
def get_context_data(self, **kwargs):
context = super(CommentCreateView, self).get_context_data(**kwargs)
context['post'] = Post.objects.get(pk=self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
Have a basic, high-level understanding of what this is doing (grabbing the PK from the URL), but am not entirely sure why the original .filter() method wasn't working.
If anyone could provide some context as to what's going on behind the scenes here, I'd love to see it.
Thanks!
I am working on invoice management system having in which user can add invoice data and it will save in database and whenever user logged in the data will appear on home page.but the problem if whenever any user logged in the home page showing other users data also but i want active users data only. could you guys help me.
here is my home page,you can see i am logged in through user 1 but at home page user 2 data also appear
here is my code
views.py
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
from .models import Invoicelist
def home(request):
context = {
'invoices': Invoicelist.objects.all()
}
return render(request, 'invoicedata/home.html', context)
class InvoiceListView(ListView):
model = Invoicelist
template_name = 'invoicedata/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'invoices'
class InvoiceDetailView(DetailView):
model = Invoicelist
class InvoiceCreateView(LoginRequiredMixin, CreateView):
model = Invoicelist
fields = ['issuer','invoice_number','date','amount','currency','other']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class InvoiceUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Invoicelist
fields = ['issuer','invoice_number','date','amount','currency','other']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
invoice = self.get_object()
if self.request.user == invoice.author:
return True
return False
class InvoiceDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Invoicelist
success_url = '/'
def test_func(self):
invoice = self.get_object()
if self.request.user == invoice.author:
return True
return False
def about(request):
return render(request, 'invoicedata/about.html', {'title': 'About'})
models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.urls import reverse
class Invoicelist(models.Model):
issuer = models.CharField(max_length=50)
invoice_number = models.CharField(max_length=50)
date = models.CharField(max_length=50)
amount = models.IntegerField()
currency = models.CharField(max_length=10)
other = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return (self.issuer)
def get_absolute_url(self):
return reverse('invoice-detail', kwargs={'pk': self.pk})
home.html
{% extends "invoicedata/base.html" %}
{% block content %}
{% for invoice in invoices %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<h2><a class="article-title" href="{% url 'invoice-detail' invoice.id %}">{{ invoice.issuer }}</a></h2>
</div>
<p class="article-content">{{ invoice.invoice_number }}</p>
<p class="article-content">{{ invoice.date }}</p>
<p class="article-content">{{ invoice.amount }}</p>
<p class="article-content">{{ invoice.currency }}</p>
<p class="article-content">{{ invoice.other }}</p>
<div class="article-metadata">
<small class="text-muted">{{ invoice.author }}</small>
</div>
</div>
</article>
{% endfor %}
{% endblock content %}
invoicelist_deatil.html
{% extends "invoicedata/base.html" %}
{% block content %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ object.author.profile.image.url }}">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ object.author }}</a>
<small class="text-muted">{{ object.date }}</small>
{% if object.author == user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'invoice-update' object.id %}">Update</a>
<a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'invoice-delete' object.id %}">Delete</a>
</div>
{% endif %}
</div>
<h2 class="article-issuer">{{ object.issuer }}</h2>
<p class="article-invoice_number">{{ object.invoice_number }}</p>
<p class="article-date">{{ object.date }}</p>
<p class="article-amount">{{ object.amount }}</p>
<p class="article-currency">{{ object.currency }}</p>
<p class="article-other">{{ object.other }}</p>
</div>
</article>
{% endblock content %}
You'll have to override get_queryset() on e.g. the list view to only include the desired user's invoices. (You'll want to do this on the update view too, to avoid users being able to edit each others' invoices.)
As an aside, your model should be called Invoice, unless a single instance of the model actually represents a list of invoices.
class InvoiceListView(ListView):
model = Invoicelist
template_name = 'invoicedata/home.html'
context_object_name = 'invoices'
def get_queryset(self):
return self.model.objects.all().filter(issuer=self.request.user)
I am trying to go to the author profile if i press on his name. This is my HTML which do these:
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ user.username }}</h2>
</div>
</div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Profile Info</legend>
<p class="text-secondary"><h3>Contact Me Here: </h3>{{ user.email }}</p>
</fieldset>
</form>
</div>
{% endblock content %}
In my views.py i have this:
def author_profile(request):
user = Post.author
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form': u_form,
'p_form': p_form
}
return render(request, 'blog/author_profile.html', context)
Where UserUpdateForm and ProfileUpdateForm from the forms.py have this code:
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
Class for the Post:
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
If i press on the name it is getting me to the user profile, I tried to work with Post.author but didn't worked.
It's my first time with django.
I'm a newbie and I've been battling with an error while trying to display the detail page of a post. I've checked other answers relating to this question but the solutions still don't work for me.
This is the error I'm getting:
Reverse for 'blog_post' with no arguments not found. 1 pattern(s) tried: ['blog\\/post/(?P<slug>[-\\w]+)/$']
This is my model:
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique_for_date='publish')
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES,
default='draft')
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post', args=[str(self.slug)])
View functions:
class BlogHomePageView(ListView):
model = Post
template_name = 'blog/index.html'
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post.html'
slug_url_kwarg = 'slug'
query_pk_and_slug = True
Urlpatterns:
path('', BlogHomePageView.as_view(), name='blog_home'),
re_path(r'post/(?P<slug>[-\w]+)/$', PostDetailView.as_view(), name='blog_post'),
base.html
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
<div class="container">
<a class="navbar-brand" href="index.html">Toluwalemi</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false"
aria-label="Toggle navigation">
Menu
<i class="fas fa-bars"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'blog_home' %}">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'blog_about' %}">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'blog_post' %}">Sample Post</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'blog_contact' %}">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
detail page:
{% extends 'blog/base.html' %}
{% block title %}Post{% endblock %}
<!-- Page Header -->
{% block page_header %}
<div class="post-heading">
<h1>{{ post.title }}</h1>
<h2 class="subheading">Problems look mighty small from 150 miles up</h2>
<span class="meta">Posted by
{{ post.author }}
on {{ post.publish }}</span>
</div>
{% endblock %}
<!-- Post Content -->
<article>
{% block content %}
<p>{{ post.body }}</p>
list page:
{% extends 'blog/base.html' %}
{% block title %}Home{% endblock %}
<!-- Page Header -->
{% block page_header %}
<div class="site-heading">
<h1>Toluwalemi's Blog</h1>
<span class="subheading">Official Blog</span>
</div>
{% endblock %}
<!-- Main Content -->
{% block content %}
{% for post in object_list %}
<div class="post-preview">
<a href="{{ post.get_absolute_url }}">
<h2 class="post-title">
{{ post.title }}
</h2>
If you look at your URL definition, you can see that it expects an argument slug:
re_path(r'post/(?P<slug>[-\w]+)/$', PostDetailView.as_view(), name='blog_post'),
which, by the way, could also be expressed as as of Django 2.0:
path('post/<slug:slug>/', PostDetailView.as_view(), name='blog_post'),
Now, the error message from the image shows clearly that you are calling the URL template tag without said argument, it only has
href="{% url 'blog_post' %}"
and no slug argument is present.
You need to add the slug argument.
You have not even shown us the right template in your question where the line appears that is shown in the error message, so I cannot help further than this.
So guys, I finally figured it out. My slug url is now working perfectly.
The error was actually coming from my list page. The url to the detail view wasn't written well. I didn't include a namespace. So instead of <a href="{{ post.get_absolute_url }}"> it becomes <a href="{% url 'blog:blog_post' post.slug %}">
I also deleted redundant lines of codes in my models and my views page.
Here is what it looks like now.
models.py:
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, default="")
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
views.py
class BlogHomePageView(ListView):
model = Post
template_name = 'blog/index.html'
context_object_name = 'posts'
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post.html'
context_object_name = 'post'
blog/urls.py:
urlpatterns = [
path('', BlogHomePageView.as_view(), name='blog_home'),
path('post/<slug:slug>/', PostDetailView.as_view(), name='blog_post'),
]
list page(index.html):
{% extends 'blog/base.html' %}
{% block title %}Home{% endblock %}
<!-- Page Header -->
{% block page_header %}
<div class="site-heading">
<h1>Toluwalemi's Blog</h1>
<span class="subheading">Official Blog</span>
</div>
{% endblock %}
<!-- Main Content -->
{% block content %}
{% for post in posts %}
<div class="post-preview">
<a href="{% url 'blog:blog_post' post.slug %}">
<h2 class="post-title">
{{ post.title }}
</h2>
I have a problem which I want to help me solve.
I have a "Post" with comments included but, when I comment a "Post" the comment I made in "Post 1" appears in "Post 2" and in all "Posts" and I want to link the comment to a single post, I've been looking for solutions but I have not been able to make it work.
EDIT I ADDED MY post/models.py
class Post(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
autor = models.CharField(max_length=200)
description = models.TextField()
likes = models.PositiveIntegerField(default=0)
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
files = models.FileField(upload_to=upload_location, validators=
[validate_file_extension])
post_type = models.CharField(max_length=100, choices=Book_Type_Choices)
tags = TaggableManager()
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='cooments')
user = models.ForeignKey(User, unique=False)
text = models.CharField(max_length=250)
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
views.py
#login_required
def add_comment_to_post(request, pk):
post= get_object_or_404(Post, pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post= post
comment.user = request.user
comment.save()
return redirect('post_detail', slug=post.slug)
else:
form = CommentForm()
return render(request, 'comments/add_comment_to_post.html', {'form':form})
my add_comment.html, this code fragment is included on my post_detail.html
<form action="{% url 'add_comment_to_post' pk=post.pk %}" method="post">
{% csrf_token %}
{{form.as_p}}
<input type="submit" class="btn btn-primary" value="Comentar">
</form>
my book_detail.html
<div class="container">
<div class="row">
<div class="col-md-6 comment" style="text-align: center;">
<!-- HERE IS WHERE I INCLUDE THE add_comment.html -->
{% include 'comments/add_comment_to_post.html' %}
</div>
{% for comment in comments %}
{% if user.is_authenticated or comment.approved_comment %}
<div class="col-sm-6 col-sm-offset-1">
<div class="media-body">
<div class="well well-lg">
<div class="avatar">
<img src="{{comment.user.profile.pic.thumbnail.url}}" class="img-responsive img-circle" alt="">
</div>
<h4 class="media-heading text-uppercase reviews">{{comment.user.get_full_name}} </h4>
<ul class="media-date text-uppercase reviews list-inline">
<li>{{comment.created_date}}</li>
</ul>
<p class="media-comment">
{{comment.text}}
</p>
<a class="btn btn-info btn-circle text-uppercase" href="#" id="reply"><span class="glyphicon glyphicon-share-alt"></span> Reply</a>
<a class="btn btn-warning btn-circle text-uppercase" data-toggle="collapse" href="#replyOne"><span class="glyphicon glyphicon-comment"></span> 2 comments</a>
</div>
</div>
</div>
{% endif %}
{% empty %}
<p>No hay comentarios que mostrar :(</p>
{% endfor %}
</div>
</div>
Any idea how I can make comments work? Thanks!
In the comment model try to remove the unique=False
Change
class Comment(models.Model):
user = models.ForeignKey(User, unique=False)
to
class Comment(models.Model):
user = models.ForeignKey(User) # remove unique=False
Do the above and take it from there