HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythondjangoMinor

Abstracting celery tasks with class based implementations

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
implementationswithcelerytasksbasedabstractingclass

Problem

Background

I want to send emails with a rendered template (django template, that is) but I also want to be able to control the QuerySets, and context provided.

The goal is to make a task whose run method is the same (because sending an email isn't the interesting bit) for each implementation but allow the user to provide data and templates as a configuration for what the run method should feed into the template and mailer.

First pass

The first approach was to define a task for accepting serializable data with rules about how to grab objects from the database - if there was only one value then it was taken as a plain value and passed along to the template context. This lead to having tasks that required a lot of knowledge of how this stuff worked so I decided a level of abstraction was appropriate for keeping the code more concise and readable.

Full Code

```
# -- coding: utf-8 --
from __future__ import unicode_literals
import logging

from django.core.mail import EmailMultiAlternatives as EmailMulti
from django.template.loader import render_to_string
from project.celery import app

logger = logging.getLogger(__name__)

class TemplateEmailTask(app.Task):
'''
Abstract base task for sending an email with one or more templates.
Implementors should define
1. get_context_data, a method like Django Views that should provide a
dictionary for passing to the template renderer,
2. a name class attribute to uniquely identify this task to Celery, and
3. a list of template names to use in rendering the email.
An example implementation would be:
class MyEmail(TemplateEmailTask):
name = 'python.module.path.to.tasks.my_email'
template_names = []
def get_context_data(self, **kwargs):
return {
today: timezone.now()
}
It is totally allowed to make database calls in get_context_data (that's
why it was built this way) to provide data to the template.
Note: it is not

Solution

Few things here:

First

PEP257
Dosstrings should use tripple-double-quotes but not tripple-single-quotes as you do.

Second

def make_message(subject=None,
                 text_body=None,
                 from_email='',
                 to_emails=[],
                 html_content=None,
                 reply_to=''):


To avoid some issues, you better use immutable defaults so to_emails should be either tuple or None.

Third

rendered = {suffix: rendered
                for suffix, rendered in
                [self.render(template_name, context)
                 for template_name in self.get_template_names()]}


This compression can be a bit prettified

render = partial(self.render, context=context)
rendered = dict(map(render, self.get_template_names()))

Code Snippets

def make_message(subject=None,
                 text_body=None,
                 from_email='',
                 to_emails=[],
                 html_content=None,
                 reply_to=''):
rendered = {suffix: rendered
                for suffix, rendered in
                [self.render(template_name, context)
                 for template_name in self.get_template_names()]}
render = partial(self.render, context=context)
rendered = dict(map(render, self.get_template_names()))

Context

StackExchange Code Review Q#146528, answer score: 2

Revisions (0)

No revisions yet.