Django: dynamic changing email recepients - django

I making a feedback form on website. I made model called 'globalapp' with all settings for future admin, it have email,address and phone fields without permissions for add or delete this objects.
In my views i have a simple code:
def index(request):
seos = SEO.objects.get(id__exact=1)
socs = Social_networks.objects.get(id__exact=1)
globs = globalapp.objects.get(id__exact=1)
index = Index.objects.get(id__exact=1)
form = ContactForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['subject']
sender = form.cleaned_data['sender']
message = form.cleaned_data['message']
fille = form.cleaned_data['fille']
recepients = ['test#test.ru']
from_email, to = sender, recepients
html_content = loader.render_to_string('globalapp/chunks/email_tpl.html',
{'subject': subject, 'sender':sender, 'message':message, 'fille':fille})
msg = EmailMultiAlternatives(subject, html_content, from_email, to)
msg.send()
return render(request, 'globalapp/index.html', {'seos': seos,
'socs': socs,
'globs': globs,
'index': index,
'form': form })
Right now mail sending on test#test.ru. I want to take email field from globalapp object, and put it in 'recepients', to give admin ability to change email address when he needs it.
The best thing that i get yet, i get email value by queryset with:
email = globalapp.objects.filter(id=1).values('email')
by in mail ive got only To: {'email': 'test#gmail.com'}
So question is how to get string from queryset object for dynamic email recepients changing? Or maybe i have option how to do it other way?
Also i have another little problem, that i cant deal with yet: after i push submit button, my page reloading, and i dont need it, can i disable it somehow?

Well there are two problems here:
you use .filter(..), and a filter means you do not get a single dictionary, but a QuerySet of dictionaries. This can be empty, contain one, or multiple elements. Since you filter on id=..., it will contan at most one element, but still it will require some extra logic to unwrap it out of the QuerySet, so we better use .get(..) here; and
we obtain a dictionary, we can retrieve the element associated with the key, by performing an element lookup, so the_dict['email'].
We can tus obtain the email address with:
email = globalapp.objects.values('email').get(id=1)['email']
Or perhaps more elegant:
email = globalapp.objects.values_list('email', flat=True).get(id=1)
Also i have another little problem, that i cant deal with yet: after i push submit button, my page reloading, and i dont need it, can i disable it somehow?
Not with a form, since that is exactly the task the browser is supposed to carry out: send a HTTP request, and load the response. But you can use an AJAX call to perform a HTTP request while the webpage still remains the same.

Related

Email with multiple attachments (from POST)

Although I am using Mailgun as my SMTP gateway, this question specifically relates to Django and forms.
A user sends an email to something#mydomain.com. The subject starts with the text "another".
The email is intercepted by MailGun.
MailGun creates a POST to my code.
My code is supposed to take that email and forward it to another#mydomain.com (the first word of the subject) with the following requirements:
Remove "another" from the subject line.
Set the FROM to the person that sent the email
Retain all of the text (html or plain) from the body of the email.
Retain all of the attachments from the original email.
Sounds simple enough but I'm having a problem with having it send multiple attachments. And, I haven't even tried to figure out #1 and #3. (Yes, I'm kind of new to this.)
Here is what I have so far:
#public
#csrf_exempt
def send_email(request):
if request.method == 'POST':
mail_data = request.FILES
sender = request.POST.get('sender')
subject = request.POST.get('subject', '')
bodyhtml = request.POST.get('body-html', 'i am not found')
mail = EmailMultiAlternatives(subject, bodyhtml, sender, ["another#mydomain.com"])
for field, value in mail_data.items():
mail.attach(value.name, value.read(), value.content_type)
mail.content_subtype = "html"
mail.send()
else:
# Just for my own testing.
return HttpResponse('Not Posted')
return HttpResponse('OK')
Can you help me modify this so that I can attach multiple files? I can probably figure out the other items (but if you have any suggestions that would be appreciated). I also know that anyone can send email and that is a security risk. I'm planning on checking the FROM address before actually sending any email. That should take care of that problem, but, again if you have any suggestions that would be appreciated.
Thanks

Django Overwrite form data saved

