In Models.py
class Post(models.Model):
user = models.CharField(max_length=100)
likes = models.IntegerField(default=0)
content = models.TextField()
date = models.DateTimeField(auto_now_add=True)
class Profile(models.Model):
following = models.ForeignKey('User',on_delete=models.CASCADE,related_name='following')
user = models.ForeignKey('User',on_delete=models.CASCADE,related_name='user')
def __str__(self):
return self.user
In views.py
def viewProfile(request,username):
posts = Post.objects.filter(user=username).order_by('id').reverse()
profile = Profile.objects.filter(user=username)
no_of_followers = profile.following.count()
return render(request, "network/profile.html",{
"posts":posts,
"username":username,
"no_of_followers":no_of_followers
})
In profile.html
{% extends "network/layout.html" %}
{% block body %}
<h2 style="margin-left: 20px;">Posts of {{username}}</h2>
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h5 class="card-title">Profile Details</h5>
<p class="card-text">Followers:{{no_of_followers}}</p>
<p class="card-text">Followings:0</p>
Go somewhere
</div>
</div>
</div>
{% for post in posts %}
<div class="card" style="width:70%;margin-left: 10%;margin-right: 20%;">
<div class="card-body">
<h5 class="card-title">{{post.user}}</h5>
<div class="form-group">
<p>{{post.date}}<br>{{post.content}}</p>
</div>
<p>{{post.likes}}</p>
</div>
</div>
{% endfor %}
{% endblock %}
Facing an error of Field 'id' expected a number but got 'xyz'. xyz is the username
If I replace profile = Profile.objects.filter(user=username)with profile = Profile.objects.filter(user__user=username) then I am getting the error django.core.exceptions.FieldError: Related Field got invalid lookup: user
The lookup should be:
profile = Profile.objects.get(user__username=username)
since you want to retrieve a Profile for which the user has a username that has the given username.
You probably should use .get(…) [Django-doc] here to retrieve a Profile object, not a QuerySet of Profiles.
You might want to use the get_object_or_404(…) function [Django-doc] instead, such that it returns a 404 in case no such profile exists:
from django.shortcuts import get_object_or_404
def viewProfile(request,username):
posts = Post.objects.filter(user__username=username).order_by('-id')
profile = get_object_or_404(Profile, user__username=username)
no_of_followers = profile.following.count()
return render(request, 'network/profile.html',{
'posts':posts,
'username':username,
'no_of_followers":no_of_followers
})
Related
I'm trying to implement a feature where logged in users can comment on a blog post, but I'm a little stuck with the comments appearing on the page.
I can type in the comment and submit them. However, this is always returning a GET request. I tried adding method='post' to both the form and button tags, but this resulted in the following:
Method Not Allowed (POST): /post/1/
Method Not Allowed: /post/1/
I'm not really sure what the problem is. Any help would be much appreciated. Code snippets below.
post_detail.html with comment section:
<div class="content-section">
<legend class="border-bottom">Comments:</legend>
{% if user.is_authenticated %}
<form id="commentForm">
{% csrf_token %}
<fieldset class="form-group">
<div class="row">
<!-- <div class="col"></div> -->
<div class="col-1 text-right">
<img class="img-comment" src="{{ user.profile.image.url }}">
</div>
<div class="col-9">
<textarea type="text" id="commentIn" name="comment"
placeholder="Your comment..." required></textarea>
</div>
<div class="col"></div>
</div>
<div class="row">
<div class="col-10 text-right">
<button id="commentBtn" name="comment_button"
class="btn btn-outline-info" type="submit">Comment</button>
</div>
<div class="col"></div>
</div>
<hr>
</fieldset>
</form>
{% endif %}
<div id="commentDiv">
{% for cmnt in comments_list %}
<img class="img-comment"
src="{{cmnt.commentator.profile.image.url }}">
<a class="mr-2" >{{ cmnt.commentator }}</a>
<p class="article-content">{{ cmnt.comment }}</p>
<hr>
{% endfor %}
</div>
</div>
urls.py in blog app
urlpatterns = [
path('post/<int:pk>/', PostDetailView.as_view(), name="post-detail"),
models.py
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)
likes = models.ManyToManyField(User, related_name='like')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
commentator = models.ForeignKey(User, on_delete=models.CASCADE)
blog = models.ForeignKey(Post, on_delete=models.CASCADE)
comment = models.CharField(max_length=200)
date_added = models.DateTimeField(default=timezone.now)
class meta:
verbose_name_plural = 'comments'
def __str__(self):
return self.comment
views.py
class PostDetailView(DetailView):
model = Post
form = CommentForm()
def get_context_data(self, **kwargs):
if self.request.method == 'GET':
print('Render a blank form for users to fill out')
print(self.form)
elif self.request.method == 'POST':
print('Get the data from the POST request')
print(self.form)
status = 0
if self.request.user in self.object.likes.all():
status = 1
context = super().get_context_data(**kwargs)
context['comments_list'] = Comment.objects.filter(blog=self.object)
context['status'] = status
return context
and finally, the form itself:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['comment']
Don't mix class-based-view with method-based-view. You have to provide function that reads method and behave as it should. For get you usually will have built-in solutions, like form_class - in post() you can call it via form = self.get_form(). It generally looks like this:
class PostDetailView(DetailView):
model = Post
form_class = CommentForm
def post(self, request, *args, **kwargs):
form = self.get_form()
print('Get the data from the POST request')
print(form)
def get_context_data(self, **kwargs):
status = 0
if self.request.user in self.object.likes.all():
status = 1
context = super().get_context_data(**kwargs)
context['comments_list'] = Comment.objects.filter(blog=self.object)
context['status'] = status
return context
Another things to check ie. here: https://stackoverflow.com/a/45661979/12775662
I try to remove comment so, I tried the first time by the class-based view then I hashed it to try the second way by normal function to know what's going on here. so, when I try to delete the comment by ID nothing does happen it's just directing me to the web page without deleting the comment so in this case the program runs but doesn't remove the object so, what is going on here?
Note: the posts and comments on the same page and slug field on that page are following by post not comment.
if the title of the post is: new title so, the slug will be new-title depending on the post
question_view.html
<div class="user-answer">
<div class="row">
<div class="col-xs-12">
{% for comment in my_question.comment_set.all %}
<div class="comments">
<div class="col-xs-0">
<div class="avatar">
<a href="{% url 'account:view_profile' comment.username %}">
<img class="img-circle img-thumbnail" style="width:50px; height: 50px;" src="{{ comment.logo }}">
</a>
</div>
</div>
<div class="col-xs-10">
<!-- --Comment itself-- -->
<div class="user_comment">
<p>{{ comment }}</p>
<div class="fa fa-caret-left comment-arrow"></div>
</div>
<!-- start Options in comment -->
<div class="sub-options">
{% if request.user.username == comment.username %}
<!-- --Edit comment-- -->
<div class="edit-comment">
<a>Edit</a>
</div>
<!-- --Delete comment-- -->
<div class="delete-comment">
<form method="post" action="{% url 'community:delete_comment' comment.pk %}">
{% csrf_token %}
<input type="hidden" name="delete-comment" value="{{ comment.comment }}">
<input type="submit" value="delete">
</form>
</div>
{% endif %}
<!-- end Options in comment -->
<!-- --comment Date-- -->
<div style="display: inline-block;color: #8e8e8e" class="comment-date">
<p>{{ comment.date|time }}</p>
</div>
</div>
</div>
<div class="clearfix"></div>
</div>
{% endfor %}
</div>
</div>
</div>
views.py
# Delete post
# class DeleteComment(DeleteView, SingleObjectMixin):
# model = Comment
# pk_url_kwarg = 'pk'
# template_name = 'community/question_view.html'
# queryset = Comment.objects.all()
#
# def get_success_url(self):
# title = UserAsking.objects.get(title=self.object)
# slug = UserAsking.objects.get(title=title).ask_slug
# return reverse_lazy('community:question_view', kwargs={'user_slug': slug})
#
# def get_object(self, queryset=None):
# request_comment = self.request.POST['delete-comment']
# return self.get_queryset().filter(pk=request_comment).get()
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk)
del comment
return redirect('community:user_questions')
urls.py
urlpatterns = [
# path('delete-comment/<int:pk>/', views.DeleteComment.as_view(), name="delete_comment"),
path('delete-comment/<int:pk>/', views.delete_comment, name="delete_comment"),
]
models.py
class Comment(models.Model):
userasking = models.ForeignKey(UserAsking, on_delete=models.CASCADE)
comment = models.TextField(max_length=500, blank=True)
date = models.DateTimeField(auto_now_add=True)
userprofile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, default=1)
name = models.CharField(max_length=30, blank=False, default='empty')
logo = models.ImageField(upload_to='images/', default='images/default-logo.jpg', blank=True)
username = models.CharField(max_length=30, blank=False, default='empty')
def __str__(self):
return self.comment
I hope if you can explain by class based-view what's happening I will appreciate that.
note that: if you could understand exactly what's happening you will know that is no error appears to me, therefore, I get no exception or traceback. thank you in advance.
Try this:
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk).delete()
return redirect('community:user_questions')
Your function only deletes the object but not the database entry since you don't trigger a valid SQL operation (delete in this case).
You just delete the object comment again which was assigned previously, but you don't affect the database entry:
def delete_comment(request, pk):
if request.method == 'POST':
comment = Comment.objects.get(pk=pk)
del comment
return redirect('community:user_questions')
More on delete() in the official docu:
Deleting objects¶
Update on your comment:
You can't use Python's del statement to interact with database entries in Django.
Pythons del statement is used to delete objects initially created by Python like lists, variables, etc. etc.
In order to interact with your database in Django, you have to use the toolbox of Django's built-in Model instance which basically translates raw SQL operations into easy to use methods.
So maybe you can adapt a proper wording to highlight the differences by calling database "objects" entries and python objects: well, objects..
However, Django still offers the option to use raw SQL as fallback.
I am creating this project in django. I am working on reporting films, where I go to a film-detail view, then hit the report button. A report_form shows up where I justify why I am reporting, and then I click report. Everything works fine, but there is one thing. After reporting I get sent back to a random(?) film-detail view, but I would like to go back to the view for the film I am reporting. But how???
views.py
class FilmReportView(LoginRequiredMixin, CreateView):
model = Report
fields = ['reason']
def form_valid(self, form):
form.instance.reporter = self.request.user
form.instance.reports_id = self.kwargs['pk']
return super().form_valid(form)
def get_success_url(self):
return "film/<int:pk>/report"
report_form.html
{% extends "board/base.html" %}
{% load crispy_forms_tags %}
{% load materializecss %}
{% block content %}
<div class="valign-wrapper row login-box">
<div class="col card hoverable s10 pull-s1 m6 pull-m3 l8 pull-l2">
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
<div class="card-content">
<span class="card-title">Jusity why you want to report this film?</span>
<div class="row">
{{ form|materializecss }}
</div>
</div>
<div class="card-action right-align">
<input type="reset" id="reset" class="btn-flat grey-text waves-effect">
<input type="submit" class="btn green waves-effect waves-light" value="Report">
</div>
</form>
</div>
</div>
{% endblock content %}
urls.py
urlpatterns = [
path("", views.films_view, name="board-home"),
path("film/add", FilmAddView.as_view(), name="film-add"),
path("film/<int:pk>/", FilmDetailView.as_view(), name="film-detail"),
path("film/<int:pk>/report", FilmReportView.as_view(), name="film-report"),
]
models.py
class Report(models.Model):
reason = models.TextField()
reporter = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Reporter")
reports = models.ForeignKey(Film, on_delete=models.CASCADE)
def __str__(self): # pragma: no cover
return f"{self.reports.title} reported by {self.reporter.username}"
def get_absolute_url(self): # pragma: no cover
return reverse("film-detail", kwargs={"pk": self.pk})
Fix your FilmReportView get_success_url() to look like that:
def get_success_url(self):
return reverse("film-detail", kwargs={"pk": self.object.reports.id})
That should take care of it
In your views.py or even in the template you can put next parameter with url you want:
form/action/url?next={{request.path}}
See more about next:
https://www.reddit.com/r/django/comments/32s9ag/how_do_i_set_the_next_context_variable_in_django/
I want to share a project that currently can create user and each user can create N posts
The source is available on github
and I has two models users and post
and the template layers
Currently the feed for each post has a button that send an commenting the post I want to change that to put the comments of the post and not send and email each user should be able to comment a post and the comment should remain
{% block container %}
<body id="bg" img style="zoom: 85%; background-position: center center; background-attachment: fixed;background-repeat:no-repeat;padding:5px; background-image: url('{% static "/back.png"%}') ";>
<div style="background-image: url({% static 'static/img/back.png' %});">
<div class="row" style="align:center">
{% for post in posts %}
<div class="col-sm-12 col-md-8 offset-md-4 mt-5 p-0 post-container,width:50%;">
<div class="card" style="width: 32rem;width:50%;">
<div class="card-body">
<div class="media pt-3 pl-3 pb-1">
<a href="{% url " users:detail" post.user.username%}">
<img alt="{{ post.user.username }}" class="mr-3 rounded-circle" height="35"
src="{{ post.profile.picture.url }}">
</a>
<h3 class="card-title">{{ post.title }}</h3>
</div>
<p class="card-text">{{ post.desc }}</p>
</div>
</div>
<img alt="{{ post.title }}" src="{{ post.photo.url }}" style="width: 50%; heigth:60%">
<div class="media-body">
<b><p style="margin-top: 5px;">#{{ post.user.username }} - <small>{{ post.created }}</small>
<a href="" style="color: #000; font-size: 20px;">
<i class="far fa-heart"></i>
</a>
<br>
</p></b>
</div>
<!-- COMENT SECTION THAT I WANT TO IMPLEMENT MY FEATURE-->
<form action="{% url 'posts:comment_new' %}" enctype="multipart/form-data" method="POST">
{% csrf_token %}
<input
class="form-control {% if form.title.errors %}is-invalid{% endif %}"
name="title"
size="16"
type="hidden"
value="{{post.title}}"
>
<input
class="form-control {% if form.title.errors %}is-invalid{% endif %}"
name="first_name "
size="16"
type="hidden"
value="{{user.first_name}}"
>
<input
class="form-control {% if form.title.errors %}is-invalid{% endif %}"
name="last_name "
size="16"
type="hidden"
value="{{user.last_name}}"
>
<textarea class="form-control" cols="50" name="comment" rows="5"
style="width:50%;" value="{{ comments.comment }}"></textarea>
<button class="btn btn-outline-info btn-lg" style="width:35%; display:block;margin:auto;" type="submit">
Publish
</button>
</form>
</div>
<br>
{% endfor %}
</div>
</div>
{% endblock %}
As I said I want to replace this form function call to create a comment section instead sending a email with the comment
< form action = "{% url 'posts:comment_new' %}">
def comment_new(request):
if request.method == 'POST':
message = request.POST['comment']
subject = request.POST['title']
user = request.POST['first_name']
last_name = request.POST['last_name']
# lastname = request.POST['lastname']
send_mail("[MAIL] " + subject, user + " " + last_name + " said " + message + " on http://url.com:8000",
'guillermo.varelli#gmail.com',
['guillermo.varelli#gmail.com'], fail_silently=False)
posts = Post.objects.all().order_by('-created')
return render(request, os.path.join(BASE_DIR, 'templates', 'posts', 'feed.html'), {'posts': posts})
I think this maybe create a comment with user and post id with the comment detail
def comment_new(request):
if request.method == 'POST':
message = request.POST['comment']
subject = request.POST['title']
user = request.POST['first_name']
last_name = request.POST['last_name']
#lastname = request.POST['lastname']
form = PostForm(request.POST, request.FILES)
form.save()
One options its create a comment
class Comment(models.Model):
"""
#id= models.AutoField(max_length=1000, blank=True)
# post = models.ForeignKey(Post, related_name='',on_delete=models.CASCADE,default=0)
"""
#comment = models.ForeignKey('posts.Post', related_name='posts_rel', to_field="comments", db_column="comments",
# on_delete=models.CASCADE, null=True, default=1, blank=True)
post = models.IntegerField(blank=True,null=True,unique=True)
user = models.ForeignKey(User, on_delete=models.CASCADE,null=True)
username = models.CharField(blank=True, null=True, unique=True ,max_length=200)
comment = models.CharField(max_length=254, blank=True, null=True)
and then the form
class CommentForm(forms.ModelForm):
class Meta:
"""form settings"""
model = Comment
fields = ('user','username','post','comment',)
finally with the function I'm able to persist but not able to render
form = CommentForm(request.POST, request.FILES)
# print formset.errors
if form.is_valid():
form.save()
but I can't find the way to render the object on the html file
please feel free to suggest any solution or better create a pull request on the public git hub repo
In the book Django 2 by Example we can find a step by step guide to create a comment system, wherein the users will be able to comment on posts.
In order to do it, is as simple as the following four steps
Create a model to save the comments
Create a form to submit comments and validate the input data
Add a view that processes the form and saves the new comment to the database
Edit the post detail template to display the list of comments and the form to add a new comment
Create a model to save the comments
In your models.py file for the application, add the following code
class Comment(models.Model):
post = models.ForeignKey(Post,
on_delete=models.CASCADE,
related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return 'Comment by {} on {}'.format(self.name, self.post)
The new Comment model you just created is not yet synchronized into the database. Run the following command to generate a new migration that reflects the creation of the new model:
python manage.py makemigrations APPNAME
and
python manage.py migrate
After this, the new table exists in the database. Now, open the admin.py file of the blog application, import the Comment model, and add the following ModelAdmin class:
from .models import Post, Comment
#admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'post', 'created', 'active')
list_filter = ('active', 'created', 'updated')
search_fields = ('name', 'email', 'body')
Create a form to submit comments and validate the input data
Edit the forms.py file of your blog application and add the following lines:
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
Add a view that processes the form and saves the new comment to the database
Edit the views.py file, add imports for the Comment model and the CommentForm form, and modify the post detail view to make it look like the following:
from .models import Post, Comment
from .forms import EmailPostForm, CommentForm
def post_detail(request, year, month, day, post):
post = get_object_or_404(Post, slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
# List of active comments for this post
comments = post.comments.filter(active=True)
new_comment = None
if request.method == 'POST':
# A comment was posted
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# Create Comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# Assign the current post to the comment
new_comment.post = post
# Save the comment to the database
new_comment.save()
else:
comment_form = CommentForm()
return render(request,
'blog/post/detail.html',
{'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form})
Edit the post detail template to display the list of comments and the form to add a new comment
At this point we have created the functionality to manage comments for a post. Now, we will need to adapt our post/detail.html template to do the following things:
- Display the list of comments
- Display a form for users to add a new comment
Append the following lines to the post/detail.html template for the list of comments:
{% for comment in comments %}
<div class="comment">
<p class="info">
Comment {{ forloop.counter }} by {{ comment.name }}
{{ comment.created }}
</p>
{{ comment.body|linebreaks }}
</div>
{% empty %}
<p>There are no comments yet.</p>
{% endfor %}
Then, for the other point, add the following lines:
{% if new_comment %}
<h2>Your comment has been added.</h2>
{% else %}
<h2>Add a new comment</h2>
<form action="." method="post">
{{ comment_form.as_p }}
{% csrf_token %}
<p><input type="submit" value="Add comment"></p>
</form>
{% endif %}
I wanna make a page which shows POST's models' contents is shown each category.For example, when I put Python link in in detail.html,only POST's models' contents with Python's category is shown in category.html.When I put Python link in category.html,I got an error,ImproperlyConfigured at /app/category/Python/ CategoryView is missing a QuerySet. Define CategoryView.model, CategoryView.queryset, or override CategoryView.get_queryset(). I wrote codes in views.py
def top(request):
content = POST.objects.order_by('-created_at')[:5]
category_content = Category.objects.order_by('-created_at')[:5]
page = _get_page(blog_content, request.GET.get('page'))
return render(request, 'top.html',{'content':content,'category_content':category_content,"page":page})
class CategoryView(BaseListView):
template_name = 'category.html'
def get_queryset(self):
category_name = self.kwargs['category']
self.category = Category.objects.get(name=category_name)
queryset = super().get_queryset().filter(category=self.category)
return queryset
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['category'] = self.category
return context
in urls.py
urlpatterns = [
path('top/', views.top, name='top'),
path('category/<str:category>/',views.CategoryView.as_view(), name='category'),
]
in models.py
class Category(models.Model):
name = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class POST(models.Model):
title = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.title
in top.html
<div class="list-group">
<a href="#">
Category
</a>
<div>
{% for category in category_content %}
<a href="{% url 'category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</div>
</div>
in category.html
{% load static %}
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Category</title>
</head>
<body>
<div>
{% for content in queryset %}
<h2>{{ content.title }}</h2>
<img src="content.image.url" />
<a class="btn btn-outline-primary btn-lg btn-block" href="{% url 'detail' content.pk %}">SHOW DETAIL</a>
{% endfor %}
</div>
<div>
<a href="#" class="list-group-item active">
Category
</a>
<div>
{% for category in category_content %}
<a class="list-group-item justify-content-between" href="{% url 'category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</div>
</div>
</body>
</html>
I really cannot understand why ImproperlyConfigured which means settings.py is wrong.When I changed BaseListView into ListView,same error happens.I wrote get_queryset in CategoryView's class so I cannot understand codes needs QuerySet. How should I fix this?What is wrong in my codes?
One issue is that you're calling super().get_queryset(), which expects your class to define a queryset or a model. Adding model = Category will fix that error.
It looks like you're returning a queryset with just one entry, though. If you only want to get the one category, it would be simpler to use a DetailView:
from django.views.generic import DetailView
from .models import Category # or wherever it is
class CategoryView(DetailView):
model = Category
template_name = 'category.html'
def get_object(self):
category_name = self.kwargs['category']
return Category.objects.get(name=category_name)
Note that your template would need to be updated to reference category or object instead of looping through the queryset.