Django中用Celery渲染邮件模板时遇TemplateDoesNotExist错误
Django Celery任务渲染模板时出现TemplateDoesNotExist错误
问题场景
开发论坛API时,通过类视图触发Celery异步任务发送邮箱验证邮件,任务内使用render_to_string渲染模板生成邮件内容。直接以普通函数调用任务逻辑时运行正常,但通过delay方法异步执行时,抛出TemplateDoesNotExist错误,且其他Celery任务可正常执行。
相关代码与配置
视图代码
class RequestEmailToConfirmAPIView(GenericAPIView): permission_classes = [IsAuthenticated, EmailIsNotConfirmed] success_message = 'The message has just been delivered.' serializer_class = DummySerializer def get(self, request): current_url = get_current_site(request, path='email-confirmation-result') user = request.user token = EmailConfirmationToken.objects.create(user=user) send_confirmation_email.delay( template_name='email/confirm_email.txt', current_url=current_url, email=user.email, token_id=token.id, user_id=user.id ) return Response(data={"message": self.success_message}, status=status.HTTP_201_CREATED)
Celery任务代码
@shared_task def send_confirmation_email(template_name: str, current_url: str, email: str, token_id: int, user_id: int): """发送验证邮件""" data = { 'current_site': str(current_url), 'token_id': str(token_id), 'user_id': str(user_id) } message = render_to_string(template_name, context=data) send_mail( subject='Пожалуйста, подтвердите почту', message=message, from_email='admin@ourweb.com', recipient_list=[email], fail_silently=True )
项目目录结构
├── backend │ ├── celery_app.py │ ├── core │ │ └── settings.py │ ├── forum │ │ └── views.py │ ├── templates │ │ └── email │ │ ├── confirm_email.txt │ │ └── ...其他模板 │ └── web.env ├── docker-compose.yml └── ...其他文件
Celery配置(celery_app.py)
from celery import Celery from django.conf import settings import os os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') app = Celery('backend.core') app.config_from_object('django.conf:settings') app.conf.broker_url = settings.CELERY_BROKER_URL app.autodiscover_tasks()
Docker Compose中Celery Worker配置
celery-worker: restart: always build: context: ./backend entrypoint: /bin/sh command: -c "export PYTHONPATH=/backend:/backend/backend && celery -A backend.celery_app:app worker --loglevel=info" volumes: - .:/backend depends_on: - db - redis - backend env_file: - backend/web.env
错误栈
celery-worker | django.template.exceptions.TemplateDoesNotExist: email/confirm_email.txt celery-worker | Traceback (most recent call last): celery-worker | File "/usr/local/lib/python3.11/site-packages/celery/app/trace.py", line 451, in trace_task celery-worker | R = retval = fun(*args, **kwargs) celery-worker | File "/backend/backend/accounts/tasks.py", line 19, in send_confirmation_email celery-worker | message = render_to_string(template_name, context=data) celery-worker | File "/usr/local/lib/python3.11/site-packages/django/template/loader.py", line 61, in render_to_string celery-worker | template = get_template(template_name, using=using) celery-worker | File "/usr/local/lib/python3.11/site-packages/django/template/loader.py", line 19, in get_template celery-worker | raise TemplateDoesNotExist(template_name, chain=chain)
排查与解决步骤
1. 验证Django模板路径配置
检查core/settings.py中的TEMPLATES配置,确保DIRS字段正确指向模板目录:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'], # 确保此处指向backend/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', ], }, }, ]
2. 修复Celery Worker的工作路径与启动命令
当前Worker的PYTHONPATH设置可能导致路径混乱,修改docker-compose.yml中的Worker配置:
celery-worker: restart: always build: context: ./backend command: celery -A celery_app:app worker --loglevel=info working_dir: /backend # 明确指定工作目录为backend根目录 volumes: - ./backend/:/backend # 确保挂载目录正确 depends_on: - db - redis - backend env_file: - backend/web.env
3. 确保Celery Worker初始化Django环境
在任务中显式初始化Django环境(避免Worker未自动加载配置):
@shared_task def send_confirmation_email(template_name: str, current_url: str, email: str, token_id: int, user_id: int): import django from django.conf import settings if not settings.configured: django.setup() # 原有逻辑 data = { 'current_site': str(current_url), 'token_id': str(token_id), 'user_id': str(user_id) } message = render_to_string(template_name, context=data) send_mail( subject='Пожалуйста, подтвердите почту', message=message, from_email='admin@ourweb.com', recipient_list=[email], fail_silently=True )
4. 备选方案:在视图中预渲染模板
若以上方法无效,可在视图中先完成模板渲染,再将渲染后的字符串传递给Celery任务:
# 视图中修改 from django.template.loader import render_to_string def get(self, request): current_url = get_current_site(request, path='email-confirmation-result') user = request.user token = EmailConfirmationToken.objects.create(user=user) # 提前渲染模板 message = render_to_string( 'email/confirm_email.txt', context={ 'current_site': str(current_url), 'token_id': str(token.id), 'user_id': str(user.id) } ) send_confirmation_email.delay(message=message, email=user.email) return Response(data={"message": self.success_message}, status=status.HTTP_201_CREATED)
修改任务:
@shared_task def send_confirmation_email(message: str, email: str): send_mail( subject='Пожалуйста, подтвердите почту', message=message, from_email='admin@ourweb.com', recipient_list=[email], fail_silently=True )
内容的提问来源于stack exchange,提问作者Mihail Bury
相关产品推荐
相关产品推荐

