2016-07-12 8 views
0

私は一般的なタスクのデフォルトの実装を含む 'core' Django製品を持っていますが、その実装を再定義できるようにしたいのですが、カスタマイズするとカスタマイズが容易になります。おそらく '、この製品のいくつかの展開でDjangoで関数を再定義する方法を教えてください。

# 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)) 

その後:中核製品で例えば

、私は、ユーザーが「すべての通知」を再送信するボタンをクリックすることを可能にするビューを持っているかもしれません以下のように見えるようにresend_notificationsニーズ:

# 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) 

にはどうすればcustomer_specificバージョンを指すようにviews.pyファイルにresend_notifications機能を得るのですか?

これをDjangoの設定で定義し、その方法で共有するべきでしょうか?タスクが実際にセロリのタスクの場合はどうなりますか?

NB:私が持っているタスクは実際にはセロリのタスクとして定義されています。私は、グローバルオブジェクトを変更するカスタムデコレータタグを試しましたが、それは間違いなくいくつかの理由のために行く方法ではありません。

PS:これは依存性注入の質問ですが、これはDjangoではよくあることではありません。

答えて

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) 
    

    これは、どちらかに私を可能にデフォルト値(つまり、 'core.tasks。resend_notications '); 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 ... 
0

同様の状況で私は解決策に行くようになりました。私はこれをアプリケーションのOrganizationモデルに入れました(GitHub組織のequiv)。

@property 
def forms(self): 
    if self.ldap: 
     from portal.ldap import forms 
    else: 
     from portal.users import forms 

    return forms 

私は基本的に、組織が認証されたユーザがLDAPを設定しているに属している場合は、別のフォームクラスを使用していた - ので、異なっていることが必要な作成/招待ユーザーフォーム。

私はそのようにのような適切なビューにget_form_classを上書き:

def get_form_class(self): 
    return self.request.user.organization.forms.CreateUserForm 

私はあなたがどのバージョンに決定し、プロキシの抽象化であなたの関数(複数可)をラップし、あなたのシナリオに似た何かをしたいかもしれないと想像使用する - env値、設定または要求に基づくものである。

+0

これは良い選択であることは間違いありません。しかし、私の場合は、「コア」モジュールと「カスタム」モジュールの両方から、この無関係なアクセスを持つことができる必要があります。カスタムモジュールの明示的な知識を必要とするコアがなくても、これが 'コア'に含まれる方法を考えることはできません。つまり、 'def forms():...'関数を 'core/tasks.py'に入れる必要がありますが、' customer_specific'から何もインポートできません。 – Doddie

+0

うーん、私はあなたの答えをもう数回読んだ。私はあなたが正しいかもしれないと思うが、 'def forms():'スタイル関数はある種の設定/ app configを使う必要がある。 – Doddie

+0

あなたは本質的にPythonモジュールレベルで抽象パターンを使用しようとしています。私がこれまでにやった最も実用的な方法は、一般的に対話するモジュールを書くことでした。 )互換性のあるインターフェース。 – Steve

関連する問題