I've posted about this problem before, but I still haven't found a solution so I'm hoping I'll have better luck this time.
I have a form that takes inputted data by the user. In another page, I am creating the identical form that the user has populated (pre-filled with that information) for editing purposes. Users will come to this page to EDIT the information they have already put in. My problem is that it isn't overwriting the instance.
def edit(request):
a = request.session.get('a', None)
if a is None:
raise Http404('a was not found')
if request.method == 'POST':
form = Name_Form(request.POST, instance=a)
if form.is_valid():
j = form.save( commit=False )
j.save()
else:
form = Name_Form( instance = a )
For this form, I'm using "unique_together" for some of the values. I'm also calling on `{{ form.non_field_errors }} in the template.
What is happening is when I make changes in the editing view, if the fields changes involves those defined in "unique_together" then an error is returned telling me that the instance already exists. Otherwise it saves a new instance. It isn't OVERWRITING.
Note that the reason i am using unique_together is that I want to prevent users from initially inputting the same form twice (before the editing stage, in the initial inputting view).
Any ideas?
EDIT: note that "a" refers to a session that includes a drop down box of all the available instances. This carried forward will indicate which instance the user wants to edit.
`
Why not do a database lookup of the model your trying to save and pull the fields from the form to the model then save the model?
Instead to store model a in session you should store it on database. Then edit it:
def edit(request, pk):
a = A.objects.get( pk = pk)
...
pk it the a identifier, you can send it to view via urls.py. I encourage to you to use POST/Redirect/GET pattern.
You can add a 'state' field on your model to control workflow (draft, valid)
You should not save objects in the session. If you really need to use a session - save a PK there and retrieve object right before giving it to Form. But the better solution is to send it in GET or POST parameters or included in url. Sessions are unreliable, data inside it can be destroyed between user's requests.
And you can retrieve value from a session in a more pythonic way:
try:
a = request.session['a']
except KeyError:
raise Http404('a was not found')

Control a function (E.g send mail) from a users profile page

I have a profile page like so: http://i.stack.imgur.com/Rx4kg.png . In management I would like a option "Notify by mail" that would control my send_email functions in every application I want. As example I'm using django-messages and it sends private messages aswell as emails when you send a message. I would like for the user to be able to specify if he wants emails aswell when he gets a message.
messages/utils.py
def new_message_email(sender, instance, signal,
subject_prefix=_(u'New Message: %(subject)s'),
template_name="messages/new_message.html",
default_protocol=None,
*args, **kwargs):
"""
This function sends an email and is called via Django's signal framework.
Optional arguments:
``template_name``: the template to use
``subject_prefix``: prefix for the email subject.
``default_protocol``: default protocol in site URL passed to template
"""
if default_protocol is None:
default_protocol = getattr(settings, 'DEFAULT_HTTP_PROTOCOL', 'http')
if 'created' in kwargs and kwargs['created']:
try:
current_domain = Site.objects.get_current().domain
subject = subject_prefix % {'subject': instance.subject}
message = render_to_string(template_name, {
'site_url': '%s://%s' % (default_protocol, current_domain),
'message': instance,
})
if instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
except Exception, e:
#print e
pass #fail silently
Apparently instance.recipient.email is the email for the recipient user. So my questions are: How do I go about creating an option in my profile management that can be used in my new_message_email to check if the user wants emails or not? My own thoughts are that I need to save a value in the database for the user and then check for that value in new_message_email function. How I do that isn't clear though. Do I create a new function in my userprofile/views.py and class in userprofile/forms.py? And have my userprofile/overview.html template change them? Some specifics and thoughts if this is the right approach would help alot!
You probably want to start off by creating a user profile so that you have a good way to store weather or not the user wants these emails sent to them. This is done using the AUTH_PROFILE_MODULE setting in your settings.py.
Once you have the data stored, you should be able to access it from instance.recipient (assuming that instance.recipient is a User object). So you could change your code to:
if instance.recipient.get_profile().wants_emails and instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
Done and done.

Proper technique for a contact form?

