def test_template_unsupported(self): """A lot of options are not compatible with SendBulkTemplatedEmail""" message = AnymailMessage(template_id="welcome_template", to=['*****@*****.**']) message.subject = "nope, can't change template subject" with self.assertRaisesMessage(AnymailUnsupportedFeature, "overriding template subject"): message.send() message.subject = None message.body = "nope, can't change text body" with self.assertRaisesMessage(AnymailUnsupportedFeature, "overriding template body content"): message.send() message.content_subtype = "html" with self.assertRaisesMessage(AnymailUnsupportedFeature, "overriding template body content"): message.send() message.body = None message.attach("attachment.txt", "this is an attachment", "text/plain") with self.assertRaisesMessage(AnymailUnsupportedFeature, "attachments with template"): message.send() message.attachments = [] message.extra_headers = {"X-Custom": "header"} with self.assertRaisesMessage(AnymailUnsupportedFeature, "extra_headers with template"): message.send() message.extra_headers = {} message.metadata = {"meta": "data"} with self.assertRaisesMessage(AnymailUnsupportedFeature, "metadata with template"): message.send() message.metadata = None message.tags = ["tag 1", "tag 2"] with self.assertRaisesMessage(AnymailUnsupportedFeature, "tags with template"): message.send() message.tags = None
def email( subject, template_name, attachments=[], batch_size=500, bcc=None, merge_data={}, merge_global_data={}, mixed_subtype=None, preheader=None, recipients=[], reply_to=None, ): if not (subject and template_name and recipients): raise NameError() if not isinstance(recipients, list): raise TypeError("recipients must be a list") # bcc is set to False by default. # make sure bcc is in a list form when sent over if bcc not in [False, None] and not isinstance(bcc, list): raise TypeError("recipients must be a list") merge_global_data['subject'] = subject merge_global_data['current_year'] = timezone.now().year merge_global_data['company_name'] = settings.SITE_NAME merge_global_data['site_url'] = settings.SITE_URL merge_global_data['preheader'] = preheader body = render_to_string(f"{template_name}.html", merge_global_data) # If we send values that don't exist in the template, # SendGrid divides by zero, doesn't pass go, does not collect $200. merge_field_format = "*|{}|*" final_merge_global_data = {} for key, val in merge_global_data.items(): if merge_field_format.format(key) in body: final_merge_global_data[key] = "" if val is None else str(val) for recipients_batch in batches(recipients, batch_size): msg = AnymailMessage( subject=subject, body=body, from_email=f"We All Code<{settings.DEFAULT_FROM_EMAIL}>", to=recipients_batch, reply_to=reply_to, merge_data=merge_data, merge_global_data=final_merge_global_data, esp_extra={ 'merge_field_format': merge_field_format, 'categories': [template_name], }, ) msg.content_subtype = "html" if mixed_subtype: msg.mixed_subtype = mixed_subtype for attachment in attachments: msg.attach(attachment) try: msg.send() except Exception as e: logger.error(e) logger.error(msg) raise e for recipient in msg.anymail_status.recipients.keys(): send_attempt = msg.anymail_status.recipients[recipient] if send_attempt.status not in ['queued', 'sent']: logger.error(f"user: {recipient}, {timezone.now()}") from coderdojochi.models import CDCUser user = CDCUser.objects.get(email=recipient) user.is_active = False user.admin_notes = f"User '{send_attempt.reject_reason}' when checked on {timezone.now()}" user.save()
def prepare_email_message(self): """ Returns a django ``EmailMessage`` or ``EmailMultiAlternatives`` object, depending on whether html_message is empty. """ if get_override_recipients(): self.to = get_override_recipients() if self.template is not None: engine = get_template_engine() subject = engine.from_string(self.template.subject).render( self.context) plaintext_message = engine.from_string( self.template.content).render(self.context) multipart_template = engine.from_string(self.template.html_content) html_message = multipart_template.render(self.context) else: subject = smart_str(self.subject) plaintext_message = self.message multipart_template = None html_message = self.html_message connection = connections[self.backend_alias or 'default'] if isinstance(self.headers, dict) or self.expires_at: headers = dict(self.headers or {}) if self.expires_at: headers.update({ 'Expires': self.expires_at.strftime("%a, %-d %b %H:%M:%S %z") }) else: headers = None if html_message: if plaintext_message: msg = AnymailMessage(subject=subject, body=plaintext_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=headers, connection=connection) msg.attach_alternative(html_message, "text/html") else: msg = AnymailMessage(subject=subject, body=html_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=headers, connection=connection) msg.content_subtype = 'html' if hasattr(multipart_template, 'attach_related'): multipart_template.attach_related(msg) else: msg = AnymailMessage(subject=subject, body=plaintext_message, from_email=self.from_email, to=self.to, bcc=self.bcc, cc=self.cc, headers=headers, connection=connection) for attachment in self.attachments.all(): if attachment.headers: mime_part = MIMENonMultipart(*attachment.mimetype.split('/')) mime_part.set_payload(attachment.file.read()) for key, val in attachment.headers.items(): try: mime_part.replace_header(key, val) except KeyError: mime_part.add_header(key, val) msg.attach(mime_part) else: msg.attach(attachment.name, attachment.file.read(), mimetype=attachment.mimetype or None) attachment.file.close() self._cached_email_message = msg return msg