I've been trying to fix my messages in order to show the messages by most recent. I can't figure out where did I go wrong in the code.
The output in my inbox shows the list of newly created conversations with a new user on the very top followed by the previous messages. Whenever I try to message the other person in my inbox, it does not go up as the most recent in the list. I am hoping to put the most recent on the top of the list.
I am thinking that I can use sent_at to know the last active conversation in an active chat but I don't know where to start.
Here are my codes:
models.py
from django.db import models
from django.core.exceptions import ValidationError
from django.utils import timezone
from django.conf import settings
AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
class Message(models.Model):
"""
A private direct message.
"""
content = models.TextField('Content')
document = models.FileField(upload_to='direct_messages', blank=True, null=True)
sender = models.ForeignKey(
AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='sent_dm', verbose_name='Sender'
)
recipient = models.ForeignKey(
AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='received_dm', verbose_name='Recipient'
)
sent_at = models.DateTimeField('sent at', auto_now_add=True)
read_at = models.DateTimeField('read at', null=True, blank=True)
class Meta:
ordering = ['-sent_at']
#property
def unread(self):
"""
Returns whether the message was read or not.
"""
if self.read_at is not None:
return True
def __str__(self):
return self.content
def save(self, **kwargs):
"""
Check message sender and recipient and raise error if the are saved.
Save message when the condition passes.
"""
if self.sender == self.recipient:
raise ValidationError("You cant't send messages to yourself!")
if not self.id:
self.sent_at = timezone.now()
super(Message, self).save(**kwargs)
class ChatRoom(models.Model):
"""
A private char room
Attributes:
created_at (datetime): datetime value when chatroom is created.
recipient (user): user whom the chatroom sends first message.
sender (user): user who created the chatroom
"""
sender = models.ForeignKey(
AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='chatroom_sender', verbose_name='Sender'
)
recipient = models.ForeignKey(
AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='chatroom_recipient'
)
created_at = models.DateTimeField('sent at', auto_now_add=True)
class Meta:
ordering = ['-created_at']
unique_together = ('sender', 'recipient')
views.py
from django.shortcuts import render,redirect
from django.urls import reverse
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.db.models import Q
from django.views.generic import CreateView, RedirectView
from .models import Message, ChatRoom
from .forms import MessageForm
from .services import MessagingService
from users.models import User
class MessageDetailView(CreateView):
"""
Show a messages, and could send messages.
"""
model = ChatRoom
form_class = MessageForm
template_name = 'direct_messages/direct_messages.html'
def get_context_data(self, **kwargs):
"""
Returns conversations based on different conditions.
1. Fetch message based on chatroom (sender, recipient)
2. Fetch current conversation of the current user and assign it
to conversations context value
3. If current loggedIn user is sender, active_recipient will be
message recipient otherwise, message sender.
4. Fetch active conversation for message / chat tab.
:param kwargs: Description
:return: Description
"""
chat_id = self.kwargs.get('pk')
chatroom = ChatRoom.objects.get(pk=chat_id)
message = Message.objects.filter(
sender=chatroom.sender,
recipient=chatroom.recipient
).first()
if not message:
message = Message.objects.filter(
sender=chatroom.sender,
recipient=chatroom.recipient
).first()
# MessagingService().mark_as_read(message)
user = self.request.user
kwargs['active_conversation'] = message
current_conversations = MessagingService().get_conversations(user=self.request.user)
kwargs['conversations'] = current_conversations
if user == message.sender:
active_recipient = message.recipient
# kwargs['recipient'] = User.objects.get(username=message.recipient)
else:
active_recipient = message.sender
# kwargs['sender'] = message.sender
running_conversations = MessagingService().get_active_conversations(user, active_recipient)
kwargs['running_conversations'] = running_conversations
return super().get_context_data(**kwargs)
def form_valid(self, form):
"""
Checks for valid form and submit with updating message object.
:param form: form object
:return: Redirect to cuurent message conversation.
"""
obj = self.get_object()
if self.request.user == obj.sender:
recipient = obj.recipient
else:
recipient = obj.sender
message = form.save(commit=False)
message.sender = self.request.user
message.recipient = recipient
message.save()
messages.success(self.request, 'The message is sent with success!')
return redirect('direct_messages:user_message', obj.pk)
#method_decorator([login_required], name='dispatch')
class MessageView(RedirectView):
permanent = False
query_string = True
pattern_name = 'direct_messages:user_message'
def get_redirect_url(self, *args, **kwargs):
"""
Prepares redirect url when the project owner accept the proposal.
"""
user = self.request.user
chatroom = ChatRoom.objects.filter(Q(sender=user) | Q(recipient=user)).first()
if chatroom:
return super().get_redirect_url(*args, pk=chatroom.pk)
messages.warning(self.request, 'You do not have any messages to show.')
return reverse('direct_messages:list_message')
direct_messages.html
{% extends 'base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% load humanize %}
{% block styles %}
<link rel="stylesheet" href="{% static 'assets/css/message.css' %}">
<link rel="stylesheet" href="{% static 'css/profile.css' %}" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
{% endblock %}
{% block content %}
<section id="main">
<div class="container">
<div class="messaging">
<div class="inbox_msg">
<div class="inbox_people">
<div class="headind_srch">
<div class="recent_heading">
<h4>Recent</h4>
</div>
</div>
<div class="inbox_chat">
{% for chat in conversations %}
<div class="chat_list {% if forloop.counter == 1 %} active_chat {% endif %} ">
<a style="display: block;
height: 100%;
width: 100%;
text-decoration: none;"
href="{% url 'direct_messages:user_message' chat.pk %}" class="fill-div">
<div class="chat_people" >
<div class="chat_img" >
<img src="{{ MEDIA_URL }}{{ chat.recipient.profile_photo.url }}" alt="sunil">
</div>
<div class="chat_ib" >
<h5>{{ chat.recipient }}</h5>
</div>
</div>
</a>
</div>
{% endfor %}
</div>
</div>
<div class="mesgs">
<div class="msg_history">
{% for conversation in running_conversations %}
{% if user == conversation.sender %}
<div class="outgoing_msg">
<div class="sent_msg">
<p>{{ conversation.content | safe }}</p>
{% if conversation.document %}
<a type="button" style="color:#FFFFFF; background-color:#A9A9A9; border: 3pt lightgrey" class="badge badge-secondary" target="_blank" href="{{ MEDIA_URL }}{{ conversation.document.url | safe }}" >See attachment</a>
{% endif %}
<span class="time_date">{{ conversation.sent_at | naturaltime }}</span>
</div>
</div>
{% else %}
<div class="incoming_msg">
<div class="incoming_msg_img">
<img src="{{ MEDIA_URL }}{{ conversation.sender.profile_photo.url }}" alt="{{ conversation.recipient.username }}">
</div>
<div class="received_msg">
<div class="received_withd_msg">
<p>{{ conversation.content | safe }}</p>
{% if conversation.document %}
<a type="button" style="color:#FFFFFF; background-color:#A9A9A9; border: 3pt lightgrey" class="badge badge-secondary" target="_blank" href="{{ MEDIA_URL }}{{ conversation.document.url | safe }}" >See attachment</a>
{% endif %}
<span class="time_date">{{ conversation.sent_at | naturaltime }}</span>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
<div class="type_msg">
<div class="input_msg_write">
<form method="post" novalidate enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
{{ field }}
{# {% if field.help_text %}#}
{# {{ field.help_text }}#}
{# {% endif %}#}
{# {% for error in field.errors %}#}
{# {{ error }}#}
{# {% endfor %}#}
{% endfor %}
<button class="msg_send_btn" type="submit"> <i class="fa fa-paper-plane-o" style="font-size:15px" aria-hidden="true"> </i> </button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
if you want to organize chats then you should update your model like:
class ChatRoom(models.Model):
"""
A private char room
Attributes:
created_at (datetime): datetime value when chatroom is created.
recipient (user): user whom the chatroom sends first message.
sender (user): user who created the chatroom
"""
sender = models.ForeignKey(
AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='chatroom_sender', verbose_name='Sender'
)
recipient = models.ForeignKey(
AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name='chatroom_recipient'
)
created_at = models.DateTimeField('sent at', auto_now_add=True)
last_update = models.DateTimeField('last message',auto_now=False)
class Meta:
ordering = ['-created_at']
unique_together = ('sender', 'recipient')
then you have to update last_update field when new message arrives from sender or send by user and arrange chat list according to last_update time like:
chats = ChatRoom.objects.all().order_by('-last_update')
I hope this is what you want
This is the code to start a new conversation, I am developing a freelance marketplace so a new conversation will only be made when the employer accepts the proposal of the freelancer
class ProposalAcceptView(RedirectView):
"""
Aceept a proposal.
"""
permanent = False
query_string = True
pattern_name = 'jobs:job_detail'
def get_redirect_url(self, *args, **kwargs):
job = get_object_or_404(Job, pk=kwargs['pk'])
job.freelancer = User.objects.get(username=kwargs.get('username'))
job.status = 'working'
job.save()
# Create message opening
is_chatroom = False
try:
chatroom = ChatRoom.objects.get(sender=self.request.user, recipient=job.freelancer)
is_chatroom = True
except:
pass
if not is_chatroom:
try:
chatroom = ChatRoom.objects.get(sender=job.freelancer, recipient=self.request.user)
except:
pass
if not is_chatroom:
chatroom = ChatRoom.objects.create(sender=self.request.user, recipient=job.freelancer)
print('is chatroom', is_chatroom)
print('chat roo', chatroom)
print('chat room created....')
MessagingService().send_message(
sender=self.request.user,
recipient=job.freelancer,
message="""
Hi {username},
Your proposal is accepted.
Project details: <a href='{url}'>{job}</a>
""".format(username=job.freelancer.username,
url=reverse("jobs:job_detail", kwargs={"pk": job.pk}),
job=job.job_title
)
)
messages.success(
self.request, 'User : {} is assiged to your project'.format(kwargs.get('username'))
)
return super().get_redirect_url(*args, pk=kwargs['pk'])
Related
I have a form so an user can ask for a loan and it will tell them if it´s approved or not. The problem is not the logic, it´s the submit input that doesn't work. It will not save the form in the database or show me the errors because of the submit input. Maybe is something wrong with the succes_url? I don't know, but here's my code:
views.py:
#don't worry about the logic part of the form, it's just to show how it´s supposed to work
class LoanRequest(LoginRequiredMixin, generic.CreateView):
form_class = LoanForm
success_url = reverse_lazy('Prestamos')
template_name = 'Prestamos/template/Prestamos/prestamos.html'
def form_valid(self, form):
user = self.request.user
cliente = Cliente.objects.get(user_id = user.id)
if not cliente.approve_loan(form.cleaned_data.get('loan_total')):
form.add_error(field=None, error='loan not approved')
return self.form_invalid(form)
else:
form.instance.customer_id = cliente
super(LoanRequest, self).form_valid(form)
return render(self.request, 'Prestamos/template/Prestamos/prestamos.html', context={'form': form, 'success_msg': 'loan approved!'})
urls.py:
urlpatterns = [
path('prestamos/', views.LoanRequest.as_view(), name = 'prestamos'),
]
forms.py:
class LoanForm(forms.ModelForm):
class Meta:
model = Prestamo #loan in English
fields = ['loan_type', 'loan_total', 'loan_date']
and the template:
<div class="container">
{%if success_msg%}
<p class="alert alert-success">{{success_msg}}</p>
{%endif%}
<form action="" method="POST">
{%csrf_token%}
{%for field in form%}
<div class="form-group">
<label for="{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{%for error in field.errors%}
<p>{{error}}</p>
{%endfor%}
{%endfor%}
<input type="submit" value="request"></input>
</form>
</div>
models.py:
class Prestamo(models.Model):
loan_id = models.AutoField(primary_key=True)
loan_type = models.CharField(max_length=20,
choices = [('PERSONAL', 'PERSONAL'), ('HIPOTECARIO', 'HIPOTECARIO'), ('PRENDARIO', 'PRENDARIO')])
loan_date = models.DateField()
loan_total = models.IntegerField()
customer_id = models.IntegerField()
class Meta:
db_table = 'prestamo'
Well, <input> is an empty tag, it does not contain anything, so don't close it.
Additionally, I'd recommend you to make gaps between template tags, like it should be {% endfor %} not {%endfor%}.
Also, remove the empty action attribute from form, as Django always take current page route if not mentioned or empty string.
Also use novalidate on form for rendering custom errors.
Try this template:
<div class="container">
{% if success_msg %}
<p class="alert alert-success">{{success_msg}}</p>
{% endif %}
<form method="POST" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{% for error in field.errors %}
<p>{{error}}</p>
{% endfor %}
{% endfor %}
<input type="submit" value="request">
</form>
</div>
Edit:
One mistake I could see the name for the view is prestamos and you have mentioned it as Prestamos, which is wrong.
So:
class LoanRequest(LoginRequiredMixin, generic.CreateView):
form_class = LoanForm
success_url = reverse_lazy('prestamos')
template_name = 'Prestamos/template/Prestamos/prestamos.html'
Hello I have faced an Issue regarding sending email In django rest API.
The Idea is user sends email and Uploads image from serializer part and I want to send same image to users Email. But I am not getting image in email.
here is the code I have been working.
models.py
class Mail(BaseModel):
full_name = models.CharField(max_length=100)
image = models.ImageField(upload_to=mail_image_to)
email = models.EmailField()
below is my serializer
class MailSerializer(serializers.ModelSerializer):
class Meta:
model = Mail
fields = '__all__'
class AddMailSerializer(MailSerializer):
class Meta(MailSerializer.Meta):
fields = (
'full_name',
'image',
'email',
)
views.py
class AddMailView(generics.CreateAPIView):
"""
Use this endpoint to add mail
"""
serializer_class = serializers.AddMailSerializer
def perform_create(self, serializer):
return AddMailUseCase(serializer=serializer).execute()
I usually write my rest of code in usecase.py
class AddMailUseCase:
def __init__(self, serializer):
self.serializer = serializer
self.data = serializer.validated_data
def execute(self):
self._factory()
def _factory(self):
self._mail = Mail(**self.data)
self._mail.save()
SendEmail(
context={
"fullname": self.data['full_name'],
'image': self.data['image']
}
).send(to=[self.data['email']])
I am using django-templated-mail 1.1.1 to send email here is my rest of code.
from templated_mail.mail import BaseEmailMessage
class SendEmail(BaseEmailMessage):
template_name = 'email.html'
and finally my email.html
{% load i18n %}
{% block subject %}
{% blocktrans %}Email Successfully Sent {% endblocktrans %}
{% endblock subject %}
{% block text_body %}
{% blocktrans %}You're receiving this email because you recently registered to our website.{% endblocktrans %}
{% trans "Your submitted details are as follow" %}
{% endblock text_body %}
{% block html_body %}
{% blocktrans %}
<p>hello {{ fullname }}</p>{% endblocktrans %}
{% blocktrans %}
<img src="{{ image }}" alt="email-image">
<p>{% trans "Thanks for using our site!" %}</p>
{% endblock html_body %}
any help regarding this issue?
the email I got is as below image is not loading
instead of using
<img src="{{ image }}" alt="email-image">
use
<img src="{{ image.url }}" alt="email-image">
and in your URL.py add
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = patterns('',
# ... the rest of your URLconf goes here ...
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
I can't find out why this is not working.
It always goes to {% else %} block.
The text in machine_model is something like "Lidl - Food Market" or "Kaufland - Buy Here" or something other without those two words.
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django import forms
from django.urls import reverse
class MyName(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
class MyModel(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
class MySide(models.Model):
name = models.CharField(max_length=50, unique=True)
class MyMachine(models.Model):
date_delivery = models.DateTimeField(null=True)
machine_name = models.ForeignKey(MyName, on_delete=models.PROTECT)
machine_model = models.ForeignKey(MyModel, on_delete=models.PROTECT)
machine_serial = models.CharField(max_length=15, default='0')
use_side = models.ForeignKey(MySide, on_delete=models.PROTECT)
views.py
from django.views.generic import ListView
from .models import MyMachine
class MyMachineListView(ListView):
model = MyMachine
template_name = 'site/home.html'
context_object_name = 'machines'
ordering = ['date_delivery']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
site_title = 'Home'
context["site_title"] = site_title
return context
home.html
{% extends "site/base.html" %}
{% load static %}
{% block content %}
{% for machine in machines %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<small class="text-muted">{{ machine.date_delivery|date:"d.F Y" }}
</small>
</div>
<h2><a class="article-title" href="">{{ machine.machine_name }} - {{ machine.machine_serial }}</a></h2>
{% if 'Lidl' in machines.machine_model %}
<p class="article-content">{{ machine.use_side }} - Lidl</p>
{% elif 'Kaufland' in machines.machine_model %}
<p class="article-content">{{ machine.use_side }} - Kaufland</p>
{% else %}
<p class="article-content">{{ machine.use_side }} - {{ machine.machine_model}}</p>
{% endif %}
</div>
</article>
{% endfor %}
{% endblock content %}
everything else is working fine. Thank You in advance!
I see two problems here.
One, you're referencing machines.machine_model, but machines is a queryset. I'm somewhat surprised referencing machine_model on it doesn't just fail with a rendering error. It should be machine.machine_model, since that's your loop variable.
That leads us to the second problem. machine.machine_model is a foreign key reference to another model, not a string. There's no string that's in a model instance (barring defining the membership function yourself). I haven't personally tested but I don't think Django stringifies on the fly (as it will when you reference {{ machine.machine_model }}). Try it with if ... in machine.machine_model.name.
I'm making my own blog with Django and I already made a Comments system.. I want to add the replies for each comment (like a normal comment's box) and I don't know what to do this is my current models.py comments:
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created_date = models.DateField(auto_now_add=True)
parent = models.ForeignKey('self', null=True, related_name='replies')
def __str__(self):
return self.text
and this is the .html where I use the comments
{% for comment in post.comments.all %}
<ul>
{{ comment.text }}
{% for reply in comment.replies.all %}
<li>
{{ reply.text }}
</li>
{% endfor %}
<ul>
{% endfor %}
and apparently It is working but when I try to make a comment in the admin site of Django it forces me to put a "Parent" to each comment (and this is not obligatory beacuse not every comment is a reply) I also don't know how to add the reply "button" in the HTML file. Please help tell me what changes can I do to make a simple comment box with replies . Thanks a lot
I had the same problem and resolved it as follows:
1.
For admin site as mentioned above just set blank=True for parent field. My comment model:
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField(max_length=200, blank=True)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
# manually deactivate inappropriate comments from admin site
active = models.BooleanField(default=True)
parent = models.ForeignKey('self', null=True, blank=True, related_name='replies')
class Meta:
# sort comments in chronological order by default
ordering = ('created',)
def __str__(self):
return 'Comment by {}'.format(self.name)
remember to run makemigrations and migrate
2.Let's start with views. I'm using the post_detail
view to display the post and its comments. We add a QuerySet to retrieve all parent active comments for this post. After this, we validate the submitted data using the form's is_valid(). If the form is valid we check if submitted data comes from hidden input in replay button form. Next if parent_id exits we create parent object(parent_obj) for replay comment and replay_comment object, then we assign parent_obj to replay_comment.
If parent_obj is equal to None we just proceed with normal comment by creating new_comment object and saving it to the database.
def post_detail(request, post):
# get post object
post = get_object_or_404(Post, slug=post)
# list of active parent comments
comments = post.comments.filter(active=True, parent__isnull=True)
if request.method == 'POST':
# comment has been added
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
parent_obj = None
# get parent comment id from hidden input
try:
# id integer e.g. 15
parent_id = int(request.POST.get('parent_id'))
except:
parent_id = None
# if parent_id has been submitted get parent_obj id
if parent_id:
parent_obj = Comment.objects.get(id=parent_id)
# if parent object exist
if parent_obj:
# create replay comment object
replay_comment = comment_form.save(commit=False)
# assign parent_obj to replay comment
replay_comment.parent = parent_obj
# normal comment
# create comment object but do not save to database
new_comment = comment_form.save(commit=False)
# assign ship to the comment
new_comment.post = post
# save
new_comment.save()
return HttpResponseRedirect(post.get_absolute_url())
else:
comment_form = CommentForm()
return render(request,
'core/detail.html',
{'post': post,
'comments': comments,
'comment_form': comment_form})
Simple comment form:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
* More about ModelForm
And lastly template. We need to create two forms. One form for comments and the second one for replays. Here you have simple template:
<!-- Comments Form -->
<h2>Add a new comment</h2>
<form action="." method="post">
{{ comment_form.as_p }}
{% csrf_token %}
<button type="submit">Add comment</button>
</form>
<!-- Comment with nested comments -->
{% for comment in comments %}
<div class="comment" style="background-color: powderblue">
<p class="info">{{ comment.name }} | {{ comment.created }}</p>
{{ comment.body|linebreaks }}
{% for replay in comment.replies.all %}
<p class="info">{{ replay.name }} | {{ replay.created }}</p>
<li>{{ replay.body }}</li>
{% endfor %}
<h5>Replay</h5>
<form action="." method="post">
{{ comment_form.as_p }}
{% csrf_token %}
<!-- Hidden input for parent comment.id -->
<input type="hidden" name="parent_id" value="{{ comment.id }}">
<input class="btn btn-primary" type="submit" value="Replay">
</form>
</div>
{% empty %}
<h4>There are no comments yet.</h4>
{% endfor %}
just add some nice css and maybe jquery to have fade in reply comments and that's all.
first Question:parent must be set in admin.
parent = models.ForeignKey('self', null=True, blank=True, related_name='replies')
blank=True can let you don't set parent in admin.
second Question:add comment dynamicly.
<form id="comment-form" method="post" role="form">
{% csrf_token %}
<textarea id="comment" name="comment" class="form-control" rows="4" placeholder="input comment!"></textarea>
<button type="submit" class="btn btn-raised btn-primary pull-right">submit</button>
</form>
$('#comment-form').submit(function(){
$.ajax({
type:"POST",
url:"{% url 'article_comments' article.en_title %}",
data:{"comment":$("#comment").val()},
beforeSend:function(xhr){
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
},
success:function(data,textStatus){
$("#comment").val("");
$(".comment ul").prepend(data);
},
error:function(XMLHttpRequest, textStatus, errorThrown){
alert(XMLHttpRequest.responseText);
}
});
return false;
});
view.py:
print_comment = u"<p>comment:{}</p>".format(text)
if parent:
print_comment = u"<div class=\"comment-quote\">\
<p>\
<a>#{}</a>\
{}\
</p>\
</div>".format(
parent.user.username,
parent.text
) + print_comment
# current comment
html = u"<li>\
<div class=\"comment-tx\">\
<img src={} width=\"40\"></img>\
</div>\
<div class=\"comment-content\">\
<a><h1>{}</h1></a>\
{}\
<p>{}</p>\
</div>\
</li>".format(
img,
comment.user.username,
print_comment,
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
)
return HttpResponse(html)
models.py
class Comment(models.Model):
author = models.CharField(max_length=100)
comment_field = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey('Post', on_delete=models.CASCADE)
reply = models.ForeignKey('Comment', on_delete=models.CASCADE, related_name="replies", null=True)
def __str__(self):
return self.author
views.py
def post_detail(request, slug):
post = Post.objects.get(slug=slug)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
reply_obj = None
try:
reply_id = int(request.POST.get('reply_id'))
except:
reply_id = None
if reply_id:
reply_obj = Comment.objects.get(id=reply_id)
author = form.cleaned_data['author']
comment = form.cleaned_data['comment']
if reply_obj:
Comment(author=author,comment_field=comment, reply=reply_obj, post=post).save()
else:
Comment(author=author,comment_field=comment, post=post).save()
return redirect(reverse('post_detail', args=[post.slug]))
else:
form = CommentForm()
comments = Comment.objects.filter(post=post, reply=None).order_by('-date_created')
context = {
'post':post,
'form':form,
'comments':comments
}
return render(request, 'post_detail.html', context)
template (post_detail.html)
{% for comment in comments %}
{{comment.author}}
{{comment.date_created.date}}
{{comment.comment_field}}
{% for reply in comment.replies.all %}
{{reply.author}}
{{reply.date_created.date}}
{{reply.comment_field}}
{% endfor %}
<a class="text-decoration-none" data-bs-toggle="collapse" href="#collapseExample{{comment.id}}" role="button" aria-expanded="false" aria-controls="collapseExample">
Reply </a>
<div class="collapse" id="collapseExample{{comment.id}}">
<div>
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
{{ form.author }}
</div>
<div class="form-group mt-md-2">
{{form.comment}}
</div>
<input type="hidden" name="reply_id" value="{{ comment.id }}">
<button class="btn btn-primary mt-md-1" type="submit" value="Reply">Reply</button> </form>
</div>
</div>
<hr>
{% endfor %}
You need to create a field called parent which is a ForeignKey with the Comment model itself.
parent = models.ForeignKey('self' , null=True , blank=True , on_delete=models.CASCADE , related_name='replies')
Also, you can create a property called children in the Comment model which returns all the replies of the comment.
#property
def children(self):
return BlogComment.objects.filter(parent=self).reverse()
For detail explanation of comment and reply system in django you can read my article about that.
I am trying to get data from a model form and then put it into a database. I have figured out how to make the form, but when clicking the submit button it doesn't seem to be put anywhere in my database. Am I doing anything wrong or am I not looking in the correct place in the database.
forms.py
from django import forms
from sxsw.models import Account
class AccountForm(forms.ModelForm):
class Meta:
model = Account
fields = ['firstName', 'lastName', 'email']
views.py
from django.shortcuts import render
from django.shortcuts import redirect
from .forms import AccountForm
from .models import Account
def sxsw(request):
if request.method == 'POST':
form = AccountForm(request.POST)
if form.is_valid():
form.save()
else:
print form.errors
else:
form = AccountForm()
return render(request, 'sxsw/sxsw.html', {'form': form})
def formSubmitted(request):
return render(request, 'sxsw/formSubmitted.html',{})
models.py
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Account(models.Model):
firstName = models.CharField(max_length = 50)
lastName = models.CharField(max_length = 50)
email = models.EmailField()
def __unicode__(self):
return self.firstName
class Module(models.Model):
author = models.ForeignKey(Account, on_delete = models.CASCADE)
nameOfModule = models.CharField(max_length = 150) #arbitrary number
moduleFile = models.FileField(upload_to = 'uploads/')#Not exactly sure about the upload_to thing
public = models.BooleanField()
def __unicode__(self):
return self.nameOfModule
sxsw.html
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<div class="jumbotron text-center">
<h3>SXSW Form</h3>
</div>
</div>
<div align="center">
<h1>New Form</h1>
<form role='form' action="/sxsw/formSubmitted/" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
</div>
</div>
{% endblock %}
formSubmitted.html
{% extends "base.html" %}
{% block content %}
<div class="container-fluid">
<div class="jumbotron text-center">
<h3>Form Successfully submitted</h3>
</div>
</div>
<div align="center">
Submit Another Response
</div>
</div>
{% endblock %}
Your form is posting to what I presume is the wrong url
<form role='form' action="/sxsw/formSubmitted/" method="post">
should use the url for the sxsw view
<form role='form' action="/sxsw/" method="post">
Once submitted, you'll likely want to redirect to the submitted view
return redirect('/sxsw/formSubmitted/') # Preferably use the url pattern name here
It looks like your form's action is set to /sxsw/formSubmitted/ that always simply return the submitted page, instead of the url that will call the sxsw view where the form's save method is called.