如何在Django中重新定义函数?

时间:2016-07-12 14:26:06

标签: python django celery

我有一个'核心' Django产品包含常见任务的默认实现,但我想允许重新定义该实现(或者如果这样可以更容易定制)。

例如,在核心产品中,我可能会有一个视图,允许用户点击按钮重新发送所有通知':

{
"fonts.fontSize": "14px",
"fonts.fontFamily": "'SourceCodePro-Medium', MS ゴシック, 'MS Gothic', monospace",
"themes.theme": "daugther.of.obsidian",
"noDistractions": false,
"wordWrap": true,

"BracketsNewProjectExtension.newProjectsFolder": "C:/Users/Family/Desktop/team2019site", 

"closeBrackets": false,

"closeTags": {
    "whenOpening": false,
    "whenClosing": false,
   } 
}

然后在本产品的某些部署中,可能会重新发送'重新发送通知。需要看起来像:

# in core/views.py
... imports etc...
from core.tasks import resend_notifications

def handle_user_resend_request(request, user_id):
    user = get_object_or_404(id=user_id)

    if request.method == 'POST':
        for follower in user.followers:
            resend_notifications(follower.id)

    ... etc etc ...


# in core/tasks.py
... imports etc...

def resend_notifications(id):
    send_email(User.objects.get(id=id))

如何将# in customer_specific/tasks.py ... imports etc ... def resend_notifications(id): person = User.objects.get(id=id) if '@super-hack.email.com' in person.email: # This is not a real email, send via the magic portal send_via_magic(person) else: send_email(person) # and send via fax for good measure send_fax(person) 文件中的resend_notifications函数指向customer_specific版本?

我应该在Django配置中定义这个并以这种方式共享访问吗?如果任务实际上是Celery任务怎么办?

NB:我所拥有的任务实际上被定义为Celery任务(我删除了这个额外的细节,因为我认为这个问题更为通用)。我尝试过一个改变全局对象的自定义装饰器标签,但由于种种原因,这绝对不是一种方法。

PS:我觉得这是一个依赖注入问题,但这在Django中并不常见。

2 个答案:

答案 0 :(得分:0)

在类似的情况下,我最终选择了这样的解决方案 - 我把它放在应用程序中的methodName(..., (data) => { if (!data) { console.log("data is valid"); } else { console.log(data.dataValidation); } }); 模型上(相当于GitHub组织)。

Organization

如果经过身份验证的用户所属的组织已配置LDAP,我本质上想要使用不同的表单类 - 因此创建/邀请用户表单需要不同。

然后我在相应的视图中覆盖了@property def forms(self): if self.ldap: from portal.ldap import forms else: from portal.users import forms return forms ,如下所示:

get_form_class

我想你可能想在你的场景中做类似的事情,将你的函数包装在代理抽象中,以确定使用哪个版本 - 基于环境变量,设置或请求。

答案 1 :(得分:0)

这最终通过可以由部署配置重新配置的Django设置对象来解决。它在很大程度上受到这里技术的启发:settings.py from django-rest-framework

例如,我的项目中有一个这样的设置文件:

<强> yourproject / settings.py

"""
Settings for <YOUR PROJECT> are all namespaced in the YOUR_PROJECT config option.
For example your project's config file (usually called `settings.py` or 'production.py') might look like this:

YOUR_PROJECT = {
    'PROCESS_TASK': (
        'your_project.tasks.process_task',
    )
}

This module provides the `yourproject_settings` object, that is used
to access settings, checking for user settings first, then falling
back to the defaults.
"""
# This file was effectively borrow from https://github.com/tomchristie/django-rest-framework/blob/8385ae42c06b8e68a714cb67b7f0766afe316883/rest_framework/settings.py

from __future__ import unicode_literals
from django.conf import settings
from django.utils.module_loading import import_string


DEFAULTS = {
    'RESEND_NOTIFICATIONS_TASK': 'core.tasks.resend_notifications',
}


# List of settings that may be in string import notation.
IMPORT_STRINGS = (
    'RESEND_NOTIFICATIONS_TASK',
)


MANDATORY_SETTINGS = (
    'RESEND_NOTIFICATIONS_TASK',
)


def perform_import(val, setting_name):
    """
    If the given setting is a string import notation,
    then perform the necessary import or imports.
    """
    if val is None:
        return None
    if callable(val):
        return val
    if isinstance(val, (list, tuple)):
        return [perform_import(item, setting_name) for item in val]

    try:
        return import_string(val)
    except (ImportError, AttributeError) as e:
        msg = "Could not import '%s' for setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
        raise ImportError(msg)


class YourProjectSettings(object):
    """
    A settings object, that allows settings to be accessed as properties.
    For example:

        from your_project.settings import yourproject_settings as the_settings
        print(the_settings.RESEND_NOTIFICATIONS_TASK)

    Any setting with string import paths will be automatically resolved
    and return the class, rather than the string literal.
    """
    namespace = 'YOUR_PROJECT'

    def __init__(self, mandatory=None, defaults=None, import_strings=None):
        self.mandatory = mandatory or MANDATORY_SETTINGS
        self.defaults = defaults or DEFAULTS
        self.import_strings = import_strings or IMPORT_STRINGS

        self.__check_settings()

    @property
    def user_settings(self):
        if not hasattr(self, '_user_settings'):
            self._user_settings = getattr(settings, self.__class__.namespace, {})
        return self._user_settings

    def __getattr__(self, attr):
        if attr not in self.defaults and attr not in self.mandatory:
            raise AttributeError("Invalid Pyrite setting: '%s'" % attr)

        try:
            # Check if present in user settings
            val = self.user_settings[attr]
        except KeyError:
            # Fall back to defaults
            val = self.defaults[attr]

        # Coerce import strings into classes
        if attr in self.import_strings:
            val = perform_import(val, attr)

        # Cache the result
        setattr(self, attr, val)
        return val

    def __check_settings(self):
        for setting in self.mandatory:
            if setting not in self.user_settings:
                raise RuntimeError(
                    'The "{}" setting is required as part of the configuration for "{}", but has not been supplied.'.format(
                    setting, self.__class__.namespace))


yourproject_settings = YourProjectSettings(MANDATORY_SETTINGS, DEFAULTS, IMPORT_STRINGS)

这允许我:

  • 使用默认值(即&#39; core.tasks.resend_notications&#39;); OR
  • 要在我的配置文件中重新定义绑定:

    <强> site_config / special.py

    ... other django settings like DB / DEBUG / Static files etc
    
    YOUR_PROJECT = {
        'RESEND_NOTIFICATIONS_TASK': 'customer_specific.tasks.resend_notifications',
    }
    
    ... etc. ...
    

然后在我的视图功能中,我通过以下设置访问正确的功能:

<强>芯/ views.py

... imports etc...
from yourproject.settings import yourproject_settings as my_settings

def handle_user_resend_request(request, user_id):
    user = get_object_or_404(id=user_id)

    if request.method == 'POST':
        for follower in user.followers:
            my_settings.RESEND_NOTIFICATIONS_TASK(follower.id)

    ... etc etc ...