9
# settings.py
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'

# view.py
from django.core.mail import send_mail

def send_letter(request):
    the_text = 'this is a test of a really long line that has more words that could possibly fit in a single column of text.'
    send_mail('some_subject', the_text, 'me@test.com', ['me@test.com'])

The Django view code above, results in a text file that contains a broken line:

this is a test of a really long line that has more words that could possibl=
y fit in a single column of text.
-------------------------------------------------------------------------------

Anyone know how to change it so the output file doesn't have linebreaks? Is there some setting in Django that controls this? Version 1.2 of Django.

Update - to back up a level and explain my original problem :) I'm using the django-registration app, which sends an email with an account activation link. This link is a long URL, with a random token at the end (30+ characters), and as a result, the line is breaking in the middle of the token.

In case the problem was using the Django's filebased EmailBackend, I switched to the smtp backend and ran the built-in Python smtpd server, in debugging mode. This dumped my email to the console, where it was still broken.

I'm sure django-registration is working, with zillions of people using it :) So it must be something I've done wrong or mis-configured. I just have no clue what.

Update 2 - according to a post in a Django list, it's really the underlying Python email.MIMEText object, which, if correct, only pushes the problem back a little more. It still doesn't tell me how to fix it. Looking at the docs, I don't see anything that even mentions line-wrapping.

Update 3 (sigh) - I've ruled out it being a MIMEText object problem. I used a pure Python program and the smtplib/MIMEText to create and send a test email, and it worked fine. It also used a charset = "us-ascii", which someone suggested was the only charset to not wrap text in MIMEText objects. I don't know if that's correct or not, but I did look more closely at my Django email output, and it has a charset of "utf-8".

Could the wrong charset be the problem? And if so, how do I change it in Django?

Here's the entire output stream from Django's email:

---------- MESSAGE FOLLOWS ----------
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Subject: some_subject
From: me@test.com
To: me@test.com
Date: Tue, 17 May 2011 19:58:16 -0000

this is a test of a really long line that has more words that could possibl=
y fit in a single column of text.
------------ END MESSAGE ------------
John C
  • 6,285
  • 12
  • 45
  • 69
  • Note - just upgraded to Django 1.3, problem still there. (Also using Python 2.6) – John C Jun 09 '11 at 01:17
  • When i test with smtp my email comes with a url unbroken. with the filebased it is broken. – James Khoury Jun 09 '11 at 03:44
  • something to try: http://almaer.com/blog/avoiding-broken-links-in-email surround your url with `<` and `>` (i think it is an email client thing) – James Khoury Jun 09 '11 at 04:07
  • @James, brackets sounds good - but just tried it, no change, unfortunately. – John C Jun 09 '11 at 12:51
  • 1
    And as an FYI, although it would be nice to get email working *any way* in Django - the *best* way would be one that allowed me to still use `django-registration's` standard behavior, rather than having to re-write an email view. It would be nice to understand *why* it's breaking - surely I'm not the first person to use `django-registration`? :) It must be working for other people. – John C Jun 09 '11 at 12:53
  • 1
    **Update** - just to clarify, the second part of @ryanshow's answer gave the solution. Once I configured a *real* email client on my VPS, it displayed the messages properly. My mistake was in assuming that Django was generating a broken file, when it was really Python's debugging email server that was breaking the files. (Well, it *is* only for debugging...). Wups. – John C Jan 25 '12 at 16:02

4 Answers4

6

You might be able to get your email client to not break on the 78 character soft limit by creating an EmailMessage object and passing in headers={'format': 'flowed'} Like so:

from django.core.mail import EmailMessage

def send_letter(request):
    the_text = 'this is a test of a really long line that has more words that could possibly fit in a single column of text.'
    email = EmailMessage(
        subject='some_subject', 
        body=the_text, 
        from_email='me@test.com', 
        to=['me@test.com'],
        headers={'format': 'flowed'})

    email.send()

If this doesn't work, try using a non-debug smtp setup to send the file to an actual email client that renders the email according to rules defined in the email header.

