tags:

views:

221

answers:

3

I can't find the bug in this code. I've tried isolating the problem, but it works when I copy the relevant code into a separate file. The problem must be with the surrounding code, but I don't see how it's even relevant. Here's everything:

The problem is with the "Activate Your PROJECT Account" email. It sends me an email with something like this in it:

--===============1413769924==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable

text here

--===============1413769924==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable

text here

Where "text here" is the same for both versions of the email, but it isn't in the actual files, and isn't when I try sending the emails from a different script. Here's the code:

def join_transporter(request):
    form = TransporterJoinForm(request.POST)

    if request.POST and form.is_valid():
        user = User(
            username = form.username.val,
            first_name = form.first_name.val,
            last_name = form.last_name.val,
            email = form.email.val,
            is_active = False,
        )
        user.set_password(form.password1.val)
        user.save()

        Profile.objects.create(
            user = user,
            phone = form.phone.val,
            company_name = form.company_name.val,
            details = Transporter.objects.create(),
            address = Address.objects.create(
                city = form.address.city.val,
                province = form.address.province.val,
                country = form.address.country.val,
                street = form.address.street.val,
                postal_code = form.address.postal.val
            )
        )

        e = send_multipart_email('Activate Your PROJECT Account', 
            'emails/transporter_joined', 
            {'user':user, 'fee': settings.PROJECT_FEE * Decimal('100.00'),'settings':settings},
            [user.email],
            send_immediately=False
        )
        e.attach_file('/home/PROJECT/webapps/media/files/InsuranceLetter.pdf')
        e.send() # <------- PROBLEM IS HERE

        send_multipart_email('Transporter Joined', 
            'emails/staff_transporter_joined', 
            {'trans':user,'settings':settings},
            ['[email protected]','[email protected]']
        )
        messages.info(request, 'Thank you for registering. Please check your email for details on how to activate your account.')
        return redirect('home')





def send_multipart_email(subject, template, data_dict, recipient_list, from_email=settings.DEFAULT_FROM_EMAIL, send_immediately=True):
    if not isinstance(recipient_list, list): recipient_list = [recipient_list]

    d = {'settings':settings} # default context vars
    d.update(data_dict)
    c = Context(d)

    try:
        tt = loader.get_template(template+'.txt')
    except:
        try:
            ht = loader.get_template(template+'.html')
        except:
            raise Exception('Neither `%(tmpl)s.txt` or `%(tmpl)s.html` could be found.' % {'tmpl':template})
        else:
            e = EmailMultiAlternatives(subject, ht.render(c), from_email, recipient_list)
            e.content_subtype = 'html'
    else:
        e = EmailMultiAlternatives(subject, tt.render(c), from_email, recipient_list)

        try:
            ht = loader.get_template(template+'.html')
        except:
            pass
        else:
            e.attach_alternative(ht.render(c), 'text/html')

    if send_immediately:
        e.send()

    return e




[PROJECT@SERVER emails]$ ls
account_activated.txt      shipper_accepted_bid.txt         transporter_awarded_shipment.txt
base.html                  shipper_joined.html              transporter_bid_declined.html
base.txt                   shipper_joined.txt               transporter_bid_declined.txt
forgot_password.html       staff_transporter_joined.html    transporter_joined.html
forgot_password.txt        staff_transporter_joined.txt     transporter_joined.txt
invoice_generated.html     transporter_approved.html        transporter_lost_auction.html
invoice_generated.txt      transporter_auction_closed.html  transporter_lost_auction.txt
shipper_accepted_bid.html  transporter_auction_closed.txt

Ticket: http://code.djangoproject.com/ticket/13364

+3  A: 

I cannot recreate anything like what you are describing (similar to Ramiro who answered the ticket, and cannot recreate the problem either). If I have two templates with same base name, different extensions, different contents, and cut and paste from your code to send an email from a view, I get the different contents appearing in the sent mail (on Gmail and checking the "original").

