λ΄ μ€μ μ TIME_ZONE = 'Asia/Kolkata'
λ° USE_TZ = True
μ΄ μμ΅λλ€.
νμ κ°λ₯ν APIλ₯Ό μ¬μ©νμ¬ μ κ°μ²΄λ₯Ό μμ±ν λ μ§λ ¬ λ³νκΈ°λ μκ°λλ₯Ό λνλ΄λ +5:30
ννμ΄ μλ datetimeμΌλ‘ μλ‘ μμ±λ κ°μ²΄λ₯Ό νμν©λλ€. λ°μ΄ν°λ² μ΄μ€λ UTCλ‘ μκ°μ μ μ₯ν©λλ€.
μκΈ°μΉ μμ λμμ λμ€μ κ°μ²΄κ° λ€μ μ§λ ¬νλ λ λ μ§/μκ°μ΄ λͺ¨λ UTC νμμ΄κ³ λ€μ Z
κ° λΆλ κ²μ
λλ€. νμ¬ λ° κΈ°λ³Έ μκ°λμ μ₯κ³ λ¬Έμμ λ°λ₯΄λ©΄, λλ μ리μΌμ νμ¬ μκ°λμ λ μ§ μκ°μ λ³ν κΈ°λνλ μ€μ λ κΈ°λ³Έ μκ°λ, κΈ°λ³Έκ°μ TIME_ZONE = 'Asia/Kolkata'
.
λ΄κ° λκ°λ₯Ό λμΉκ³ μμ΅λκΉ?
μ€ν μ€λ²νλ‘μ λν΄ μ§λ¬Έ νκ³ κ²°λ‘ μ datetimeμ΄ μ ν¨μ± κ²μ¬ ν(μ¦, μμ± λλ μ λ°μ΄νΈ ν) κ·Έλλ‘ μ§λ ¬νλκΈ° λλ¬Έμ νμ¬ μκ°λλ μ¬μ©μκ° μ 곡ν datetimeμλ§ μ¬μ©λλ€λ κ²μ λλ€. Django λ¬Έμμμ μ΄κ²μ΄ μλλ λμμ΄λΌκ³ λ―ΏκΈ° μ΄λ ΅μ§λ§ μ΄κ²μ Rest Frameworkκ° μλλΌ Django μ체μ λ¬Έμ μ΄λ―λ‘ μ΄ λ¬Έμ λ₯Ό λ«κ² μ΅λλ€.
μλ νμΈμ @tomchristie ,
μ΄κ²μ DRF λ²κ·ΈλΌκ³ μκ°ν©λλ€.
IMHO, DRFμ μμ°μ€λ¬μ΄ μ¬μ©μ DRFλ₯Ό django ν
νλ¦Ώκ³Ό κ°μ μΆλ ₯μΌλ‘ μ¬μ©νλ κ²μ
λλ€.
django ν
νλ¦Ώμμ μκ°λκ° μ¬λ°λ₯΄κ² νμλλλ° drf μ§λ ¬ λ³νκΈ°κ° django μκ°λ μ€μ μ μ‘΄μ€νμ§ μλ μ΄μ λ 무μμ
λκΉ?
μλ νμΈμ,
DatetimeField
ν΄λμ€μ fields.py
μ½λλ₯Ό μ΄ν΄λ³΄λ©΄ Django μκ°λ μ€μ μ΄ λ΄λΆ κ°( to_internal_value()
)μ λν΄μλ§ μ κ³ λ €λλ€λ κ²μ μμμ΅λλ€.
λ€μκ³Ό κ°μ΄ λͺ¨λΈ μ§λ ¬ λ³νκΈ°λ₯Ό μ¬μ©νλ κ²μ 보μμ΅λλ€.
class MyModelSerializer(ModelSerializer):
class Meta:
model = MyModel
depth = 1
fields = ('some_field', 'my_date_time_field')
my_date_time_field
νλμ κ²½μ° (DateTimeFieldλΌκ³ μκ°ν©λλ€ :)) ννμ μκ°λλ κΈ°λ³Έκ°( to_representation()
)μ κ²½μ° μμμ
λλ€.
μ¦, λ΄κ° νλ¦¬μ§ μμΌλ©΄ Django μκ°λλ κ°μ΄ μ€ν λ¦¬μ§ λ°±μλμ μ°μ¬μ§ λλ§ κ³ λ €λ©λλ€.
IMHO to_representation()
μ λ°ν κ°μ λ€μκ³Ό κ°μμΌ νλ€κ³ μκ°ν©λλ€.
return self.enforce_timezone(value).strftime(output_format)
Django USE_TZ
μ€μ μ λ°λΌ.
μ΄μ λν ν 리νμ€νΈλ₯Ό μμ±ν©λλ€.
μ°¨μ€
λ°λ ν°λ
Έ
@vpistis νμ¬ λ³΄κ³ μλ API λμμ μμ 무μμ λ³Ό κ²μΌλ‘ μμνλμ§(ꡬν μΈ‘λ©΄μ λ¨Όμ λ ΌμνκΈ° 보λ€λ) μλ₯Ό λ€μ΄ μ μν μ μλ€λ©΄ μ΄κ²μ κ²ν νλ κ²μ΄ λ μ¬μΈ κ²μ λλ€.
TIME_ZONE = 'Asia/Kolkata'
νκ³ λ¨μΌ λ μ§/μκ° νλ appointment
μ¬μ©νμ¬ λͺ¨λΈ μ§λ ¬ λ³νκΈ°λ₯Ό λ§λλλ€.
μμ±/μ λ°μ΄νΈ μ€μ λ€μμ 보λ λλ€.
{
"appointment": "2016-12-19T10:00:00"
}
κ·Έλ¦¬κ³ λμμ€λ€:
{
"appointment": "2016-12-19T10:00:00+5:30"
}
κ·Έλ¬λ ν΄λΉ κ°μ²΄λ₯Ό λ€μ κ²μνκ±°λ λμ΄νλ©΄ λ€μμ μ»μ΅λλ€.
{
"appointment": "2016-12-19T04:30:00Z"
}
μμ±νλ λμ Zκ° μ§μ λμ§ μμ κ²½μ° DRFλ ν΄λΌμ΄μΈνΈκ° TIME_ZONE
μ§μ λ μκ°λλ₯Ό μ¬μ©νκ³ μλ€κ³ κ°μ νκ³ ν΄λΉ μκ°λμ νμμ΄ μ§μ λ μκ°μ λ°νν©λλ€(λμ +5:30
λ₯Ό μΆκ°νμ¬ ν΄λΌμ΄μΈνΈκ° μ 곡ν μκ°μΈ κ²½μ° μ€μ λ‘ μ ν¨νμ§ μμ). ν₯ν μ‘μΈμ€κ° ν΄λΉ μκ°λμ νμμ΄ μ§μ λ μκ°μ λ°ννλ κ²½μ° λ μλ―Έκ° μμΌλ―λ‘ κ²μ/λͺ©λ‘ μ€ μλ΅μ΄ μμ±/μ
λ°μ΄νΈ μ€ μλ΅κ³Ό λμΌν©λλ€.
λ€μκ³Ό κ°μ΄ μμ±/μ λ°μ΄νΈ μ€μ νν Zκ° μ 곡λ λ ꡬμ±λ μκ°λμ μκ°μ λ°νν μ§ μ¬λΆμ λν μ§λ¬Έλ μμ΅λλ€.
{
"appointment": "2016-12-19T04:30:00Z"
}
λ³΄κ³ :
{
"appointment": "2016-12-19T10:00:00+5:30"
}
λͺ©λ‘/κ²μμ λν μλ΅κ³Ό μΌκ΄λ μλ΅μ μ μ§νκΈ° λλ¬Έμ μ΄μ λμν©λλ€.
λ λ€λ₯Έ μ΅μ μ μμ±/μ λ°μ΄νΈ μ€μλ νμ UTC μκ°μ λ°ννλ κ²μ΄μ§λ§ λ μ μ©νλ€λ κ²μ μμμ΅λλ€. μ΄λ μͺ½μ΄λ μΌκ΄λ μκ°λλ₯Ό κ°λ κ²μ΄ νμ¬ μ°λ¦¬κ° κ°μ§ 50/50 μν©λ³΄λ€ λ λμ κ²μ λλ€.
TIME_ZONE = 'Asia/Kolkata'λ‘ μ€μ νκ³ λ¨μΌ datetime νλμΈ μ½μμ μ¬μ©νμ¬ λͺ¨λΈ μ§λ ¬ λ³νκΈ°λ₯Ό λ§λλλ€.
μμ±/μ λ°μ΄νΈ μ€μ λ€μμ 보λ λλ€.
{
"μ½μ": "2016-12-19T10:00:00"
}
κ·Έλ¦¬κ³ λμμ€λ€:{
"μ½μ": "2016-12-19T10:00:00+5:30"
}
κ·Έλ¬λ ν΄λΉ κ°μ²΄λ₯Ό λ€μ κ²μνκ±°λ λμ΄νλ©΄ λ€μμ μ»μ΅λλ€.{
"μ½μ": "2016-12-19T04:30:00Z"
}
κ°μ¬ν©λλ€ @jonathan-golorry μ΄κ²μ λ΄κ° μ€μ λ‘ λ³΄λ μ νν λμμ
λλ€.
λλ₯Ό μν΄ νλμ λ€μκ³Ό κ°μμΌν©λλ€ ( @jonathan-golorry μμ μ¬μ© :) ).
κΈ°λ³Έ DATETIME_FORMATμΌλ‘ μμ±/μ λ°μ΄νΈνλ λμ λ€μμ 보λ λλ€.
{
"appointment": "2016-12-19T10:00:00"
}
κ·Έλ¦¬κ³ λμμ€λ€:
{
"appointment": "2016-12-19T10:00:00+5:30"
}
ν΄λΉ κ°μ²΄λ₯Ό λ€μ κ²μνκ±°λ λμ΄νλ©΄ λ€μμ μ»μ΅λλ€.
{
"appointment": "2016-12-19T10:00:00+5:30"
}
IMHOλ μ΄ λμμ κ΄λ¦¬νκΈ° μν DRF μ€μ μ΄μ΄μΌ ν©λλ€(μ: DateTimeFieldκ° κΈ°λ³Έ μκ°λλ₯Ό μ¬μ©νμ¬ νμλλλ‘ κ°μ νλ μ€μ ).
κ°μ¬ν©λλ€ @tomchristie
μλ‘ λ€λ₯Έ νλ μ¬μ΄μ λΆμΌμΉλ μ¬κΈ°μμ μ¬κ°νκΈ° μν ν΅μ¬μ λλ€. λλ κ·Έκ²μ΄ μ¬μ€μΈμ§ λͺ°λμ΅λλ€. μ ν μ¬νμΌλ‘ λ§λ€ μ λ μμ§λ§ κΈ°λ³Έμ μΌλ‘ μ 체 UTCλ₯Ό μ¬μ©ν κ²μΌλ‘ μμν©λλ€.
DRF 3.4.6μ μ¬μ©νλ νλ‘λμ
μΉ μ±μ κ²½μ° λ€μ ν΄κ²° λ°©λ²μΌλ‘ ν΄κ²°νμ΅λλ€.
https://github.com/vpistis/django-rest-framework/commit/be62db9080b19998d4de3a1f651a291d691718f6
λꡬλ μ§ λ€μμ ν¬ν¨νλ ν 리νμ€νΈλ₯Ό μ μΆνκ³ μΆλ€λ©΄:
κ°μ₯ νμν μΌμ λλ€.
ν
μ€νΈν μ½λλ₯Ό μμ±νμ§λ§ drf ν
μ€νΈ μΌμ΄μ€λ₯Ό μ΄λ»κ² μ¬μ©νλμ§ μ λͺ¨λ₯΄κ² μ΅λλ€. λ°νμμ μκ°λ λ° κΈ°ν μ€μ μ λ³κ²½νκΈ° μν΄ django μ€μ μ κ΄λ¦¬νλ λ°©λ²μ λͺ¨λ₯΄κ² μ΅λλ€.
ꡬ체μ μΈ μλ κ°μ΄λλ₯Ό λ§ν¬ν΄ μ£ΌμΈμ.
κ°μ¬ν©λλ€
μ μ ν μ€νΈ μ€μ μ μμ νλ €λ κ²½μ° μ¬κΈ° μ
ν
μ€νΈ μ€μ μ€μ μ μ¬μ μνλ €λ κ²½μ° override_settings
λ°μ½λ μ΄ν°λ₯Ό μ¬μ©ν μ μμ΅λλ€.
μ΄ κΈ°λ₯μ κ°μ¬λ립λλ€. USE_TZ
μ€μ μ΄ μ‘΄μ€λμ΄μΌ νκ³ νμ€ Django λμκ³Ό μ μ¬νκ² κ°μ΄ λ³νλμ΄μΌ νλ€κ³ μκ°ν©λλ€.
μ¬κΈ°μ 첫 λ²μ§Έ λ¨κ³λ νμ¬ ν
μ€νΈ μ€μνΈμ μΆκ°ν μ μλ μ€ν¨ν ν
μ€νΈ μΌμ΄μ€λ₯Ό μμ±νλ κ²μ
λλ€.
λ€μ λ¨κ³λ ν΄λΉ μ¬λ‘μ λν μμ μ μμνλ κ²μ
λλ€. :)
μλ
νμΈμ,
λλ₯Ό μν΄ μ΄κ²μμ΄ κΈ°λ₯μ λν ν
μ€νΈ μΌμ΄μ€μ
λλ€
class TestDateTimeFieldTimeZone(TestCase):
"""
Valid and invalid values for `DateTimeField`.
"""
from django.utils import timezone
valid_inputs = {
'2001-01-01 13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.get_default_timezone()),
'2001-01-01T13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.get_default_timezone()),
'2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.get_default_timezone()),
datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.get_default_timezone()),
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00,
tzinfo=timezone.get_default_timezone()),
# Django 1.4 does not support timezone string parsing.
'2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.get_default_timezone())
}
invalid_inputs = {}
outputs = {
# This is not simple, for now I suppose TIME_ZONE = "Europe/Rome"
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.get_default_timezone()): '2001-01-01T13:00:00+01:00',
datetime.datetime(2001, 1, 1, 13, 00, ): '2001-01-01T13:00:00+01:00',
}
field = serializers.DateTimeField()
λ΄ ν¬ν¬μμ μ¬λ°λ₯Έ μκ°λ λ΄ 3.6.2_tz_fix μκ°μ μ»κΈ° μν΄ λͺ κ°μ§ νΈλ¦μ μ¬μ©ν©λλ€.
μ΄ λμμ΄ λκΈ°λ₯Ό λ°λλλ€ :)
λλ μ΄κ²μ΄ λ«ν μλ€λ κ²μ μμ§λ§ drf 3.6.3μ μ€ν μ€μ΄κ³ λ΄ postgres λ°μ΄ν°λ² μ΄μ€μ μ΄ νμμ€ν¬νκ° "2017-07-12 14:26:00-06"μ΄μ§λ§ μ°νΈ λ°°λ¬λΆλ₯Ό μ¬μ©νμ¬ λ°μ΄ν°λ₯Ό μ»μ λ μ΄ "νμμ€ν¬ν"λ₯Ό μ»μ΅λλ€. : "2017-07-12T20:26:00Z". λλ κ·Έκ²μ΄ -06 μκ°μ μΆκ°λλ κ²μ²λΌ 보μ λλ€.
λ΄ django μ€μ μ tzlocalμ μ¬μ©νμ¬ TIME_ZONE = str(get_localzone())
μκ°λλ₯Ό μ€μ ν©λλ€. λ°λΌμ μμ μ μκ°λκ° μ€μ λ©λλ€.
κΈ°λ³Έ modelSerializerλ₯Ό μ¬μ©νκ³ μμ΅λλ€.
class SnapshotSerializer(serializers.ModelSerializer):
class Meta:
model = Snapshot
resource_name = 'snapshot'
read_only_fields = ('id',)
fields = ('id', 'timestamp', 'snapshot')
λ΄κ° λκ°λ₯Ό λμΉκ³ μμ΅λκΉ?
μμ§ λ«νμ§ μκ³ μ΄λ €μμ΅λλ€ :)
νμλλ λΉ¨κ°μ "λ«ν λ²νΌ"μ μ°Έμ‘° λ¬Έμ μ
λλ€.
...κ·Έλ¦¬κ³ λΉμ λ§μ΄ λ§μ, "λ²κ·Έ"λ μ¬μ ν κ±°κΈ°μ μμ΅λλ€ ;(
μ΄μ νκ° 3.6.3μμ 3.6.4λ‘ λ³κ²½λμμ΅λλ€.
μ€ κ·Έλ. κ°μ¬ν©λλ€!
2017λ
7μ 12μΌ μ€ν 5μ 10λΆ "λ°λ ν°λ
Έ νΌμ€ν°μ€" μλ¦Ό @github.com
μΌλ€:
μμ§ λ«νμ§ μκ³ μ΄λ €μμ΅λλ€ :)
νμλλ λΉ¨κ°μ "λ«ν λ²νΌ"μ μ°Έμ‘° λ¬Έμ μ λλ€.
...κ·Έλ¦¬κ³ λΉμ λ§μ΄ λ§μ, "λ²κ·Έ"λ μ¬μ ν κ±°κΈ°μ μμ΅λλ€ ;(
μ΄μ νκ° 3.6.3μμ 3.6.4λ‘ λ³κ²½λμμ΅λλ€.β
λΉμ μ΄ λκΈμ λ¬μκΈ° λλ¬Έμ μ΄κ²μ λ°λ κ²μ λλ€.
μ΄ μ΄λ©μΌμ μ§μ λ΅μ₯νκ³ GitHubμμ νμΈνμΈμ.
https://github.com/encode/django-rest-framework/issues/3732#issuecomment-314923582 ,
λλ μ€λ λ μμκ±°
https://github.com/notifications/unsubscribe-auth/AIMBTcx-6PPbi_SOqeLCjeWV1Rb59-Ohks5sNVJ0gaJpZM4G0aRE
.
μλ νμΈμ,
μ΄κ²μ΄ μλ μ§λ¬Έκ³Ό μμ ν κ΄λ ¨μ΄ μλμ§ νμ€νμ§ μμ§λ§ DRFλ https://docs.djangoproject.com/en/1.11/ref/utils/#djangoμ λ°λΌ μ€μ λ μκ°λ μ¬μ μλ₯Ό μ‘΄μ€ν΄μΌ ν©λλ€
λ΄ μ¬μ©μκ° μκ°λμ μ°κ²°λμ΄ μκ³ APIκ° μμ§ν λ μ§ μκ°μ μμ νλ μμ€ν
μ΄ μμ΅λλ€. ν΄λΉ λ μ§ μκ°μ νμ¬ μ¬μ©μμ μκ°λλ‘ λ³νν μ μμ κ²μΌλ‘ μμνμ§λ§ ../rest_framework/fields.py
μ΄ κΈ°λ³Έ μκ°λ(μ: django μ€μ νμΌμ μκ°λ)λ₯Ό μ μ©νκ³ μμμ μ μ μμ΅λλ€.
def enforce_timezone(self, value):
field_timezone = getattr(self, 'timezone', self.default_timezone())
if (field_timezone is not None) and not timezone.is_aware(value):
try:
return timezone.make_aware(value, field_timezone)
[...]
def default_timezone(self):
return timezone.get_default_timezone() if settings.USE_TZ else None
λ΄ κ²½μ°μ κ°μ΄ μμ© νλ‘κ·Έλ¨μ΄ μ¬μ μλ₯Ό μ€μ ν κ²½μ° timezone.get_current_timezone()
λ₯Ό κΈ°λ³Έ μ€μ μΌλ‘ μ¬μ©ν΄μΌ ν©λκΉ?
μλ νμΈμ @RichardForshaw λ λ³κ°μ λ¬Έμ μ²λΌ 보μ΄μ§λ§ νμ€ν λμΌν
μμλλ λμμ λ€λ£¨λ μ μ ν ν μ€νΈ μΌμ΄μ€ μΈνΈλ₯Ό μ»μ μ μλ€λ©΄ μ¬κΈ°μμ PRμ λ³Ό κ²μ λλ€.
λ΄ μ²« λ²μ§Έ μκ°μ μλ²μ ꡬμ±λ μκ°λμ μμ‘΄νμ§ μκ³ APIμ μκ°λ μ 보λ₯Ό 보λ΄λλ‘ νλ κ²μ λλ€. κ·Έ μΈμλ ν΄λΌμ΄μΈνΈμ μκ°μ νμ§νν μ€λΉκ° λμ΄ μμ΄μΌ νλ€κ³ μκ°ν©λλ€.
νμ§λ§ μ¬κΈ°μλ ν΄κ²°ν΄μΌ ν λΆμΌμΉκ° μμ΅λλ€. (ν보 νμμ΄λΌκ³ λ§μλλ Έλμ? π)
carltongibson/django-filter#750μ΄ μ¬κΈ°μ κ΄λ ¨λμ΄μΌ ν©λλ€. λλ μλ DRFμ django-filterμ μκ°λ μ²λ¦¬λ₯Ό κΈ°λ°μΌλ‘ νμΌλ―λ‘ 750μ λ³κ²½ μ¬νμ μ¬κΈ°μ μ½κ² μ μ©ν μ μμ΅λλ€.
λ΄ μ΄λ³΄μμκ² λ―Έμνμ§λ§ μ¬κΈ°μ λ¬Έμ κ° μ νν 무μμ λκΉ? λ΄ psql λ°μ΄ν°λ² μ΄μ€μ νμμ€ν¬νκ° μ ννκ³ Djangoκ° μ¬λ°λ₯Έ μκ°λλ₯Ό μ¬μ©νλλ‘ μ€μ λμ΄ μμ΅λλ€. νμ μ€ν¬νλ₯Ό λ³ννμ§ μλλ‘ DRF μ€μ μ΄ μμ΅λκΉ?
μλ νμΈμ @michaelaelise β λ°μ΄ν°μ μλ₯Ό 보면(μλ¨ λΆκ·Ό):
+5:30
).Z
, "Zulu"μ κ²½μ°)λ‘ λμμ΅λλ€.ν΄λΌμ΄μΈνΈκ° μκ°λ λ³νμ μ²λ¦¬νλ€λ κ°μ νμ μ€μ λ¬Έμ
νμ§λ§ μ½κ°μ λΆμΌμΉκ° μμ΅λλ€. νμ€ν 2μ 3μ κ°μ νμμ΄μ΄μΌ ν©λκΉ? (ν΄λΌμ΄μΈνΈκ° μ¬λ°λ₯΄κ² λ μ€ νλλ₯Ό λλ±ν κ°μΌλ‘ μλ½νλλΌλ.)
λλ μ΄κ²μ λ«λ κ²½ν₯μ΄ μμ΅λλ€.
"2016-12-19T10:00:00+5:30"
λ° "2016-12-19T04:30:00Z"
β μ΄λ μ λκΉμ§λ κ·Έλ€μ΄ μ΄λ»κ² λμμ¬μ§ λκ° μ κ²½μ μ°κ² μ΅λκΉ?PRμ λ³΄κ² λμ΄ κΈ°μ©λλ€. κ·Έλ¬λ μ΄κ²μ _κ³΅κ° λ¬Έμ _λ‘ κ³ λ €νκ³ μΆμμ§ μ λͺ¨λ₯΄κ² μ΅λλ€.
μ, μ΄ λ¬Έμ μ€λ λμ μλ κ²μλ¬Όμμ "Z"κ° μλ―Ένλ€λ κ²μ κΉ¨λ«μ§ λͺ»νμ΅λλ€.
κ·Έλμ DRFλ UI/νΈμΆμκ° μ²λ¦¬ν μ μλλ‘ μΈμ λ μ§/μκ°μ UTCλ‘ λ³ννκ³ μμ΅λκΉ?
μ€λͺ
ν΄μ£Όμ
μ κ°μ¬ν©λλ€.
λ§μ§λ§ νκ°μ§.
λ€λ₯Έ μκ°λμ μ₯μΉλ₯Ό ν΄λ§νκΈ° λλ¬Έμ "2016-12-19T10:00:00+5:30"μ΄ λ°νλκΈ°λ₯Ό μνλ©΄ μ΄λ»κ² λ κΉμ?
"RETURN_DATETIME_WITH_TIMEZONE" μ€μ μ΄ μλκΉμ?
μ°λ¦¬λ μμ§ μ₯μΉμμ django/drfλ₯Ό μ¬μ©νκ³ μμ΅λλ€. λ°λΌμ μ½μ λλ λͺ¨λ λ μ§ μκ°μ μμ§ μ₯μΉ μκ°λκ° κ΅¬μ±λκ³ postgres νλκ° ν΄λΉ μ₯μΉ λ μ§ μκ°μ λν΄ νμ μ ννκΈ° λλ¬Έμ μμ§νμ§ μ¬λΆλ₯Ό μ κ²½ μ°μ§ μμ΅λλ€.
νμ¬ μλ리μ€μ ν΄λΌμ°λ μλ²λ κ° μ₯μΉμ μκ°λλ₯Ό μμμΌ νλ©°, μλ§λ django/drfμμ μ²λ¦¬ν ν΄λΌμ°λ μ±μΌλ‘ μμ μ μ΄λνκΈ°λ§ νλ©΄ λ©λλ€.
USE_TZλ₯Ό κ°μ νλ©΄ DRFλ μ΄λ―Έ μκ°λ μ 보μ ν¨κ» λ μ§ μκ°μ λ°ννκ³ μμ΅λλ€. λ°λΌμ μ΄λ―Έ νμν μμ μ μννκ³ μμ΅λλ€.
μ¬κΈ°μ μ μΌν λ¬Έμ λ λμΌν DTκ° ν μκ°λ λλ λ€λ₯Έ μκ°λμμ νμνλλμ§ μ¬λΆμ λλ€. (νμ§λ§ κ·Έλ€μ μ¬μ ν ββκ°μ μκ°μ λλ€.)
@carltongibson
μ¬κΈ°μλ λ Όλ¦¬ μ€λ₯κ° μμ΅λλ€.
μ΄κ²μ κ°μ μκ°μ λλ€. "2016-12-19T10:00:00+5:30" λ° "2016-12-19T04:30:00Z" β μ΄λ μ λ κ·Έλ€μ΄ μ΄λ»κ² λμμ€λ μκ΄μμ΅λκΉ?
IMHO μ΄κ²μ΄ λ¬Έμ μ
λλ€. λ°νλ λ¬Έμμ΄μ
λλ€!
Django νμ€ μκ°λ μ€μ μ μ¬μ©νκ³ λͺ¨λ ν
νλ¦Ώμ μμλλ‘ μ¬λ°λ₯Έ μκ° "2016-12-19T10:00:00+5:30"
λ°ννμ§λ§ DRFλ κ·Έλ μ§ μμ΅λλ€. DRF λ°ν "2016-12-19T04:30:00Z"
.
λ΄ REST APIλ₯Ό μ¬μ©νλ ν΄λΌμ΄μΈνΈμλ λ
Όλ¦¬, μκ° λ³ν λλ λ μ§/μκ° λ¬Έμμ΄ ν΄μμ΄ μμ΅λλ€.
μ¦, DRF μλ΅μ datetimeμ΄ Django Template "response"μ λμΌν κ²μΌλ‘ μμν©λλ€. μλ²λ ν΄λΌμ΄μΈνΈμ λν λͺ¨λ λ°μ΄ν°λ₯Ό μ€λΉνκ³ ν΄λΌμ΄μΈνΈλ μ΄λ₯Ό 보μ¬μ£ΌκΈ°λ§ ν©λλ€.
μ΄μ¨λ , μ΄ νμμ μΈ νλ‘μ νΈμ λν κ·νμ μΈλ΄, μ§μ λ° νλ₯ν μμ μ λν΄ λλ¨ν κ°μ¬ν©λλ€!
@vpistis μ¬κΈ°μ λ΄ μμ μ νμλ λ μ§κ° μ ννκ³ ννμ΄ μμλμ§ μλλ€λ κ²μ λλ€. κΈ°λ³Έ Dateλ‘ κ΅¬λ¬Έ λΆμνλ μ¦μ μΈμ΄κ° μ΄λ₯Ό μ²λ¦¬νμ§λ§ μ°¨μ΄κ° μμ΅λλ€.
μ¬μ©μκ° λ μ§ λ¬Έμμ΄μ λ μ§λ‘ ꡬ문 λΆμν κ²μΌλ‘ μμνμ§λ§ μμ λ¬Έμμ΄μ μ¬μ©νλ λμ ν΄λΌμ΄μΈνΈ μΈμ΄κ° μ΄λ₯Ό μ 곡ν©λλ€.
λλ λΉμ μ΄ μμ λ¬Έμμ΄μ μλΉνλ€λ©΄ λΉμ μ κΈ°λκ° μ¬κΈ°μμ μΆ©μ‘±λμ§ μμ κ²μ΄λΌλ μ μ μΈμ ν©λλ€. (νμ§λ§ κ·Έλ κ² νμ§ λ§μΈμ. UNIX νμμ€ν¬νλ₯Ό 보λΈλ€κ³ μμν΄ λ³΄μΈμ. μμ νμμ€ν¬νλ₯Ό μλΉν λ°©λ²μ΄ μμ΅λλ€. ν΄λΌμ΄μΈνΈ μΈμ΄λ‘ λ κ²μ΄ 무μμ΄λ μ μ ν Date κ°μ²΄λ‘ λ³ννμμμ€.)
μ΄μ λν΄ PRμ νκ² λμ΄ μ λ§ κΈ°μ©λλ€. (μμ§ μ λ«μμ΄μ!)
κ·Έλ¬λ λ³΄κ³ λ μ§ κ±°μ 2λ μ΄ μ§λ¬κ³ 첫 λ²μ§Έ μκ²¬μ΄ λμ¨ μ§ 9κ°μμ΄ μ§λ¬μ΅λλ€(λΉμ μ κ², 1λ ν). μ무λ μ°λ¦¬μκ² μ€ν¨ν ν μ€νΈ μΌμ΄μ€λ₯Ό μ£Όμ§ μμμ΅λλ€. κ·Έκ²μ λꡬμκ²λ κ·Έλ κ² μ€μν μ μμ΅λλ€. κ·Έλ§νΌ μκ°μ λ°°λΆνκΈ° μ΄λ ΅λ€.
(λ°λΌμ λλ PRμ΄ λνλλ©΄ PRμ ν κ²μ΄λΌλ κ·Όκ±°λ‘ κ·Έκ²μ λ«λ κ²½ν₯μ΄ μμ΅λλ€)
μλ νμΈμ μ¬λ¬λΆ, μ΄κ²μ #5408λ‘ μμ λμ΄μΌ ν©λλ€. λΆκΈ°λ₯Ό μ€μΉνκ³ λͺ¨λ κ²μ΄ μμλλ‘ μλνλμ§ νμΈν μκ°μ΄ μλ€λ©΄ νμμ μΌ κ²μ λλ€. κ°μ¬ ν΄μ!
λλ λ¬Έμ κ° μ΄λ»κ² λ λ€μ λμ λμλ€κ³ μκ°ν©λλ€.
κΈ°λ³Έ TZλ₯Ό UTCμμ μ λ½/μμ€ν λ₯΄λ΄μΌλ‘ λ³κ²½νμ λ ν μ€νΈ μ€ νλκ° μ€ν¨νκ³ DRFκ° κΈ°λ³Έ TZμ λ€λ₯Έ κ²μΌλ‘ μ§λ ¬ννκ³ μμμ μμμ΅λλ€.
νΈμ§: λ¬Έμ λ ν μ€νΈ/곡μ₯ μ€μ κ³Ό κ΄λ ¨λμμ΅λλ€.
μλμμ μ€μ μ ν
μ€νΈνμμμ€.
λͺ¨λΈ
class Something(StampedModelMixin):
MIN_VALUE = 1
MAX_VALUE = 500
id = models.BigAutoField(primary_key=True) # pylint: disable=blacklisted-name
product_id = models.BigIntegerField(db_index=True)
start_time = models.DateTimeField()
end_time = models.DateTimeField()
percentage = models.IntegerField()
enabled = models.IntegerField()
곡μ₯
class SomethingFactory(factory.django.DjangoModelFactory):
""" Base Factory to create records for Something
"""
start_time = factory.Faker('date_time', tzinfo=get_default_timezone())
end_time = factory.Faker('date_time', tzinfo=get_default_timezone())
percentage = factory.Faker('random_int', min=1, max=500)
enabled = factory.Faker('random_element', elements=[0, 1])
class Meta: # pylint: disable=missing-docstring
model = Something
λ¨μ ν μ€νΈ
class TestSomething:
def test__get__empty(self):
# preparation of data
series = SeriesFactory.create(product_id=2)
SomethingFactory.create_batch(3, product_id=1)
# prepare request params
url = reverse('series-somethings', kwargs={'pk': series.pk})
# call the endpoint
response = self.client.get(url)
# asserts
assert response.data == []
def test__get__single(self):
# preparation of data
series = SeriesFactory.create(product_id=1)
old_somethings = SomethingFactory.create_batch(1, product_id=1)
# prepare request params
url = reverse('series-somethings', kwargs={'pk': series.pk})
# call the endpoint
response = self.client.get(url)
# asserts
assert SomethingSerializer(old_somethings, many=True).data == response.data
보λ€
class SomethingElseView(APILogMixin, ModelViewSet):
@action(detail=True, methods=['get'])
def somethings(self, request, pk=None):
""" GET endpoint for Somethings
.. seealso:: :func:`rest_framework.decorators.action`
"""
otherthings = self.get_object()
something_qs = Something.objects.all()
something_qs = something_qs.filter(product_id=otherthings.product_id)
serializer = self.something_serializer_class(something_qs, many=True)
return Response(serializer.data)
μ§λ ¬ λ³νκΈ°
class SomethingSerializer(serializers.ModelSerializer):
class Meta:
model = Something
list_serializer_class = SomethingListSerializer
fields = '__all__'
extra_kwargs = {
'percentage': {'min_value': Something.MIN_VALUE,
'max_value': Something.MAX_VALUE}
}
read_only_fields = ('id',
'ts_activated',
'ts_created',
'ts_updated')
ν μ€νΈ ipdb κ²°κ³Ό
ipdb> old_somethings
[{'product_id': 1, 'start_time': datetime.datetime(2011, 7, 13, 1, 10, 33, tzinfo=<DstTzInfo 'Europe/Amsterdam' LMT+0:20:00 STD>), 'end_time': datetime.datetime(2003, 3, 10, 9, 31, tzinfo=<DstTzInfo 'Europe/Amsterdam' LMT+0:20:00 STD>), 'percentage': 103, 'enabled': 0}]
ipdb> response.data
[OrderedDict([('id', 1), ('ts_created', '2019-02-27 14:16:33'), ('ts_updated', '2019-02-27 14:16:33'), ('product_id', 1), ('start_time', '2011-07-13 02:50:33'), ('end_time', '2003-03-10 10:11:00'), ('percentage', 103), ('enabled', 0)])]
κ²μ¬ κ²°κ³Ό
E AssertionError: assert [OrderedDict(...nabled', 0)])] == [OrderedDict([...nabled', 0)])]
E At index 0 diff: OrderedDict([('id', 1), ('ts_created', '2019-02-27 14:38:15'), ('ts_updated', '2019-02-27 14:38:15'), ('product_id', 1), ('start_time', '2011-07-13 01:10:33'), ('end_time', '2003-03-10 09:31:00'), ('percentage', 103), ('enabled', 0)]) != OrderedDict([('id', 1), ('ts_created', '2019-02-27 14:38:15'), ('ts_updated', '2019-02-27 14:38:15'), ('product_id', 1), ('start_time', '2011-07-13 02:50:33'), ('end_time', '2003-03-10 10:11:00'), ('percentage', 103), ('enabled', 0)])
E Full diff:
E - [OrderedDict([('id', 1), ('ts_created', '2019-02-27 14:38:15'), ('ts_updated', '2019-02-27 14:38:15'), ('product_id', 1), ('start_time', '2011-07-13 01:10:33'), ('end_time', '2003-03-10 09:31:00'), ('percentage', 103), ('enabled', 0)])]
E ? ^^^^^^^^^^ ^^^^^^^^^^^
E + [OrderedDict([('id', 1), ('ts_created', '2019-02-27 14:38:15'), ('ts_updated', '2019-02-27 14:38:15'), ('product_id', 1), ('start_time', '2011-07-13 02:50:33'), ('end_time', '2003-03-10 10:11:00'), ('percentage', 103), ('enabled', 0)])]
E ?
μ€ν:
Django==2.0.10
djangorestframework==3.9.0
factory-boy==2.11.1
Faker==0.8.18
pytz==2018.9
μλ νμΈμ @diegueus9 - μ΄κ²μ λ κ°λ¨ν ν μ€νΈ μΌμ΄μ€λ‘ μ€μΌ μ μμ΅λκΉ? μ§λ ¬νλ κ°μ§ λ°μ΄ν°μ 보기μ μλ΅ λ°μ΄ν°λ₯Ό λΉκ΅νκ³ μμ΅λλ€. λ°λΌμ μ€μ μμ κ°μ΄ 무μμΈμ§ λͺ ννμ§ μμ΅λλ€. μΌλΆ κ²°κ³Όλ₯Ό νλμ½λ©λ κ°κ³Ό λΉκ΅νλ κ²μ΄ μ’μ΅λλ€. μ,
assert SomethingSerializer(old_somethings, many=True).data == {'blah':'blah'}
@rpkilby λ΅λ³ κ°μ¬ν©λλ€. λ΄ μ€μμμ΅λλ€. λ΄ λ¨μ ν μ€νΈμ λ¬Έμ λ DBμμ μλ‘ κ³ μΉμ§ μκ³ factory-boy/fakerλ₯Ό μ¬μ©νκ³ μμκΈ° λλ¬Έμ μ°¨μ΄μ μ΄ μμ΅λλ€. λ°©κΈ μΆκ°νμ΅λλ€.
for old_something in old_somethings:
old_something.refresh_from_db()
μ΄μ λκΈμ μ κ±°ν΄μΌ ν©λκΉ μλλ©΄ λ€λ₯Έ μ¬λμ΄ λμΌν μ€νμ§λ₯Ό κ²½νν κ²½μ°λ₯Ό λλΉνμ¬ λ¨κ²¨ λμ΄μΌ ν©λκΉ?
μλ
νμΈμ @diegueus9 , κ±±μ λ§μΈμ - μ λ details
νκ·Έμ μ½λλ₯Ό μ¨κ²Όμ΅λλ€.
κ°μ₯ μ μ©ν λκΈ
μ΄ κΈ°λ₯μ κ°μ¬λ립λλ€.
USE_TZ
μ€μ μ΄ μ‘΄μ€λμ΄μΌ νκ³ νμ€ Django λμκ³Ό μ μ¬νκ² κ°μ΄ λ³νλμ΄μΌ νλ€κ³ μκ°ν©λλ€.