Introduction
I’m developing an article publishing application in Django.And I’m developing a function to edit comments on articles using Bootstrap modal forms.
Premise
The models are CustomUser
for user information, Article
for articles, and Comment
for comments, with the following relationship.
from mdeditor.fields import MDTextField
class CustomUser(AbstractUser):
username = models.CharField(
_("username"),
max_length=30,
help_text='Required 30 characters or fewer.',
unique=True,
error_messages={
'unique': _("This Username already exists."),
},)
email = models.EmailField(
_('email'),
unique=True,
error_messages={
'unique': _("A user with that email address already exists."),
},)
class Meta:
verbose_name_plural = 'CustomUser'
class Article(models.Model):
post_user = models.ForeignKey(CustomUser, verbose_name='Post User', on_delete=models.CASCADE, related_name='name',)
title = models.CharField(verbose_name='title', max_length=50,)
content = MDTextField()
created_at = models.DateField(verbose_name='created_at', auto_now_add=True,)
class Meta:
verbose_name_plural = 'Article'
def __str__(self):
return self.title
class Comment(models.Model):
writer = models.ForeignKey(CustomUser, on_delete=models.CASCADE,)
text = MDTextField()
target = models.ForeignKey(Article, on_delete=models.CASCADE,)
created_at = models.DateField(verbose_name='created_at', auto_now_add=True,)
updated_at = models.DateField(verbose_name='updated_at', auto_now=True,)
def __str__(self):
return self.text[:20]
The requirements for the article detail page (article_detail.html) where the comment edit function is to be placed are as follows.
・Display the contents of the article (DetailView)
・Display comments on the article
・Set up an input form for comments.
・Provide a pull-down menu for each comment with a link to edit/delete the comment.
・A modal window is displayed when a link in the pull-down menu is pressed, and the comment can be edited/deleted on that window.
The ArticleDetailView
is used to display the article detail page, while the list of comments, comment input form, and comment edit form are prepared by overriding get_context_data()
. Also, the View for edit comments is done in CommentUpdateView
, but since it is done in a modal window, no template for edit comments is prepared.
class ArticleDetailView(generic.DetailView):
model = Article
template_name = 'article_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
#comment creation form
context['form'] = CommentCreateForm()
#comment editing form
comment = Comment.objects.get(article__target=self.object)
context['comment_form'] = CommentCreateForm(instance=comment)
#comment list
context['comment_list'] = Comment.objects.select_related('target').filter(target=self.kwargs['pk'])
return context
class CommentUpdateView(generic.UpdateView, LoginRequiredMixin):
model = Comment
form_class = CommentCreateForm
template_name = 'article_detail.html'
def get_success_url(self):
article = Article.objects.get(comment__pk=self.kwargs['pk'])
return reverse_lazy('article:article_detail', kwargs={'pk': article.pk})
def form_valid(self, form):
messages.success(self.request, 'Your comment has been successfully updated.')
return super().form_valid(form)
def form_invalid(self, form):
messages.error(self.request, 'Failed to correct comment.')
return super().form_invalid(form)
The model form, routing, and template (only the part related to the modal for comment edit) are as follows.
class CommentCreateForm(forms.ModelForm):
class Meta:
model = Comment
fields = ("text",)
app_name = 'article'
urlpatterns = [
path('article_detail/<int:pk>', views.ArticleDetailView.as_view(), name='article_detail'),
path('<int:pk>/comment/update', views.CommentUpdateView.as_view(), name='comment_update'),
]
{% if comment_list %}
<section class="comment-list">
<h2 id="comments" class="comment-title"><i class="fa-regular fa-comments"></i> comment</h2>
{% for comment in comment_list %}
{% if user == comment.writer %}
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img class="rounded-circle u-box-shadow-sm mr-2" width="25" height="25" src="{% static 'img/three_dots_icon.png' %}" alt="Icon">
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item" id="showModal" data-toggle="modal" data-target="#commentModal-{{ comment.pk }}">Edit</a>
<a class="dropdown-item" id="showModal2" data-toggle="modal" data-target="#deleteModal-{{ comment.pk }}">Delete</a>
</div>
<!-- Comment Edit Modal Dialog -->
<div class="modal fade bd-example-modal-lg" id="commentModal-{{ comment.pk }}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modify Your Comment</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form class="text-center w-md-75 mx-auto" method="POST" enctype="multipart/form-data" action="{% url 'article:comment_update' comment.pk %}">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.media }}
{% for field in comment_form %}
<div class="col-xl-12 form-group mb-4">
{{ field|markdown }}
{{ field.errors }}
</div>
{% endfor %}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-lg btn-primary py-3 px-4" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-lg btn-primary py-3 px-4">Save changes</button>
</div>
</div>
</div>
</div>
</li>
</ul>
{% endif %}
<div class="comment-content">
<p>{{ comment.text|markdown|safe }}</p>
</div>
<div class="reply">
<a class="comment-reply-link" href="#"><span class="fa fa-comment-o"></span> Reply</a>
</div>
</div>
</li>
</div>
{% endfor %}
</section>
{% endif %}
Issues
I got a FieldsError in my browser.
According to Traceback, it seems to be failing to create an instance of CommentCreateForm
.
#views.py-ArticleDetailView()-get_context_data()
#comment editing form
comment = Comment.objects.get(article__target=self.object)
context['comment_form'] = CommentCreateForm(instance=comment)
I would like to use get_context_data()
in ArticleDetailView
to capture the comments to be edited and create a CommentCreateForm()
instance based on that information.
I was wondering if it would be correct to use the CommentUpdateView
to capture the comments to be edited, instead of using the methods in ArticleDetailView
to do so. In any case, I have no idea how to capture the comment to be edited and set up an instance of it.