You say your .html and .txt files are different but it really sounds, based on everything you say, that your .html file has the .txt contents. You've got both the file system and app directories loader listed: which one is supposed to be finding these files? Perhaps there is a stray file in a place where the other one is looking that has the wrong contents?

Some experiments in the shell might help you debug. Load the .html template and see whether it really contains what you think it does. For example:

>>> from django.template import loader
>>> from pprint import pprint
>>> template = 'emails/dun'
>>> ht = loader.get_template(template+'.html')
>>> pprint(ht.nodelist)
[<Text Node: '<p><strong>Mr. '>,
 <Variable Node: user>,
 <Text Node: '</strong>: Pay us $ '>,
 <Variable Node: amt>,
 <Text Node: ' before next Friday.</p>
'>]

Verify the .html version is different from the .txt version:

>>> tt = loader.get_template(template+'.txt')
>>> pprint(tt.nodelist)
[<Variable Node: user>,
 <Text Node: ': This is an important me'>,
 <Variable Node: amt>, 
 <Text Node: '.
'>]

If they are the same when loaded, then you need to look into why the right template file isn't being found or why the one that is found has the wrong contents, and the answer will have nothing to do with the render() code. Only if they are different when loaded and the same upon rendering would looking into render() be necessary.

UPDATE: So based on the question update, the loaded templates are different, in that each extends a differently-named base template. So the next question becomes, are those base templates different? Changing my recreation scenario to match yours, at least so far as the originally-loaded templates extend base templates, I still cannot recreate the problem. I see different templates loaded:

>>> from django.template import loader, Context
>>> from pprint import pprint
>>> template = 'emails/dun'
>>> ht = loader.get_template(template+'.html')
>>> pprint(ht.nodelist)
[<ExtendsNode: extends "emails/base.html">]
>>> tt = loader.get_template(template+'.txt')
>>> pprint(tt.nodelist)
[<ExtendsNode: extends "emails/base.txt">]

That render differently:

>>> c = Context({'user': 'Joe', 'amt': '50.00'})
>>> tt.render(c)
u'\nJoe: This is an important message. You owe us $ 50.00.\n\n'
>>> ht.render(c)
u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt; <html xm
lns="http://www.w3.org/1999/xhtml"&gt;\n&lt;head&gt;\n&lt;title&gt;&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n\n&lt;p&gt;&lt;strong&gt;Mr. Joe</strong>: Pay us $ 5
0.00 before next Friday.</p>\n\n</body>\n</html>\n'

Because the base templates referenced have different contents:

>>> template = 'emails/base'
>>> ht = loader.get_template(template+'.html')
>>> pprint(ht.nodelist)
[<Text Node: '<!DOCTYPE html PUBLIC "-/'>,
 <Block Node: title. Contents: []>,
 <Text Node: '</title>
</head>
<body>
'>,
 <Block Node: content. Contents: []>,
 <Text Node: '
</body>
</html>
'>]
>>> tt = loader.get_template(template+'.txt')
>>> pprint(tt.nodelist)
[<Block Node: content. Contents: []>, <Text Node: '
'>]
>>>
Karen Tracey
It just prints one extends node for each (see update) but they *are* different...
Mark
+1  A: 

get_template() looks for the first matching template in settings.TEMPLATE_LOADERS

TEMPLATE_LOADERS is usually built from filesystem and app_directories

app_directories checks all the INSTALLED_APPS for templates

So, I'd check all your settings.INSTALLED_APPS for templates that have an 'emails' folder, and rename your template, or put the one you want first in the INSTALLED_APPS list.

Robert
Thanks, but that's not it. There's only one `templates` folder, one `emails` folder, and only one copy of each of those templates. It can't possibly be confusing it with the wrong file?
Mark
A: 

FYI, I think I finally figured out that it's a bug with the Django Development Server !! I don't seem to have this problem when I switch it into production and run it on my apache server.

Mark