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 %}
Related
I am following a Django course on youtube, and I need a small change in my Django form.
The form looks like:
Rendering the form fields in 'edit-user.html' template:
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-input">
<label for="id_{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{% endfor %}
<div id="lrf-options">
<div id="lrf-btn">
<input class="btn" type="submit" value="Update">
</div>
</div>
</form>
What I am actually asking:
So in Avatar Currently: 1044-3840x2160.jpg, I want actual image to display here instead of <filename>.
I think maybe I need to change my forms.py to generate <img> tag instead if <a> tag but I don't know how to do this.
models.py
class UserModel(AbstractUser):
name = models.CharField(max_length = 90)
email = models.EmailField(unique = True)
about = models.TextField(blank = True, null = True)
avatar = models.ImageField(null=True, default="avatar.svg")
USERNAME_FIELD = 'email'
and forms.py is:
class UserForm(forms.ModelForm):
class Meta:
model = UserModel
fields = ['avatar', 'name', 'username', 'email', 'about']
and in views.py the update function is:
def editUserProfile(request):
user = request.user
form = UserForm(instance=user)
if request.method == 'POST':
form = UserForm(request.POST, request.FILES, instance = user)
if form.is_valid():
form.save()
return redirect('chat:userProfileView', uname = user.username)
context = {
'form' : form
}
return render(request, 'chat/edit-user.html', context)
You can add request.user.avatar.url instead of currently url.Hide input and use jquery to give a click to input:
<div class="form-input">
<label for="id_Avatar">Avatar</label>
Currently:
<img id="current-avatar" src="{{request.user.avatar.url}}"/>
<br>
<a id="change-avatar">Change</a>
<input id="avatar-input" style="display:none;" type="file" name="avatar" accept="image/*" id="id_avatar">
</div>
<script>
$("#change-avatar").click(function(){
$("#avatar-input").click();
});
</script>
If you don't have jQuery add this to your main HTML:
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
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 made a notification system. When "B" user comments at "A" user's post, notification sends to A.
This is my part of my code.
models.py
from django.db import models
from freeboard.models import FreeBoardComment
from users.models import CustomUser
class Notification(models.Model):
TYPE_CHOCIES = (
("FreeBoardComment", "FreeBoardComment"),
)
creator = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, related_name="creator")
to = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, related_name="to")
notification_type = models.CharField(max_length=50, choices=TYPE_CHOCIES)
comment = models.CharField(max_length=1000, blank=True, null=True)
post_id = models.IntegerField(null=True)
class Meta:
ordering = ["-pk"]
def __str__(self):
return "From: {} - To: {}".format(self.creator, self.to)
notification/views.py
from django.views.generic import ListView
from .models import Notification
def create_notification(creator, to, notification_type, comment, post_id):
if creator.email != to.email:
notification = Notification.objects.create(
creator=creator,
to=to,
notification_type=notification_type,
comment=comment,
post_id=post_id,
)
notification.save()
class NotificationView(ListView):
model = Notification
template_name = "notification/notification.html"
freeboard/views.py
...
#login_required
def comment_write(request, pk):
post = get_object_or_404(FreeBoardPost, pk=pk)
if request.method == 'POST':
form = CreateFreeBoardComment(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.comment_writer = request.user
comment.post = post
comment.save()
# 포인트
award_points(request.user, 1)
### NOTIFICATION PART!!!! ###
create_notification(request.user, post.author, "FreeBoardComment", comment.comment_text, post.pk)
### NOTIFICATION PART !!! ###
return redirect("freeboard_detail", pk=post.id)
else:
form = CreateFreeBoardComment()
return render(request, "bbs/freeboard/free_board_comment.html", {"form": form})
notification.html
{% extends "base.html" %}
{% block css_file %}
<link href="/static/css/notification/notification.css" rel="stylesheet">
{% endblock %}
{% block content %}
<ul class="list-group">
{% for notification in notification_list %}
<li class="list-group-item dropdown">
<a href="{% if notification.notification_type == "FreeBoardComment" %}/free/{{ notification.post_id }}{% endif %}"
class="dropdown-toggle" style="color: #555; text-decoration: none;">
<div class="media">
<img src="{{ notification.creator.image.url }}" width="50" height="50"
class="pull-left img-rounded" style="margin: 2px"/>
<div class="media-body">
<h4 class="media-heading"><span
class="genre">[#{{ notification.to.nickname }}]</span> {{ notification.comment }}
</h4>
<span style="font-size: 0.9rem;"><i
class="fa fa-user"></i> {{ notification.creator.nickname }}</span>
</div>
</div>
</a>
</li>
{% endfor %}
</ul>
{% endblock %}
And what I'm trying to add is notification delete feature WITHOUT TEMPLATE(deleteview).
When user clicked the notification and redirected to url, I want to delete that notification.
Is there way I can do this?
you code is a little bit confusing but i here is the thing, i think it would be better to use DRF and remove the notification without the redirection like #Edgardo_Obregón mentioned. or if you don't wanna use DRF, then it would be a good idea if you simply use a small view:
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt # if you wanna bypass csrf, otherwise you can use csrf with ajax docs
def delete_notification(request,pk):
if request.METHOD == "DELETE": # optional
notification = Notification.objects.get(pk=pk)
if notification is not None:
notification.delete()
csrf with ajax docs
I've got a feedback app in django and it all seems to work fine, no errors i can submit the form and it all seems to work, however i have my model registered into my admin however when i submit the form i doesn't appear in my admin. Sorry if this is very basic i just cant get my head around it please help.
in my models.py
class Feedback(models.Model):
email = models.CharField(max_length=100)
message = models.CharField(max_length=1000)
def __unicode__(self):
return self.title
which i then pass through to forms.py
class FeedbackModelForm(forms.ModelForm):
class Meta:
model = Feedback
fields = ["email", "message"]
and my view is
def feedbackform(request):
form = FeedbackModelForm(request.Post or None)
if form.is_valid():
form.save()
return render(request, "feedback.html", {"form": form})
now in my html looks like this
{% block content %}
<div id="feedback">
<div id="feedback-form" style='display:none;' class="col-xs-4 col-md-4 panel panel-default">
<form method="POST" action="{{ form }}" class="form panel-body" role="form">{% csrf_token %}
<div class="form-group">
<input class="form-control" name="email" autofocus placeholder="Your e-mail" type="email" />
</div>
<div class="form-group">
<textarea class="form-control" name="message" required placeholder="Please write your feedback here..." rows="5"></textarea>
</div>
<button class="btn btn-primary pull-right" type="submit">Send</button>
</form>
</div>
<div id="feedback-tab">Feedback</div>
</div>
{% endblock %}
and in my admin
from .models import Feedback
from .forms import FeedbackModelForm
class FeedbackAdmin(admin.ModelAdmin):
form = FeedbackModelForm
admin.site.register(Feedback, FeedbackAdmin)
You have passed the
{{ form }}
as the action attribute, which is completely wrong. Put it inside a div as
{{ form.as_p }}
that will work for you.
And in the action attribute pass a url in the form of
{% url 'home_page_example' %}
if you wanted to remain in the same page and redirect via view
you can write
action = "."
Show us how did you register your model in the admin.
Make sure that you explicit config the form, like this
class FeedbackAdmin(admin.ModelAdmin)
form = FeedbackModelForm
admin.site.register(Feedback, FeedbackAdmin)
You should return email or message in def __unicode__(self):, not title.
class Feedback(models.Model):
email = models.CharField(max_length=100)
message = models.CharField(max_length=1000)
def __unicode__(self):
return self.email
I think that you should check if the view is currently saving your Feedback.
Try inspecting the DB or in a manage.py shell check if len(Feedback.objects.all()) change when you submit a Feedback in your view.
Also, I recommend you to change the email field to an EmailField and use the FormView class based view.
i am not able to upload an image from html page but it is possible from admin page
here is my models.py:
def get_upload_file_name(instance,filename):
return "image/%s_%s"%(str(time()).replace('.','_'),filename)
class Company_Profile(models.Model):
user = models.ForeignKey(User)
name = models.CharField(_('Company Name'), max_length= 30)
logo = models.FileField(_('Company Logo'), upload_to=get_upload_file_name)
address = models.TextField(_('Contact Address'), max_length=50)
phone_no = models.IntegerField(_('Contact No'), max_length=12)
my views.py:
def company_prof(request):
if request.method == 'POST':
comp_prof = Company_Prof(request.POST, request.FILES)
if comp_prof.is_valid():
save_prof = comp_prof.save(commit=False)
save_prof.user = request.user
save_prof.save()
messages.success(request, 'Thank you for Registration')
return HttpResponseRedirect('company/'+str(save_prof.id))
else:
comp_prof =Company_Prof()
variables = RequestContext(request, {
'comp_form': Company_Prof()})
return render_to_response("comp_profile.html",
locals(),
context_instance = RequestContext(request))
my settings.py is:
MEDIA_ROOT ='G:\Mini project\Pycharm projects\project4\static/'
MEDIA_URL = ''
html page is:
<form enctype="application/x-www-form-urlencoded" class="global_form" action="" method="post"><div><div><h3>Create Account</h3>
<div id="connect_signup_box" class="connect_box_form clearfix">
<form method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ comp_prof.as_p }}
<input type="submit" class="btn btn-success" value="submit">
{% if save_prof %}
<h3>The details are submitted</h3>
{% endif %}
<input type="reset" class="btn" value="cancel">
{% if value == 'cancel' %}
<h3>Canceled</h3>
{% endif %}
</form>
</div>
</div>
</div>
</form>
when i submit it says no files are chosen. but from admin page there is no problem.
help me..
I think the problem is you have nested forms, which isn't supported by browsers (Can you nest html forms?).
So I am assuming that the browser is just using the first form definition, which has the wrong enctype. Try removing the first form declaration and just keeping this one: <form method="POST" enctype="multipart/form-data">.