ジャンゴとタイムゾーン

ケース、数字、タイムゾーン、そして夏/冬時間への移行がひどく、私たちの兄弟の血をだめにする日常的なものがいくつかあります。 意図せずに、あなたは中国全体でタイムゾーンが1つしかない中国人をうらやましく、目に見える死はありません。 少なくともDjangoアプリケーションの場合、タイムゾーンとそれらの間の変換を一度にすべて処理することは非常に便利です。



Python自体では、これは悪くありません。優れたpytzモジュールがあり、組み込みのdatetimeオブジェクトはタイムゾーンで正しく動作します。 事は小さい-便利なハーネスを実装するために。 最初に頭に浮かぶのは、localtimeテンプレートフィルターを作成し、次のように呼び出すことです。



{{ comment.time_added | localtime }}

{{ comment.time_added | localtime | date :"dmY" }}







しかし、すぐにいくつかの問題が発生します。 第一に、フィルター関数はコンテキストを受け入れません。つまり、どのタイムゾーンにつながるべきかを決定することはできません。 次に、現在のタイムゾーンはさまざまな方法で理解できます。選択した都市またはIPによって決定される現在のユーザーの設定から取得します。 つまり 現在のローカルタイムゾーンはアプリケーションによって異なるため、このようなフィルターは常に書き換える必要があります。 そして第三に、これらのパラメーターはすべてコンテキストに渡す必要があります。



最初の問題は、フィルターの代わりにタグを使用することで解決されます(コンテキストを取得できます)。



{% localtime comment.time_added %}

{% localtime comment.time_added as time_added %}{{ time_added | date :"dmY" }}







特に危険でせっかちな美容愛好家はpatchを使用できます。これにより、コンテキストをフィルターに転送することができます。



コンテキストにはまだ問題があります。コンテキストプロセッサを作成するか、標準のdjango.core.context_processors.requestを使用してそのタイムゾーンプロパティをミドルウェアで埋めることができます。



class TimezoneMiddleware ( object ):

"""

request timezone

"""


def process_request ( self , request):

assert hasattr (request, 'session' ), "*.TimezoneMiddleware requires session middleware to be installed."

request . timezone = get_timezone(request)

request . session[ 'timezone' ] = request . timezone

return None







セッションでタイムゾーンをキャッシュしない場合は、セッションミドルウェアへの依存を削除できます。 get_timezone()



関数はアプリケーションに依存し、次のようになります。



def get_timezone (request):

#

if hasattr (request, 'user' ) and request . user . is_authenticated():

profile = request . user . get_profile()

if profile . timezone:

return profile . timezone



#

if request . session . has_key( 'timezone' ):

return request . session[ 'timezone' ]



# IP,

city_id = ip_to_city_id(request . META[ 'REMOTE_ADDR' ])

if city_id:

try :

city = City . objects . get(pk = city_id)

if city . timezone:

return city . timezone

except City . DoesNotExist:

pass



#

return pytz . timezone(settings . FALLBACK_TIMEZONE)







実際には、タグのコードとテンプレートのフィルターを引用して切り捨てることができますが、私のようなプロの怠programmerなプログラマーは、タグまたはローカル時間フィルターを毎回書くのは面倒であると判断し、フォームを発行するときは、フィールドの時間を手動で変換する必要があります逆に、リクエストコンテキストがない場合(たとえば、クラウンで文字を送信する場合)、これは追加のジェスチャなしでは機能しません。さらに、ビューで作業する場合、アラートを常に監視する必要があります。 まあ、勤勉な人は上記のパッチの例のフィルターコードを取り、そのようにすることができ、残りは小さな魔術の準備ができています。



明らかに、日付と時刻を現在のタイムゾーンに自動的に変換する場合は、魔法がなければ実行できません。 フィールドからモデルからすべてのデータを取得します-優れた、サンプリング後および挿入前の時間の変換により、目的の効果を得ることができます。 ただし、フィールドはテンプレートまたはリクエストオブジェクトのコンテキストについて何も知らないため、まったく存在しない場合があります。 明らかに、アクティブなタイムゾーンはグローバルでなければなりません。 同様の状況がdjango.utils.translationでどのように解決されるかを確認し、タイムゾーンに同じことを実装できます。



import pytz

from django.utils.thread_support import currentThread



_active = {}



def default_timezone ():

"""

.



"""


from django.conf import settings

_default_timezone = pytz . timezone(settings . TIME_ZONE)

global default_timezone

default_timezone = lambda : _default_timezone

return _default_timezone



def activate (tz):

if isinstance (tz, pytz . tzinfo . BaseTzInfo):

_active[currentThread()] = tz

else :

_active[currentThread()] = pytz . timezone(tz)



def deactivate ():

global _active

if currentThread() in _active:

del _active[currentThread()]



def get_timezone ():

tz = _active . get(currentThread(), None )

if tz is not None :

return tz

return default_timezone()



def to_active (dt):

tz = get_timezone()

if dt . tzinfo is None :

dt = default_timezone() . localize(dt)

return dt . astimezone(tz)



def to_default (dt):

if dt . tzinfo is None :

return default_timezone() . localize(dt)

else :

return dt . astimezone(default_timezone())







activate()



関数は現在のタイムゾーンを設定し、 deactivate()



はデフォルトのタイムゾーンを返します。 to_default()



およびto_active()



、時間をサーバーベルトまたは現在のベルトに変換します。 独自のモデルフィールドを記述することは残ります。



class TimezoneDateTimeField (models . DateTimeField):

__metaclass__ = models . SubfieldBase



def _to_python ( self , value):

"""

datetime

"""


return super (TimezoneDateTimeField, self ) . to_python(value)



def to_python ( self , value):

"""

datetime.



"""


if value is None :

return value

return timezone . to_active( self . _to_python(value))



def get_db_prep_value ( self , value):

"""



"""


if value is not None :

value = timezone . to_default( self . _to_python(value))

return connection . ops . value_to_db_datetime(value)







そして、例えばTimezoneMiddleware



追加するなど、リクエストごとにアクティブなタイムゾーンを設定します:



class TimezoneMiddleware ( object ):

def process_request ( self , request):

...

timezone . activate(request . timezone)

return None



def process_response ( self , request, response):

timezone . deactivate()

return response







完了です。標準のDateTimeField



をフィールドに置き換えるだけで、時間はテンプレート、フォーム、および管理パネルのどこでも魔法のように変換されます。 もちろん、完璧に制限はありません。ユーザーから受け取った時間にアクティブなタイムゾーンをアタッチするためのフォームフィールドを実装できます。サードパーティのアプリケーションとそのフィールド以外のモデルが使用されている場合は、フィルターを記述できます。 これはスキー休暇に関する 1つのプロジェクトで行ったもので、このコードはすべて書かれています。



これまで読んだすべての人が、多くのDjango作家に馴染みのある実用的な利益または審美的な喜びのいずれかを受け取ったことを願っています。



PS最近リリースされたDjango 1.2では、モデルフィールドのインターフェイスが変更されたため、上記のTimezoneDateTimeField



コードは、更新手順に従って更新する必要があります。



All Articles