Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django can't find my template directory even though it's configured in settings?

Tags:

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.

enter image description here

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
                }

like image 784
andres Avatar asked Apr 12 '21 22:04

andres


People also ask

Where is template folder in Django?

To configure the Django template system, go to the settings.py file and update the DIRS to the path of the templates folder. Generally, the templates folder is created and kept in the sample directory where manage.py lives. This templates folder contains all the templates you will create in different Django Apps.

How do you solve template does not exist in Django?

Django TemplateDoesNotExist error means simply that the framework can't find the template file. To use the template-loading API, you'll need to tell the framework where you store your templates. The place to do this is in your settings file ( settings.py ) by TEMPLATE_DIRS setting.

How do I inherit a template in Django?

extends tag is used for inheritance of templates in django. One needs to repeat the same code again and again. Using extends we can inherit templates as well as variables.

What does {% %} mean in Django?

{% %} and {{ }} are part of Django templating language. They are used to pass the variables from views to template. {% %} is basically used when you have an expression and are called tags while {{ }} is used to simply access the variable.


2 Answers

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.

like image 158
azundo Avatar answered Sep 21 '22 15:09

azundo


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()
like image 45
Yash Rank Avatar answered Sep 17 '22 15:09

Yash Rank