Django can not sent email with attachments - django

I am trying to sent email by using this code. But when I was trying. Either html or email attachment in sent. But not both. If I omit attachment email body is sent. but if I add attachment email body is not sent . Please someone provide you suggestion.
msg = EmailMultiAlternatives(subject=self.mail_subject, from_email=from_mail, to=(to,),
connection=connection)
if isinstance(attachment, list):
for attach_path in attachment:
msg.attach_file(attach_path)
msg.attach_alternative(html_with_context_data, "text/html")
try:
msg.send(fail_silently=True)
except:
pass

With code like this what do you expect?
try:
msg.send(fail_silently=True)
except:
pass
You are telling django to fail silently which means it will not tell you if something goes wrong. And even if you switched off the silent mode, your exception handling is going to make sure that you never ever hear about it!!
Never do this
except:
pass
Always catch specific extensions, and on the rare cases that you need to catch generic exceptions like this, log it.
import traceback
except:
traceback.print_exc()
Then you will see why exactly your attachment is not being sent.
(perhaps this should be a comment but it's far too long for that)

After spending 2 hours I have added only one line. Now I am getting both HTML and attachment.
msg.content_subtype = 'html'

Related

Django forms EmailValidation not working

I have been researching on this issue but it seems there's not a lot of explanation around there covering this.
...
class RangerRegistrationForm(RegistrationFormUniqueEmail):
email = forms.EmailField(label=_("Email Address"), validators=[EmailValidator(whitelist=['gmail.com'])])
...
Here's the part of my script where I check if the user supplies a gmail account. Unfortunately, as long as it's a valid email it will always pass the check.
What am I doing wrong here?
This is NOT a bug in Django (re-read the source code link posted in #catavaran's answer).
A whitelist in this case is not a "block everything except for this domain part" solution. Rather, the whitelist is a domain part that would otherwise be flagged as invalid by Django's EmailValidator.
For example, the default whitelist is set to domain_whitelist = ['localhost']...an otherwise invalid domain_part that is being flagged as being OK for this use case.
To validate the domain part of an email field, you are going to need to write your own clean function. Something like:
class RangerRegistrationForm(forms.Form):
email = forms.EmailField(label=_("Email Address"))
def clean_email(self):
submitted_data = self.cleaned_data['email']
if '#gmail.com' not in submitted_data:
raise forms.ValidationError('You must register using a Gmail address')
return submitted_data
Congratulations! You had found a bug in Django.
Look at this code from the EmailValidator:
if (domain_part not in self.domain_whitelist and
not self.validate_domain_part(domain_part)):
...
If the domain part of the e-mail is valid then checking against the self.domain_whitelist just ignored.

Django testing - fail on sending email

I have a simple function in Django 1.4 that results in a mail being sent. This is put in a try/except, just in case the mailing service might be down (which is an external dependency).
Now, I would like to test this exception. I thought this would be simple, by overriding some email settings (like settings.EMAIL_HOST or settings.EMAIL_BACKEND) but Django test framework does not cause send_mail() to throw an error even if the backend is configured with jibberish...
So the question is: How do I make send_mail() throw an error in my test case?
Thanks!
Answer:
import mock
class MyTestCase(TestCase):
#mock.patch('path.to.your.project.views.send_mail', mock.Mock(side_effect=Exception('Boom!')))
def test_changed_send_mail(self):
I'm not a testing expert, but I think you should use a send_mail mock that raise the exception you want to test.
Probably you could take a look at this stackoverflow question to know more about mocking in Django.
Yes, the test suite does not set up the email system. Unfortunately, I don't know of any way t o test the email system.
You shouldn't really be testing send_mail functions as it is a built in. That said, you can validate the data being passed into send_mail by another function. If you know the domain of expected inputs, you can verify and throw (raise) an exception of your own.
https://docs.djangoproject.com/en/1.4/topics/email/#the-emailmessage-class
This is the more django'y way to send email:
# attempt to send out a welcome email
try :
t = loader.get_template('email_templates/membership/register.html')
c = Context({
'user' : user,
'site' : Site.objects.get(id=settings.SITE_ID)
})
msg = EmailMessage('Welcome to Site', t.render(c), settings.EMAIL_HOST_USER, to=[user.email,])
msg.content_subtype = "html"
msg.send()
except :
messages.info(request, _(u'Our email servers are encountering technical issues, you may not recieve a welcome email.'))
in my settings.py:
import os
EMAIL_HOST_USER = os.environ['SENDGRID_USERNAME']
EMAIL_HOST= 'smtp.sendgrid.net'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_PASSWORD = os.environ['SENDGRID_PASSWORD']
note: SENDGRID_USERNAME and SENDGRID_PASSWORD are added as env variables by a heroku addon, you may have the actual credentials embedded in your settings file which is fine.
so why isnt your email throwing exceptions? https://docs.djangoproject.com/en/1.4/topics/email/#django.core.mail.get_connection
The fail_silently argument controls how the backend should handle errors. If fail_silently is True, exceptions during the email sending process will be silently ignored.
The patch documentation at http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch explains in detail how to go about this.
I noted that the answer indeed was in the question but to help others understand where the catch is, the link above will prove to be quite insightful.
If you want to patch an object reference in class b.py, ensure that your patch call mocks the object reference in b.py rather than in a.py from where the object is imported. This can be a stumbling block for java developers who are getting used to the whole idea of functions being 1st class citizens.

Django Next Parameter Redirect - Adding Additional Parameters

I'm trying to add authentication to my webapp. A user begins the authentication process by going to:
/authorization/v1/new?client_id=X&response_type=Y&redirect_uri=Z
If request.user does not exist, I redirect using the following code:
try:
user_identity = Identity.objects.get(user=request.user)
except:
resource = '%s?next=/authorization/v1/new' % settings.LOGIN_URL
return HttpResponseRedirect(resource)
This takes the user to /login/?next=/authorization/v1/new.
The user logs in successfully, and is redirected back to /authorization/v1/new.
However, now I'm missing the required GET parameters client_id, response_type, and redirect_uri, and this causes the authorization endpoint to throw an error for missing required parameters.
What is the best way to capture the initial GET params, pass them through to login, and attach them to the redirection back to the auth endpoint?
I thought about using sessions or modifying the redirect_uri to attach the other params, but I still need to figure out how to capture a single parameter through this process. I'd like to not have to modify the login code, if possible. Any ideas/suggestions greatly appreciated. Thanks!
If you were using Django's built-in #login_required decorator, I believe this would be taken care of for you as of r2954.
Given that you're not, you just need to replicate the same steps. Just set next to the value of the full path (as Django does in #login_required), like so:
from urllib import quote
...
try:
user_identity = Identity.objects.get(user=request.user)
except Identity.DoesNotExist:
return HttpResponseRedirect('%snext=%s' % (settings.LOGIN_URL, quote(request.get_full_path()))
NB I've also changed your code to avoid your Pokémon exception handling ("Gotta catch 'em all!").

django-paypal ipn works fine but signal is not received

I have this code at the end of my models.py file
from paypal.standard.ipn.signals import payment_was_successful
def confirm_payment(sender, **kwargs):
# it's important to check that the product exists
logging.debug('** CONFIRMED PAYMENT ***') #never reached this point
try:
bfeat = BuyingFeature.objects.get(slug=sender.item_number)
except BuyingFeature.DoesNotExist:
return
# And that someone didn't tamper with the price
if int(bfeat.price) != int(sender.mc_gross):
return
# Check to see if it's an existing customer
try:
customer = User.objects.get(email=sender.payer_email)
except User.DoesNotExist:
customer = User.objects.create(
email=sender.payer_email,
first_name=sender.first_name,
last_name=sender.last_name
)
# Add a new order
CustomerOrder.objects.create(customer=customer, feature=bfeat, quantity=1, paypal_email=sender.payer_email, invoice=sender.invoice, remarks='')
payment_was_successful.connect(confirm_payment)
The whole process runs ok. Payment is complete. return_url and cancel_url work fine. notify_url was tested from the paypal sandbox's test tools and works ok. However, signal is never received.
Signal code is placed at the end of the models.py and django-paypal code is placed inside my project's directory.
(code was 'stolen' from here)
I must be doing something completely wrong. Any help would be appreciated!
In django-paypal there are two signals for basic transactions:
payment_was_successful
payment_was_flagged
You must handle both signals.
I had this problem - and having chased around a few similar questions have found a resolution for my specific case. I mention it here in case anyone else is hitting this wall.
I've not researched it thoroughly, but it looks as though it's highly dependent on which version/repository you source your copy of django-paypal from. Specifically, the version I downloaded wasn't updated to accommodate the {% csrf_token %} idiom. To get this to work, I had to add the #csrf_exempt decorator to two views:
the ipn view in paypal.standard.views
the view loaded by the return url in my django paypal dictionary (... this one flags a highly accurate error if you have debug on).
Is django-paypal there in the settings.INSTALLED_APPS?
I don't see any other reason why the signal wouldn't be fired, otherwise.

how show personalized error with get_object_or_404

I would like to know how to show personalized errors with the get_object_or_404 method. I don't want the normal Http404 pages, but I want to display a custom message with the message: the result is none.
Thanks :)
The get_object_or_404() is essentially a simple 5-line function. Unless you have some specific reason for using it, just do:
try:
instance = YourModel.objects.get(pk=something)
except YourModel.DoesNotExist:
return render_to_response('a_template_with_your_error_message.html')
If for whatever reason you have to use get_object_or_404(), you can try putting it in a try: ... except Http404: ... block, but I honestly can't think of a plausible reason for that.
As stated by michael, when using get_object_or_404 you cannot customize the message given on http 404. The message provided in DEBUG does offer information about the exception however: "No MyModel matches the given query."
Check out the doc on this. There are three arguments: Model, *args, and **kwargs. The last two are used to build an argument for either get() or filter() on the Model.
The reason I wrote, however, is to address the question of why we would want to use a helper function such as get_object_or_404() instead of, for example, catching it with an exception like Model.DoesNotExist.
The later solution couples the view layer to the model layer. In an effort to relax this coupling we can take advantage of the controlled coupling offered in the django.shortcuts module[1].
And why exactly aren't you using your server's capeability to do just that?
get_object_or_404() is redirecting to the default 404 page right?
If you are on debug mode you won't see it but when deployed django will just refer to the server's 404 html page.
That can't be done with that shortcut. It will only raise a Http404 exception. Your best bet is a try catch if you want full control. Eg.
try:
obj = Model.objects.get(pk = foo)
except:
return HttpResponseRedirect('/no/foo/for/you')
#or
return render_to_response ...