we are using django-contrib-comments in are project for communication between stuff and clients. Staff communication work's internally and I've made a custom email notification system which will notify you every time some other stuff member left a comment on your page.
Now the problem that I'm facing is that the user who want's to reply to a comment will only notify it's self and not the staff member who left a comment.
Email notification setup is made of two parts, first part is for the staff member who is leaving a comment and second is for the user who is replying to the comment.
Problem is how to refer to the user who all ready left a comment, I need his email, so I could add him to the recipient list so he will be notify when someone else replies to the comment.
comment.user and comment.user_email are things on which you refer on the comment model, but I can not find how to refer to user_email who's all ready left a comment when you want to reply to it, this part of the documentation explain model fields, I can understand that
user_email - The email of the user who posted the comment.
and it is perfect in the first part, but in the second it will refer to the user who is leaving a comment and that is why the other one will not be notified, so can someone please help me better understand how to refer to the user_email on which I'm replying, so I can make it work properly.
def send_comment_posted_emails(self, comment):
comment_user = comment.user
comment_user_email = comment.user_email
comment_text = comment.comment
handler_user = self.account_handler
handler_email = handler_user.email
# First part
if handler_email is not None and handler_email != comment_user.email:
current_site = Site.objects.get_current()
sub_org_url = self.get_view_url() + "#CommentsDiv"
ctx = {"sub_org_url": sub_org_url, "site_name": current_site.name, "sub_org_sn": self.serial_number,
"posted_name": user_util.get_user_full_name_or_user_name(comment_user),
"comment_text": comment_text}
subject = render_to_string("clients/emails/email_sub_org_comment_posted_subject.txt", ctx)
subject = "".join(subject.splitlines())
message = render_to_string("clients/emails/email_sub_org_comment_posted_message.html", ctx)
MailManager.send_mail_with_error_handler(subject, message, settings.DEFAULT_FROM_EMAIL,
[handler_email, comment_user_email], message_html=message)
# Second part
if handler_email is not None and handler_email == comment_user.email:
current_site = Site.objects.get_current()
sub_org_url = self.get_view_url() + "#CommentsDiv"
ctx = {"sub_org_url": sub_org_url, "site_name": current_site.name, "sub_org_sn": self.serial_number,
"posted_name": user_util.get_user_full_name_or_user_name(comment_user),
"comment_text": comment_text}
subject = render_to_string("clients/emails/reply_to_email_sub_org_comment_posted_subject.txt", ctx)
subject = "".join(subject.splitlines())
message = render_to_string("clients/emails/reply_to_email_sub_org_comment_posted_message.html", ctx)
MailManager.send_mail_with_error_handler(subject, message, settings.DEFAULT_FROM_EMAIL,
[handler_email, comment_user_email], message_html=message)
I found that django-contrib-comments are now composed/divided out in two separate libraries,
django-fluent-comments
django-threadedcomments
so in the threadedcomments there is a parent model field on which you can related when you are replying to a comment so, I've just made something like comment.parent.user_email and I've checked if parent is defined, and not None, and problem solved.
Related
I am trying to add messaging functionality to my web app made in Django.
So far, I have managed to successfully send and receive messages from user to user.
But, now I have been stuck at showing all the conversation lists to the inbox.html page of the logged user.
I have tried different approaches that I can think of but can not get the expected result.
models.py
class Messaging(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sender')
receiver = models.ForeignKey(User, on_delete=models.CASCADE, related_name='receiver')
message_text = models.TextField(max_length=360, verbose_name='Write Message')
message_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.sender}\'s Message to {self.receiver}'
viwes.py
Function to send and receive messages from user to user
#login_required
def messageview(request, user_name):
sender_user = request.user
receiver_user = User.objects.get(username=user_name)
message_list = Messaging.objects.filter(sender=sender_user, receiver=receiver_user).order_by('message_date') | \
Messaging.objects.filter(receiver=sender_user, sender=receiver_user).order_by('message_date')
if request.method == 'POST':
msg_text = request.POST.get('msg_text')
messaging = Messaging()
messaging.sender = sender_user
messaging.receiver = receiver_user
messaging.message_text = msg_text
messaging.save()
return redirect(request.META['HTTP_REFERER'])
context = {
'sender_user': sender_user,
'receiver_user': receiver_user,
'message_list': message_list,
}
return render(request, 'message.html', context)
Now I want to create an inboxview in views.py that will render all the conversation of the logged user.
Suppose I have two users in the database A and B, they have exchange 4 messages between them. What I want is to show the conversation as a list, which is in this case only one. For example, the logged user is A, he exchanges messages with user B and C. The inbox will show two rows. When user A clicks on either of the rows, he will be taken to the details message page corresponding to the user. It is kinds of like WhatsApp or messenger. I hope I can explain.
Edited: Added example image for better understanding
I am able to do this:
I need help to do this:
Please guide me the way.
You could try something like this.
This view will query the Messaging model and get all entries where sender or receiver is the logged in user.
#login_required
def inbox(request, user):
# Get all the records where either sender OR receiver is the logged in user
messages = Messaging.objects.filter(sender=request.user) | Messaging.objects.filter(receiver=request.user)
context = {'messages': messages}
return render(request, 'inbox.html', context)
You can add any extra lines of code to the above code that suits your requirements.
inbox.html
{% for message in messages %}
# You can show your message details here
{% endfor %}
I'm currently working on a social network site with Django.
I am working on the "tag" feature, which sends a Notification to a user whenever they are tagged in a Post.
I created a post_save signal like this:
def post_tagged(*args, **kwargs):
instance = kwargs['instance']
message = f'{instance.user.slug} mentioned you in a post.'
#Find in the content of the post if there are any "#username", if yes create a Notification object
for user in MyUser.objects.all():
if instance.content.find(f'#{user.slug}') != -1 and instance.user != user:
Notification.objects.create(user = user, message = message)
models.signals.post_save.connect(post_tagged, sender = Post)
This is working fine when I create a new Post, but it also triggered when I delete a Post. This is the code in the view to delete a post:
if 'deletePost' in request.POST:
postId = request.POST.get('deletePost', None)
post = Post.objects.get(pk = postId)
if request.user == post.user:
post.delete()
message = 'Success'
else:
message = 'Fail'
jsonData = {"message": message,}
return HttpResponse(json.dumps(jsonData))
Also if it helps you can see I'm using ajax to send the request to the view. And the delete is also working fine. The only problem is that it also create a Notification to the tagged users when I delete a post.
Anyone having the same issue? I appreciate any help. Thanks
Maybe I am not asking the right question in the search area, but I can't find an answer for this. I am pretty sure that many people have this use case, but as a beginner in Django + Python, I need to ask it.
I have user that fills up a form and the data is stored in the database. Basically this form asks for an access to a Database and after the form is submitted I want my program to send an email to the user's manager and to the DBA to APPROVE or DENY it. Very simple, right?
My idea is that in this e-mail I send two URL's, one for approving and one for denying the request. When the URL the is clicked I send a response to the server with an update in the manager_approval field.
Has anyone implemented this solution, or could point me to something that could help me?
I am doing everything using Django + Python.
Regards,
Marcos Freccia
Basically this technique used in email verification. This is where you should look into.
Let's say you have model, named request, which has field like username to identify the person who requested access, database name, well, everything. But it will also have two "password-like" fields which will be used to determine if request was declined or not.
class Request(models.Model):
user = models.ForeignKey ...
databasename =
date =
...
access_granted = models.BooleanField(default=False)
deny_token = models.CharField()
allow_token = models.CharField()
The point is to generate those tokens on saving request in the View:
if request.method == POST:
form = RequestForm(request.POST)
if form.is_valid():
data['user'] = form.cleaned_data['user'])
data['databasename'] = form.cleaned_data['databasename'])
...
data['access_token'] = GENERATE_USING_HASH_FUNCTION()
data['deny_token'] = GENERATE_USING_HASH_FUNCTION()
form.save(data)
Then you can use module EmailMultiAlternatives to send html email like so:
subject, from_email, to = 'Request', 'admin#example.com', form.cleaned_data['manager_email']
html_content = render_to_string(HTML_TEMPLATE, CONTEXT) # Just as any regular templates
text_content = strip_tags(html_content)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to], reply_to=["admin#example.com"])
msg.attach_alternative(html_content, "text/html")
msg.send()
And inside that template you construct reverse url:
{% url 'app:grant_access' allow_token=token %} # "token" you get from context
{% url 'app:deny_access' deny_token=token %} # will become example.com/deny_access/7ea3c95, where 7ea3c95 is token
Then add lines to urls.py of your app like that:
url(r'^allow_access/(?P<allow_token>[0-9]+)$', CheckAcessView.as_view(), name="app:grant_access"),
url(r'^deny_access/(?P<deny_token>[0-9]+)$', CheckAcessView.as_view(), name="app:deny_access"),]
Then create CheckAcessView view. Where you access request stored in your database and check if, for example, parameter of url "allow_token" is equal stored allow_token. If so, change request status to allowed.
so i currently have my likes app which deals with friend requests, and it works fine however my notification dont seem to be working. Whenever some likes someone else regardless of weather they are liked by that user or not it only sends the second of the two notify.send. I presume its an issue with the line "user = get_object_or_404(User, username=user.username)", however i dont know how to get round it. Here is my code:
def like_user(request, id):
pending_like = get_object_or_404(User, id=id)
user_like, created = UserLike.objects.get_or_create(user=request.user)
user = get_object_or_404(User, username=user.username)
liked_user, like_user_created = UserLike.objects.get_or_create(user=user)
if pending_like in user_like.liked_users.all():
user_like.liked_users.remove(pending_like)
elif request.user in liked_user.liked_users.all():
user_like.liked_users.add(pending_like)
notify.send(request.user,
#action=request.user.profile,
target=request.user.profile,
recipient=pending_like,
verb='sent you a friend request view'),
else:
user_like.liked_users.add(pending_like)
notify.send(request.user,
#action=request.user.profile,
target=request.user.profile,
recipient=pending_like,
verb='accepted your friend request view')
return redirect("profile", username=pending_like.username)
Here is an example of where the line " if request.user in liked_user.liked_users.all():" works fine i presume because the line " user = get_object_or_404(User, username=username)" has username in it.
#login_required
def profile_view(request, username):
user = get_object_or_404(User, username=username)
liked_user, like_user_created = UserLike.objects.get_or_create(user=user)
do_they_like = False
if request.user in liked_user.liked_users.all():
do_they_like = True
However in my first bit of code I'm trying to use user.username instead of username=username but i get the error "local variable 'user' referenced before assignment". What is the best way round this? am i do it completely wrong? should i try and pass in username, because when i do i get the error "like_user() takes exactly 3 arguments (2 given)". Sorry quite new to django, any help would be massively appreciated!
Here is my likes app model incase it helps:
class UserLike(models.Model):
user = models.OneToOneField(User, related_name='liker')
liked_users = models.ManyToManyField(User, related_name='liked_users', blank=True)
objects = UserLikeManager()
def __unicode__(self):
return self.user.username
def get_mutual_like(self, user_b):
i_like = False
you_like = False
if user_b in self.liked_users.all():
i_like = True
liked_user, created = UserLike.objects.get_or_create(user=user_b)
if self.user in liked_user.liked_users.all():
you_like = True
if you_like and i_like:
return True
else:
return False
really sorry for the long post, but im very stuck as most of this was written by some more advanced than me and im left with the issue of fixing it, any help would be massively appreciated!
thanks
You are correct, the issue is with the line:
user = get_object_or_404(User, username=user.username)
You're trying to get the attribute 'username' of the 'user' object, but 'user' is not defined within the scope of your function yet. In other words, at that point 'user' doesn't exist yet.
I don't have nearly enough info to start helping you, but if it was me debugging that, I'd consider the following:
You might not need that problematic line at all. It seems like request.user holds the reference to a user (The one who's liking stuff??), and pending_like seems to contain a reference to the recipient of the like. That's probably enough users to establish a 'like' relationship
Maybe do a print of the attributes of pending_like print(pending_like.__dict__) and request.user print(request.user.__dict__) to see if they contain all the info you need?
CONTEXT:
A model object's user_account_granted account indicates if a model object is linked to an non null user account.
When user_account_granted changes from False to True, I detect this in the overridden save() function. Here, I successfully make a User, pulling arguments (email, username, name, etc) from the model object
I create a password and send the new account login info to the object's email
If the email failed, I delete the account
PROBLEM:
I want to alert the current user (who just submitted the form triggering the save() ) that the email was either successful (and the new account now exists) or unsuccessful (and no new account is created). I cannot use the Django Messaging Framework in the save() function, as it requires the request. What can I do?
def save(self, *args, **kwargs):
if self.id:
previous_fields = MyModel(pk=self.id)
if previous_fields.user_account_granted != self.user_account_granted:
title = "Here's your account!"
if previous_fields.user_account_granted == False and self.user_account_granted == True:
user = User(
username=self.first_name + "." + self.last_name,
email=self.email,
first_name=self.first_name,
last_name=self.last_name
)
random_password = User.objects.make_random_password() #this gets hashed on user create
user.set_password(random_password)
user.save()
self.user = user
message = "You have just been given an account! \n\n Here's your account info: \nemail: " + self.user.email + "\npassword: " + random_password
if previous_fields.user_account_granted == True and self.user_account_granted == False:
message = "You no longer have an account. Sorry :( "
try:
sent_success = send_mail(title, message, 'example#email.com', [self.email], fail_silently=False)
if sent_success == 1:
##HERE I WANT TO INDICATE EMAIL SENT SUCCESS TO THE USER'S VIEW AFTER THE FORM IS SUBMITTED
else:
##HERE I WANT TO INDICATE EMAIL SENT FAILURE TO THE USER'S VIEW AFTER THE FORM IS SUBMITTED
user.delete()
self.user_account_granted = False
except:
##HERE I WANT TO INDICATE EMAIL SENT FAILURE TO THE USER'S VIEW AFTER THE FORM IS SUBMITTED
user.delete()
self.user_account_granted = False
super(MyModel, self).save(*args, **kwargs)
You don't send an email in a model's save() function. Ever. That's just not its purpose. Consider:
save() may be called from the shell.
save() may be called from wherever in your project.
The purpose of the save() method is to save the object. End of story.
Now. Let's get back to what you really want to achieve. You are in the process of handling a form submitted by the user. Which means you're dealing with at least to other things here: the form and the view.
Let's have a closer look at what their purpose is:
The Form's basic role is to encapsulate data input and validation. It can be extended so as to encompass the full role of a Command. After all, it only misses an execute() function.
The View's basic role is to take action based on the browser's request (here, the POSTing of a form) and trigger the displaying of the result.
You may choose either. You could have an execute() method on your form, and just call that from you view. Or you could have the view take action after checking that the form is_valid(). I would personnaly choose the form for the actual action, and the view for showing the result.
Thus, in my view, I would customize the form_valid() method to call execute() and do whatever is needed depending on the result. That is:
class MyForm(Form):
# whatever you already have there
def clean(self):
# unrelated to your issue, but just reminding you
# this is the place you make sure everything is right
# This very method MUST raise an error if any input or any
# other condition already known at that point is not fulfilled.
if you_do_not_want_to_grand_an_account:
raise ValidationError('Account not granted, because.')
return self.cleaned_data
def execute(self):
do_whatever()
needs_to_be_done()
if it_failed:
raise AnAppropriateError(_('Descriptive message'))
class MyView(FormView):
form = MyForm
# your other view stuff here
def form_valid(self, form):
try:
form.execute()
except AnAppropriateError as err:
messages.add_message(self.request, messages.ERROR, err.message)
else:
messages.add_message(self.request, messages.INFO, _('It worked'))
AnAppropriateError may just be RuntimeError if you want it quick and dirty, but you should define your own exception class.
Also, you may want to decorate the execute()̀ method with#transaction.atomic()`, depending on its content.
As a final note, remember you cannot be sure the email was actually sent. It's common to have mail systems that accept emails even if there is some error. You'll only know when it bounces, several days later.
First of all it is really not a good idea to send email from model classes. I highly object that. It is better to do that in your form
But you have already implemented that.
There is a way you can access the request inside a model, I wouldn't recommend it though. There is a plugin called CRequest Middlewar, https://pypi.python.org/pypi/django-crequest
You can use this middleware to access the request anywhere new like.
$ python pip install django-crequest
First import the crequest’s middleware:
from crequest.middleware import CrequestMiddleware
Get the current request ;):
current_request = CrequestMiddleware.get_request()
Done. Now you can use the message framework as you wish in that model.