I have a contact form at shantiyoga.ca/contact
With the view
def contact(request):
template = 'contact.html'
form = ContactForm(request.POST or None)
if form.is_valid(): # All validation rules pass
subject = form.cleaned_data['subject']
message = "%s\n\n%s" % (form.cleaned_data['message'], form.cleaned_data['sender'])
cc_myself = form.cleaned_data['cc_myself']
recipients = ['contact#shantiyoga.ca']
sender = form.cleaned_data['sender']
if cc_myself:
recipients.append(sender)
headers = {'Reply-To': form.cleaned_data['sender']}
from django.core.mail import send_mail
send_mail(subject,message,sender,recipients,headers)
return redirect('/thanks/')
return render_to_response(template, {'form': form, 'current':'contact'}, context_instance=RequestContext(request))
Which works fairly well. I'm not terribly sophisticated with Django and my Python skills are not quite up to snuff, so please bear with me if I step through this in a basic fashion.
I would like to clarify that there is no way for the form recipient (contact#shantiyoga.ca) to receive the contact form from the value of the email field (user entered). It will always be sent by the authenticated email in my settings.py, which at this point is my personal email?
A user fills out the contact form and hits submit, an email is sent to contact#shantiyoga.ca from my personal email, and if the user decides to cc themself, a copy of the email is sent to them, also from my personal email.
This is not ideal, should I create an email like contactform#shantiyoga.ca for my settings.py to send the email from?
Also, the headers = {'Reply-To': form.cleaned_data['sender']} does not appear to be doing anything and I can't seem to find documentation describing its proper usage, has anyone had success using this technique?
Thank you for your time,
Noah
I would like to clarify that there is no way for the form recipient (contact#shantiyoga.ca) to receive the contact form from the value of the email field (user entered). It will always be sent by the authenticated email in my settings.py, which at this point is my personal email?
You're setting the sender of the email to sender but using EMAIL_HOST, correct? If this is the case, be careful that your SMTP account will let you send email from users other than your domain. Normally, providing you have an authenticated account on that server, you'll be able to set the From: field to whatever you like.
So in short, this email will hit your inbox appearing to be from the sender variable. So when you hit reply, you'll be able to email them back, which is I think what you want. However, it will be sent using the SMTP server whose authentication details you provide. There is a disconnect between being able to send email (SMTP) and being able to receive it, which I think has got you confused.
I've never tried it, but to add extra headers I believe you need to use the EmailMessage object. According to the docs, you would:
e = EmailMessage(headers={'Reply-To':...
e.send()
Be careful doing this; specifically, be careful to strip out newlines in the reply to field. I do not know if the default clean methods would do this.
Finally, there isn't much wrong with your django/python at all. The only thing I'd say, by way of awareness, is not to use this:
return redirect('/thanks/')
but instead:
return HttpResponseRedirect(reverse('myapp.views.method',args=tuple,kwargs=dict))
This gives you the ability to not hardcode urls into your application. The reverse method will look them up from urls.py for you, so you can move your application around/change urls and it will still work.
Here is signature of send_mail from django documentation:
send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None)
As you can see it does not have headers argument.
You will need to use EmailMessage object directly.
It would also be nice to remove email-formatting from view. I would write everything something more like this:
from django.core.mail.message import EmailMessage
from django.conf import settings
class ContactsEmailMessage(EmailMessage):
def __init__(self, sender, subject, message, cc_myself):
super(ContactEmailMessage, self).__init__(
subject=subject,
body=self._get_message(self, sender, message),
from=settings.DEFAULT_FROM_EMAIL,
to=settings.CONTACT_RECIPIENTS,
headers=self._get_headers(sender),
cc=(sender, ) if cc_myself else None
)
def _format_message(self, sender, message):
return "%s\n\n%s" % (sender, message)
def _get_headers(self, sender):
return {
'reply-to': sender
}
Now you can write clean and easy to read view:
from myproject.mail.message import ContactsEmailMessage, BadHeaderError
from django.core.exceptions import SuspiciousOperation
def contact(request):
...
form = ContactForm(request.POST or None)
if form.is_valid(): # All validation rules pass
try:
message = ContactsEmailMessage(**form.cleaned_data)
message.send()
except BadHeaderError:
# django will raise this error if user will try to pass suspicious new-line
# characters to "sender" or other fields. This is safe-guard from
# header injections
raise SuspiciousOperation()
return redirect('/thanks/')
...

Django blog reply system

i'm trying to build a mini reply system, based on the user's posts on a mini blog.
Every post has a link named reply. if one presses reply, the reply form appears, and one edits the reply, and submits the form.The problem is that i don't know how to take the id of the post i want to reply to. In the view, if i use as a parameter one number (as an id of the blog post),it inserts the reply to the database.
But how can i do it by not hardcoding?
The view is:
def save_reply(request):
if request.method == 'POST':
form = ReplyForm(request.POST)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.creator = request.user
new_post = New(1) #it works only hardcoded
new_obj.reply_to = new_post
new_obj.save()
return HttpResponseRedirect('.')
else:
form = ReplyForm()
return render_to_response('replies/replies.html', {
'form': form,
},
context_instance=RequestContext(request))
i have in forms.py:
class ReplyForm(ModelForm):
class Meta:
model = Reply
fields = ['reply']
and in models:
class Reply(models.Model):
reply_to = models.ForeignKey(New)
creator = models.ForeignKey(User)
reply = models.CharField(max_length=140,blank=False)
objects = NewManager()
mentioning that New is the micro blog class
thanks
heyy there. i solved the problem,using your advices, but I've created another.
I was thinking that as the reply form is in another page, simply clicking on that reply link ain't gonna help me retain the post id anyhow, because the blog page is gone, after i push thet reply button. So, in my view, i 've created a function that holds the id of the blog post as a parameter. It saves just as it should, no problem, but now my problem is: HOW CAN I PASS A LINK LIKE
url(r'^save_reply/(?P<id>\d+)/$',
save_reply,
name='save_reply'),
(this is what i hold in my urls.py)
to the reply under each post? I mean, until now, my reply link was simply calling the function replies/save_reply(i had Reply) but now, when i have the id as a parameter, how can i put it in my a href = 'what here'?
here is my views.py that works right:
def save_reply(request, id):
if request.method == 'POST':
form = ReplyForm(request.POST)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.creator = request.user
u = New.objects.get(pk=id)
new_obj.reply_to = u
new_obj.save()
return HttpResponseRedirect('.')
else:
form = ReplyForm()
return render_to_response('replies/replies.html', {
'form': form,
},
context_instance=RequestContext(request))
and i'm callin it by typing in my browser:
http://127.0.0.1:8000/replies/save_reply/1/ (for instance)
of course, i've removed my foreign key field, as now it is unnecessarry
Thank you!
You need to have a hidden field in your form to capture the PK of whichever instance of New the comment is related to.
Since you're using a ModelForm, your Reply model already has the ForiegnKey relationship established. You can set the widget type to be hidden, so your users don't see it..
# forms.py
class ReplyForm(ModelForm):
class Meta:
model = Reply
fields = ['reply', 'reply_to']
widgets = {
'reply_to': forms.HiddenInput),
}
When you initialize the ReplyForm, you can populate the reply_to field like form = ReplyForm({'reply_to': new.pk}) - where new is an instance of New
BTW you might consider changing the name of your New model to something like Post. 'New' is a bit hard to talk about, and a little confusing since 'new' usually means something completely different in a programming context.
if one presses reply, the reply form appears,
I think this is the part you need to work on. When the reply form is rendered it needs to have the id of the post being replied to with it (the instance of New). This presumably has to come via the request unless you have some other way of keeping track of it?
Something along the lines of:
def save_reply(request):
...
else:
form = ReplyForm()
form.reply_to = New.objects.get(id=request.REQUEST["post_id"])
Thus you'll need to ensure that the link which causes the form to be rendered includes a 'post_id' param (or similar - presumably you already have an equivalent, used for displaying the post in question?).
Alongside the
widgets = {
'reply_to': forms.HiddenInput),
}
code this should render the form as you need it.
The post id has to be passed all the way along the chain
--post_id-> Render Post --post_id-> Render Reply Form --post_id-> Store Reply