2

I'm trying to find a way to store a function in a Django Model. My use case is an emailer system with planned and conditional sending.

"send this email tomorrow if John has not logged in".

Ideally I would like to create emails with a test function field.

def my_test(instance):
  if (now() - instance.user.last_login) > timedelta(days=1):
    return False
  return True

Email(send_later_date=now() + timedelta(days=1),
      conditional=my_test)

Here is what I'm thinking about

import importlib

Email(send_later_date=now() + timedelta(days=1),
      conditional='my_app.views.my_test')



class Email(models.Model):
  send_later_date = models.DateTimeField(null=True, blank=True)
  conditional = models.TextField()

  def send_email(self):
    if self.send_later_date < now():
      if not self.conditional:
        function_send_email()
      else:
        function_string = self.conditional
        mod_name, func_name = function_string.rsplit('.',1)
        mod = importlib.import_module(mod_name)
        func = getattr(mod, func_name)
        if func():
          function_send_email()

How would you do that? I was thinking of storing the function name in a Text field. How would you run it once needed? ImportLib seems interesting...

1 Answer 1

2

Storing the function name is a valid aproach, since it is actually the one used by the pickle module. This has the benefit (in your case) that if you update the code of a function, it will automatically apply to existing e-mails (but be careful with backward-compatibility).

For convenience and safety (especially if the function name could come from user inputs), you may want to keep all such functions in the same module, and store only the function name in DB, not the full import path. So you could simply import the function repository and read it with getattr. I imagine you will also want to give parameters to these functions. If you don't want to restrict yourself to a given number / order / type of arguments, you could store in DB a dictionary as a JSON string, and expand it with the ** operator.

import my_app.func_store as store
import json

func = getattr(store, self.conditional)
params = json.loads(self.conditional_params)
if func(**params):
    function_send_email()
Sign up to request clarification or add additional context in comments.

1 Comment

Nice answer. User can't enter any fonction or code, emails are all generated from within the code. so it's quite handy to have the condition function sitting near where the email is generated. I currently have a centralised version of this email app which is a nightmare to maintain. That's why I'm working on this idea.. :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.