Reputation: 1766
I'm trying to use an HTML template for django mail module. MY current issue is that I'm getting this error:
django.template.exceptions.TemplateDoesNotExist
when I try to render HTML inside my app called users
:
html = render_to_string('email/email_confirm.html', context)
Here is my folder layout, my app is called users, my project settings live in /core
. My templates are located in BASE_DIR.
Here is my templates code in settings:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'social_django.context_processors.backends',
'social_django.context_processors.login_redirect',
],
},
},
]
How can I get Django to properly find the templates folder? I have all my apps hooked up and data is working fine. This is strictly a templates path issue.
EDIT:
I have kept my APP_DIRS
= True and moved the templates/email folder inside the users application folder.
Still django is not finding the template?
Here is the View.py in question:
class CustomUserCreate(APIView):
permission_classes = [AllowAny]
def post(self, request, format='json'):
serializer = CustomUserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
# GENERATE EMAIL CONFIRMATION TOKEN
user_data = serializer.data
user = User.objects.get(email=user_data['email'])
token = RefreshToken.for_user(user).access_token
# GENERATE EMAIL CONFIRMATION TEMPLATE
current_site = get_current_site(request).domain
relative_link = reverse('users:email-verify')
# CHANGE TO HTTPS in PRODUCTION
absurl = 'http://'+current_site+relative_link+"?token="+str(token)
email_body = 'Hi '+ user.username+', Please use link below to verify your email \n' + absurl
context = {
'name': user.first_name,
}
html = render_to_string('email/email_confirm.html', context)
text = render_to_string(email_body, context)
data = {'to_email':user.email,
'email_subject': 'Verify your email',
'email_body':email_body,
'message':text,
'html_message':html
}
Util.send_email(data)
return Response(user_data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
EDIT 2: First I tried doing this:
template = get_template('email/email_confirm.html', { 'name': user.first_name})
I got TypeError: unhashable type: 'dict'
as an error for the above.
Then I switched it around doing this:
absurl = 'http://'+current_site+relative_link+"?token="+str(token)
email_body = 'Hi '+ user.username+', Please use link below to verify your email \n' + absurl
context = {
'name': user.first_name,
}
template = get_template('email/email_confirm.html')
email = render_to_string(template, context)
data = {'to_email':user.email,
'email_subject': 'Verify your email',
'email_body':email_body,
'html_message':email
}
Util.send_email(data)
Which lead to this error:
raise TypeError(f'{funcname}() argument must be str, bytes, or '
TypeError: join() argument must be str, bytes, or os.PathLike object, not 'Template'
final edit:
data = {'to_email':user.email,
'email_subject': 'Please Verify Your Email',
'email_body':email_body,
'html_message':html
}
Upvotes: 5
Views: 1933
Reputation: 31
did you tried EmailMultiAlternatives ?
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
def sendMail():
ctx = {
#your context here..
}
html_content = render_to_string('path/to_email.html', context=ctx)
text_content = strip_tags(html_content)
email = EmailMultiAlternatives(
subject= "Activate your Account",
body=text_content,
from_email=settings.EMAIL_HOST_USER,
to=[to_email]
)
email.attach_alternative(html_content,"text/html")
res = email.send()
Upvotes: 2
Reputation: 6052
Note that the arguments to render_to_string
are a path to a template, and a context dictionary. Fundamentally the issue is ensuring you are passing a template path to render_to_string
every time you call it.
In your first post, the issue is not with the line that you've highlighted
(html = render_to_string('email/email_confirm.html', context)
), that line is actually totally fine and not the source of your errors. Rather the following line where the first argument to render_to_string
is a string of the email body: text = render_to_string(email_body, context)
. There is no rendering to be done here since email_body
is already a string of content. You can delete that line entirely and use email_body
instead of text
elsewhere in your code.
Alternatively you can create a new template for the text body of the email (maybe email/email_confirm.txt
) and render that instead of using string concatenation to create email_body
.
In the edits, this sequence is problematic:
template = get_template('email/email_confirm.html')
email = render_to_string(template, context)
since render_to_string
takes a path to a template, not the template itself. The fact that get_template
worked here is just an illustration that your template settings are working fine and that email = render_to_string('email/email_confirm.html', context)
was never the issue in the first place and was a bit of a red herring. Once you solve the issue with the text = render_to_string(email_body, context)
line the original code to create the html
variable is fine.
Upvotes: 2