ryanshow
  • 552
  • 3
  • 9
  • I tried using `headers`, no effect. I'm not sure what you mean by non-debug smtp setup - I did turn off debugging in Django, no change. – John C Jun 09 '11 at 13:28
  • Ah, I just realized you meant smtp-based versus file-based backend. Well, I am running Windows XP, and don't have a *real* email server, but I did run the debugging server that is built in to Python's smtplib - `python -m smtpd -n -c DebuggingServer localhost:1025`. From the docs, it just prints the output to console, so I would think it doesn't change any formatting. In any case, it didn't affect the problem. – John C Jun 09 '11 at 14:05
  • I did finally setup a Python smtp object that could login to my ISP, for sending email - and it worked properly. I'm still trying to get `django-registration` to send email to my ISP, but so far, it looks like that is the answer - just a problem with using a local, simplistic email server. – John C Jun 09 '11 at 18:12
  • I don't think it's as much an issue with the email server as it is with the way the email is being rendered (passing the email through stdout is not going to render it properly). Also, django-registration should send the email using the default email settings that you define in your settings.py. Just set `EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'` and then make sure you [define the proper email server settings](https://docs.djangoproject.com/en/dev/ref/settings/#email-host) to your ISP's smtp server. You don't even need to have python host a local smtp server. – ryanshow Jun 09 '11 at 21:53
  • But if I was using the `FileBackend`, would the email go through stdout? Seems like it would be written as-is to the file. In any case, I did finally get `django-registration` activation-email working. Part of my problem was *not* defining the proper settings - in particular, `HOST_PORT = '587'`, with quotes around the port, is a *really* bad idea :( But hey, it's working, and your post did push me in the right direction, thanks. – John C Jun 09 '11 at 22:22
  • Glad I could help. By mentioning stdout, I was referring to the fact that the email is being displayed in an environment that was not designed to render the email. A typical text editor would have the same issue when loading the email from a file. This is sort of analogous to viewing a html file in stdout or in a text editor. It's not going to look like how you want it because only a viewer that can interpret html (and in this case, an email) will display the formatting correctly. – ryanshow Jun 09 '11 at 23:32
0

I've seen this is python2.5 and it's fixed in python2.7.

The relevant code in email/generator.py now has a comment saying

# Header's got lots of smarts, so use it.  Note that this is
# fundamentally broken though because we lose idempotency when
# the header string is continued with tabs.  It will now be
# continued with spaces.  This was reversedly broken before we
# fixed bug 1974.  Either way, we lose.

You can read about the bug here http://bugs.python.org/issue1974

Or you can just change the '\t' to ' ' in this line of email/generator.py

print >> self._fp, Header(
v, maxlinelen=self._maxheaderlen,
header_name=h, continuation_ws='\t').encode()
Brian
  • 9
  • 1
0

Try to define EMAIL_BACKEND in your settings.py. Maybe it doesn't solve your problem, but is the right place where to define it, otherwise it's likely not going to be used.

(Since I'm not sure I'm solving your problem here, I was trying to make a comment on your, but apparently I cannot.)

ygneo
  • 416
  • 3
  • 10
  • Ah, I did put it in settings.py, I just sort of compressed the post. :) I'll fix it. And yeah, it takes 50 rep to leave comments... – John C May 08 '11 at 22:28
0

The email lines aren't "broken" per se -- they're just represented in the quoted-printable encoding. As such, at 76 characters, =\n is inserted. Any competent mail client ought to decode the message properly and remove the break.

If you want to represent the body of an email decoded, you can use this by passing decode=True to the the get_payload method:

body = email.get_payload(decode=True)

This tells the message to decode the quoted-printable encoding.

More to the point, if your main concern is getting the python console debugging server to print the message decoded, you could do something quick and dirty like this snippet rather than using the built-in DebuggingServer. More properly, you could parse the "data" string as an Email object, print out the headers you care about, then print the body with decode=True.

user85461
  • 6,510
  • 2
  • 34
  • 40
  • Thanks, but I wasn't really concerned with getting the python debugging email-server working, just making the message links work properly in a *real* email server. I mistakenly assumed that the problem was in what *Django* was generating, not how the debugging server was interpreting it. – John C Jan 25 '12 at 15:58