_stackoverflow ์ง๋ฌธ IMO๊ฐ ์๋๋ฏ๋ก ์ค๋กํฉ๋๋ค_
์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ๋๋ฐ v2 ๋ถ๊ธฐ์์ ๋๋ฌด ๋ง์ ๋ฌธ์ ๊ฐ ์์๊ธฐ ๋๋ฌธ์ ๋ ผ์ํ๊ธฐ ์ด๋ ต๋ค๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค. ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ ํ๋์์ ์ ํํ ํ๋ก์ ํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํ๊ณ ์ถ์ต๋๋ค.
QUERY_PARAMS
์ ์ ๊ณต๋ ํ๋ก์ ํธ ID ๋ฐ v3-branch์์ ์ง๊ธ์ฒ๋ผ perform_create
์ ์ ์ฉ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ์ ์ฅํ๊ธฐ ์ ์ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ ํ๋์ ํ๋ก์ ํธ๊ฐ ํ์ํฉ๋๋ค. ์ง๋ ฌ ๋ณํ๊ธฐ ๋ฉํ๋ฐ์ดํฐ์ ๊ฐ์ฒด์ ๋ํ ๊ธฐ๋ณธ๊ฐ์ ์ ๊ณตํ๊ณ ์ถ์ต๋๋ค. ๋ฐ๋ผ์ ์ฌ์ฉ์๋ ํ๋ก์ ํธ ๊ฐ์ฒด์์ ์ ํ์ ์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์๋ ๋ช ๊ฐ์ง ์ฌํญ์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
๋ฐ๋ผ์ $#$3$# context
์ get_serializer_context
๋ฅผ ํ๋ก์ ํธ ๊ฐ์ฒด๋ก ์ฑ์๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ธฐ๋ณธ๊ฐ์ ์ค์ ํ๋ ค๋ฉด ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ์์ __init__
๋ฅผ ๋ค์ ์์ฑํด์ผ ํฉ๋๋ค.
๋ฌธ์ ๋ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ๊ฐ __init__
context
๋๋ parent
๋ฅผ ์์ ํ์ง ์๋๋ค๋ ๊ฒ์
๋๋ค. ์ปจํ
์คํธ๋ฅผ ์ ํ ์์ ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค! ๋ฐ๋ผ์ pull-request 497 ์ดํ ํ๊ท์ฌ์ผ ํฉ๋๋ค.
๋ด ์๊ฐ์๋ ํ๊ท๊ฐ ์๋๋ผ ๋์์ธ ๋ณ๊ฒฝ๊ณผ ๋น์ทํ์ง๋ง serizliser ๋์์ ๋ณ๊ฒฝํ ์ ์์ผ๋ ค๋ฉด ์ปจํ ์คํธ๊ฐ ํ์ํฉ๋๋ค!
์ปจํ ์คํธ๋ ๋ถ๋ชจ ์ง๋ ฌ ๋ณํ๊ธฐ์์ ์ ์ธ๋ ๋ ์ด๊ธฐํ๋๊ธฐ ๋๋ฌธ์ ์ด๊ธฐํ ์์ ์์ ํ๋(๋๋ ์ค์ฒฉ ์ง๋ ฌ ๋ณํ๊ธฐ)๋ก ์ ๋ฌ๋์ง ์์ต๋๋ค(์ ๋ฌ๋ ์ ์์)...
class MySerializer(Serializer):
child = ChildSerializer() <--- We've just initialized this.
๊ฐ์ฅ ์ข์ ๊ฒ์ MySerializer.__init__
์์ ์ด์ ๊ฐ์ ๋์์ ์ํํ๋ ๊ฒ์
๋๋ค. ์๋ฅผ ๋ค์ด...
def __init__(self, *args, **kwargs):
super(MySerializer, self).__init__(*args, **kwargs)
self.fields['child'].default = ...
๊ทธ ์์ ์์ ์ปจํ ์คํธ์ ๋ํ ์ก์ธ์ค ๊ถํ์ด _do_ ์์ต๋๋ค.
์ ๋ง๋ก ์ํ๋ค๋ฉด ํ๋๊ฐ ๋ถ๋ชจ์ ๋ฐ์ธ๋ฉ๋๋ ์ง์ ์์ ๋ช ๊ฐ์ง ์์ ์ ์ํํ ์ ์์ต๋๋ค.
class ChildSerializer(Serializer):
def bind(self, *args, **kwargs):
super(ChildSerializer, self).bind(*args, **kwargs)
# do stuff with self.context.
์ด๊ฒ์ ๋น๊ณต๊ฐ API๋ก ๊ฐ์ฃผ๋์ด์ผ ํ๋ฉฐ ์์ ๋์ด๋ ์์ __init__
์คํ์ผ์ด ์ ํธ๋์ด์ผ ํฉ๋๋ค.
์, ๋น์ ์ด ์ค๋ช
ํ๋ ๊ฒ์ ์ดํดํฉ๋๋ค. ๊ทธ๋์์ธ์ง ์กฐ๊ธ ๊น๋ค๋กญ๋ค๊ณ ์๊ฐํฉ๋๋ค. ๊ทธ๋์ ๋ด ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋ถ๋ชจ์ __ini__
์ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ๋ฅผ ๋ง๋๋ ๊ฒ์
๋๋ค. ๊ทธ๋ฌ๋ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ __init__
๋ deepcopy๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ ์ง๋ ฌ ๋ณํ๊ธฐ ์ธ์คํด์ค๊ฐ ์์ฑ๋ ๋๋ง๋ค ํธ์ถ๋์ง๋ง ์์ ์ธ์คํด์ค๋ฅผ ๋ชฐ๋ ๋ค์ด๊ฐ ์๋ ์์ต๋๋ค...
๊ทธ๋์ v2์์ ๋ณผ ์ ์๋ฏ์ด ํ๋์๋ ๋ถ๋ชจ ์ง๋ ฌ ๋ณํ๊ธฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ธฐ๋ณธ๊ฐ์ ์ด๊ธฐํํ๊ธฐ์ ์๋ฒฝํ initialize
๋ฉ์๋๊ฐ ์์์ต๋๋ค.
๋ฐ๋ผ์ bind
๊ฐ ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
์ด๊ฒ์ ๋น๊ณต๊ฐ API๋ก ๊ฐ์ฃผ๋์ด์ผ ํ๋ฉฐ ์์ ๋์ด๋ ์์
__init__
์คํ์ผ์ด ์ ํธ๋์ด์ผ ํฉ๋๋ค.
๊ทธ๋ฌ๋ __init__
๋ฅผ ์ฌ์ฉํ ์๋ ์๊ธฐ ๋๋ฌธ์ ํ์ฌ ๋ณด๊ธฐ์์ ์์ฑ๋ ๋ชจ๋ ์ฌ์ฉ ๊ฐ๋ฅํ ํ๊ฒฝ์ ์ก์ธ์คํ ์ ์์ผ๋ฏ๋ก ๋ชจ๋ ๊ฒ์ด ์์ ๋ ํ๋(serializer)์์ ํธ์ถ๋๋ ๊ณต๊ฐ API ๋ฉ์๋๊ฐ ์์ด์ผ ํฉ๋๋ค. ์ฌ์ฉ ๊ฐ๋ฅ. initialize
์ฒ๋ผ.
DRF 2์์ ์๋ํฉ๋๊น?
์ ๊ฐ์ค๋ฝ๊ฒ๋ ์ปจํ ์คํธ๊ฐ ๋ ์ด์ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ๋ก ์ ํ๋์ง ์์ต๋๋ค.
์ ๊ฐ์ค๋ฝ๊ฒ๋ ์ปจํ ์คํธ๊ฐ ๋ ์ด์ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ๋ก ์ ํ๋์ง ์์ต๋๋ค.
๋ ๊ทธ๊ฑฐ ์ด๋์ ์ป์ ๋ ? Afaik, ์ปจํ ์คํธ๊ฐ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ๋ก ์ ํ๋์์ต๋๋ค.
@xordoquy ์ด์ํฉ๋๋ค. ์ต์ drf์ ๋ํ ํ ์คํธ ์ผ์ด์ค๋ฅผ ๋ง๋ค์๊ณ ๋ฌธ์ ๋ฅผ ์ฌํํ ์ ์์ต๋๋ค. ๋๊ตฌ๋ ๋ด ์์ ์ ์ฝ๋์ ๋ํด ์ฌํํ ์ ์์ต๋๋ค. ๋ฌธ์ ํด๊ฒฐ๋จ.
@xiaohanyu ์ค์ ๋ก ๋ค์ ํ ๋ฒ์ด ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ข ์ด์ํฉ๋๋ค.
>>> serializer
Out[12]:
# Note there is context passed in constructor
CouponSerializer(<Coupon: Coupon Coupon #0 for offer Offer #0000>, context={'publisher': <User: John ZusPJRryIzYZ>}):
id = IntegerField(label='ID', read_only=True)
title = CharField(max_length=100, required=True)
short_title = CharField(max_length=30)
offer_details = OfferLimitedSerializer(read_only=True, source='offer'):
id = IntegerField(label='ID', read_only=True)
title = CharField(help_text='The Offer Title will be used for the Network, Advertisers and Publishers to identify the specific offer within the application.', read_only=True)
status_name = CharField(read_only=True, source='get_status_display')
is_access_limited = SerializerMethodField()
exclusive = BooleanField(required=False)
categories_details = OfferCategorySerializer(many=True, read_only=True, source='categories'):
id = IntegerField(read_only=True)
category = CharField(read_only=True, source='code')
category_name = CharField(read_only=True, source='get_code_display')
# all fields except one got context propagated
>>> [f.field_name for f in serializer.fields.values() if not f.context]
Out[20]:
['offer_details']
# offer_details is no different than categories_details, both are nested, read only, model serializers..
>>> serializer.fields['categories_details'].context
Out[21]:
{'publisher': <User: John ZusPJRryIzYZ>}
# lets look inside problematic serializer
>>> offer_details = serializer.fields['offer_details']
>>> offer_details
Out[25]:
OfferLimitedSerializer(read_only=True, source='offer'):
id = IntegerField(label='ID', read_only=True)
title = CharField(help_text='The Offer Title will be used for the Network, Advertisers and Publishers to identify the specific offer within the application.', read_only=True)
status_name = CharField(read_only=True, source='get_status_display')
url = SerializerMethodField()
is_access_limited = SerializerMethodField()
>>> offer_details.context
Out[23]:
{}
>>> offer_details._context
Out[24]:
{}
# ! surprisingly context is available inside fields... but not in the parent
>>> offer_details.fields['is_access_limited'].context
Out[26]:
{'publisher': <User: John ZusPJRryIzYZ>}
# context is available in all of them!
>>> [x.field_name for x in offer_details.fields.values() if not x.context]
Out[27]:
[]
@tomchristie ๊ท์ฐฎ๊ฒ ํด์ ๋ฏธ์ํ์ง๋ง, ๊ทธ๊ฒ ์ด๋ป๊ฒ ๊ฐ๋ฅํ์ง ์ ์ ์์๊น์?
๊ทธ๋์ ๋๋ ์ด ํดํน์ ์ฌ์ฉํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด์ผ ํ์ต๋๋ค.
class Serializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
super(Serializer, self).__init__(*args, **kwargs)
# propagate context to nested complex serializers
if self.context:
for field in six.itervalues(self.fields):
if not field.context:
delattr(field, 'context')
setattr(field, '_context', self.context)
@pySilver ๊ฐ๋จํ ํ ์คํธ ์ผ์ด์ค ์์ด๋ ํฐ ๋์์ด ๋์ง ์์ต๋๋ค. ์ปจํ ์คํธ ์ ํ๋ฅผ ์ค๋จํ๋ ์ผ๋ถ ์ฌ์ฉ์ ์ ์ ์ฝ๋๊ฐ ์์ ์ ์๋ ์ฌ๋ฌ ์ง๋ ฌ ๋ณํ๊ธฐ๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
์ฌ๋ฌ ํด๋์ค๊ฐ serializers.Serializer์ ์์์ธ ๋ณต์กํ mro
๋๋ฌธ์ ๋ฐ์ํ๋ ๊ฒ์ผ๋ก ๋ํ๋ฌ์ต๋๋ค. ๊ทธ๋ฐ ๊ฒ
class MyMixin(serializers.Serializer):
# some code within __init__, calling parent too
class MyModelSerializer(serializers.ModelSerializer):
# some code within __init__, calling parent too
class MyProblematicSerializer(MyModelSerializer, MyMixin)
# this one will not receive context somehow.
pass
์ด์จ๋ ์ง์ญ ๋ฌธ์ ๋ผ๊ณ ์๊ฐํ ์ ์์ต๋๋ค.
ํผ๋๋ฐฑ ๊ฐ์ฌํฉ๋๋ค ๐
@xordoquy @tomchristie ์ค์ ๋ก ๋ฌธ์ ๊ฐ ์ ๋ง ์ด์ํด์ก์ต๋๋ค. ๋๋ ์ด์ด ์์ด fields
์์ฑ์์ ๊ทธ๋ ๊ฒ ํ๋ ค๊ณ ํ์ต๋๋ค. ๋ค์์ ํ
์คํธ ์ผ์ด์ค์
๋๋ค.
def test_serializer_context(self):
class MyBaseSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
super(MyBaseSerializer, self).__init__(*args, **kwargs)
x = 0
if self.context.get('x'):
x = self.context['x']
class SubSerializer(MyBaseSerializer):
char = serializers.CharField()
integer = serializers.IntegerField()
class ParentSerializer(MyBaseSerializer):
with_context = serializers.CharField()
without_context = SubSerializer()
serializer = ParentSerializer(data={}, context={'what': 42})
assert serializer.context == {'what': 42}
assert serializer.fields['with_context'].context == {'what': 42}
assert serializer.fields['without_context'].context == {'what': 42}
๊ทธ๋ฌ๋ ์ด๊ฒ์ ํต๊ณผํ ๊ฒ์ ๋๋ค. imho ์ฌ์ ์๋ ๋ฉ์๋์์ ์ปจํ ์คํธ์ ์ก์ธ์คํ๋ ๋ฐ ๋ฌธ์ ๊ฐ ์์ ์ ์๋ค๋ ์ ์ ์์ค์์ ์ธ๊ธํด์ผ ํฉ๋๋ค...
def test_serializer_context(self):
class MyBaseSerializer(serializers.Serializer):
<strong i="12">@property</strong>
def fields(self):
fields = super(MyBaseSerializer, self).fields
x = 0
if self.root.context.get('x'):
x = 1
return fields
class SubSerializer(MyBaseSerializer):
char = serializers.CharField()
integer = serializers.IntegerField()
class ParentSerializer(MyBaseSerializer):
with_context = serializers.CharField()
without_context = SubSerializer()
serializer = ParentSerializer(data={}, context={'what': 42})
assert serializer.context == {'what': 42}
assert serializer.fields['with_context'].context == {'what': 42}
assert serializer.fields['without_context'].context == {'what': 42}
๋ช ํํ ํ์๋ฉด, ์ฌ๊ธฐ์ ๋ด๊ฐ ํ๊ณ ์๋ ๊ฒ์ ์ปจํ ์คํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋์ ๋์์ ๋ณ๊ฒฝํ๋ ๊ฒ์ ๋๋ค.
UPD: ์ด๋ค ๊ฒฝ์ฐ์๋ ๋ง์ง๋ง ์ฝ๋๋ ์ ๋ขฐํ ์ ์์ต๋๋ค. ๋ง์ง๋ ์ปจํ ์คํธ๊ฐ ์ํํ๋ค๋ ๊ฒ์ด ๋ฐํ์ก์ต๋๋ค.
__init__
์ปจํ
์คํธ์ ์ก์ธ์คํ ์ ์๋ค๋ ๊ฒ์ ๋ถํํ ์ผ์
๋๋ค. ์ปจํ
์คํธ์ ๋ฌด์ธ๊ฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ง๋ ฌ ๋ณํ๊ธฐ์์ ํ๋๋ฅผ ๋์ ์ผ๋ก ์์ฑํ๋ ค๋ ๊ฒฝ์ฐ ์ด๋์์ ์ํํ์๊ฒ ์ต๋๊น?
์์์ ์ ์ํ ๋๋ก bind
์์ ์๋ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์๋ฅผ ๋ค์ด self.fields['asdf'] = serializers.CharField()
. bind
๊ฐ ํธ์ถ๋ ๋ ํ๋๊ฐ ์ด๋ฏธ ํ๊ฐ๋์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
fields
์์ฑ์์ ์ํํ๋ ๊ฒ๋ ์ข์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค. ํด๋น ํญ๋ชฉ์ด ๋ง์ด ํธ์ถ๋๊ณ ์ง์ฐ ํ๊ฐ๋ก ์ธํด self._fields
์ ์บ์ฑ ๋ฉ์ปค๋์ฆ์ด ์์ผ๋ฏ๋ก ๋ณต์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์
๋๋ค. ๊ตฌ์ฒด์ ์ธ ์ง๋ ฌ ๋ณํ๊ธฐ์์. ๋ด๋ถ์ ์ผ๋ก ์บ์๋๊ธฐ ์ ์ ํ๋๋ฅผ ํ ๋ฒ ์์ ํ ์ ์๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ง๋ ฌํ๊ธฐ๋ฅผ ์ง์ ํธ์ถํ์ง ์๊ณ ํ๋๋ก ์ค์ฒฉ์ ์ํํ๋ค๋ฉด ๋ฌธ์ ๊ฐ ๋์ง ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
์ด์ ๊ฐ์ด:
class ChildSerializer(serializers.Serializer):
...
class ParentSerializer(serializers.Serializer):
child = serializers.NestedField(serializer=ChildSerializer)
๊ทธ๋ฌ๋ฉด ChildSerializer
ParentSerializer
์ ํ๋ ์ ์ธ์์ ์ง์ ์ธ์คํด์คํ๋์ง ์๊ธฐ ๋๋ฌธ์ __init__
์ ์ปจํ
์คํธ์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
์ปจํ ์คํธ์ ๋ฌด์ธ๊ฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ง๋ ฌ ๋ณํ๊ธฐ์์ ํ๋๋ฅผ ๋์ ์ผ๋ก ์์ฑํ๋ ค๋ ๊ฒฝ์ฐ ์ด๋์์ ์ํํ์๊ฒ ์ต๋๊น?
๊ตฌ์ฒด์ ์ธ ์์ ๋งฅ๋ฝ์์ ์ด์ ๋ํด ์ด์ผ๊ธฐํด ๋ณด๊ฒ ์ต๋๋ค. ๋ฉ์ง ๊ฐ๋จํ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์๊ฐํด ๋ด๊ณ ์ด๋ฅผ ํด๊ฒฐํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ์์๋ผ ์ ์์ต๋๋ค.
์ง๋ ฌํ๊ธฐ๋ฅผ ์ง์ ํธ์ถํ์ง ์๊ณ ํ๋๋ก ์ค์ฒฉ์ ์ํํ๋ค๋ฉด ๋ฌธ์ ๊ฐ ๋์ง ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
์๋ํด๋ณด์ญ์์ค. ์ฐ๋ฆฌ๊ฐ ๊ทธ ๊ธธ์ ๊ฐ ๊ฒ ๊ฐ์ง๋ ์์ง๋ง. ์ด๊ฒ์ ํ์ฌ ์ฐ๋ฆฌ๊ฐ ํ๋ ์ผ์ ํฐ ๋ณํ๋ฅผ ๊ฐ์ ธ์์ผ๋ฉฐ, ๋ํ ์์ ์ง๋ ฌ ๋ณํ๊ธฐ์ ์ธ์๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค. ๊ทธ๋ฐ ์คํ์ผ์ ํ๋นํ ๊ฒฝ์ฐ๊ฐ ์๋ค๊ณ ์๊ฐํ์ง๋ง ์ด ์์ ์์ ์ฐ๋ฆฌ๊ฐ ํน๋ณํ ๋ณํ๋ ๊ฒ์ ๋ณด์ง๋ ๋ชปํฉ๋๋ค.
@tomchristie ๋ต๋ณ ๊ฐ์ฌํฉ๋๋ค.
๋ด๊ฐ ์ง๊ธ ๊ฐ์ง๊ณ ์๋ ํ ๊ฐ์ง ์ฌ์ฉ ์ฌ๋ก๋ ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ์์ฑ์ ๋ฐ๋ผ ์ค์ฒฉ๋ ์์ ์ง๋ ฌ ๋ณํ๊ธฐ์์ ๋ ๊ฐ์ ์๋ก ๋ค๋ฅธ ํ๋ ์งํฉ ์ฌ์ด๋ฅผ ์ ํํ๋ ๊ฒ์ ๋๋ค.
Type A ์ฌ์ฉ์๋ Type B ์ฌ์ฉ์์ ๊ด๋ จ๋ ํ๋๋ฅผ ์ ๋ ๋ณผ ์ ์์ผ๋ฉฐ ๊ทธ ๋ฐ๋์ ๊ฒฝ์ฐ๋ ๋ง์ฐฌ๊ฐ์ง์ ๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ๋ณด์ ๋๋ค.
class ChildSerializer(serializers.Serializer):
# relevant for all users
name = serializers.CharField()
# only relevant for users of Type A
phone = serializers.CharField()
# only relevant for users of Type B
ssn = serializers.CharField()
๊ทธ๋ฌ๋ ์ ๋ drf ์ง๋ ฌ ๋ณํ๊ธฐ ๋ฐ/๋๋ django ์์์ ์ฌ์ฉํ์ฌ api ๋ฐ html ์์ ๋ชจ๋์์ ๋ค์ํ ๋ฐฉ์์ผ๋ก ํ๋๋ฅผ ์ ๊ธฐ์ ์ผ๋ก ์์ ํฉ๋๋ค. ๋์ ์ผ๋ก ํ๋๋ฅผ ์ถ๊ฐํ๊ณ , ํ๋๋ฅผ ์ ๊ฑฐํ๊ณ , ์์ฑ์ ์์ ํ๋ ์์ ์๋ ์ ์ผํ ๋ฐฉ๋ฒ์ด ์๋๋๋ค. ๋๋ ์ด์ ์ ์ค์ฒฉ๋ ์์์์ ๊ทธ๊ฒ์ ํ ํ์๊ฐ ์์๊ณ ์ด๊ฒ์ ์์ ํ ๋นํฉํ์ต๋๋ค.
@tomchristie ๋ด ์ผ๋ฐ์ ์ธ ์ฌ์ฉ ์ฌ๋ก๋ ์ปจํ ์คํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋๊ฐ ํ์ํ์ง ์ฌ๋ถ๋ฅผ ์ ์ดํ๋ โโ๊ฒ์ ๋๋ค(์ฌ์ฉ์ ๋ฐ ๊ด๋ฆฌ์๋ฅผ ์ํ ์ง๋ ฌ ๋ณํ๊ธฐ๋ฅผ ์์ํด ๋ณด์ญ์์ค. ์ฌ๊ธฐ์ ์ปจํ ์คํธ๋ฅผ ํตํด ์ ๋ฌ๋๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์ ID๋ฅผ ์ ๊ณตํ ํ์๊ฐ ์์ต๋๋ค.) ์ด ์ค๋ ๋์ ์ด์ ๊ฒ์๋ฌผ์์ ์๋ฃจ์ ์ ์ฌ์ฉํ์ญ์์ค.
ํ ๊ฐ์ง ํด๊ฒฐ ๋ฐฉ๋ฒ์ ํ๋ ์ ์ธ ๋์ ParentSerializer
์ __init__
๋ฉ์๋์์ ํญ์ ์ค์ฒฉ๋ ChildSerializer
์ ์ธ์คํด์คํํ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ฌ๋ฉด ์ปจํ
์คํธ๋ฅผ ์๋์ผ๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.
์ด์ ๊ฐ์ด:
class ChildSerializer(serializers.Serializer):
...
class ParentSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['child'] = ChildSerializer(context=self.context)
์ด๊ธฐ ํ ์คํธ์์ ์๋ํ๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ด๊ฒ์ ์ด๋ค ์บ์น๊ฐ ์๋์ง ํ์คํ์ง ์์ต๋๋ค.
๊ฐ์ ์ ์์ ํ๋ ค๊ณ ํ์ด, ๋ค.
๊ธ์, ์ด๋ค ๊ฒฝ์ฐ์๋ ์ ์๊ฒ ํจ๊ณผ๊ฐ ์์์ต๋๋ค. ์๋ง๋ ๋ค์ค ์์ ๋๋ฌธ์ผ ๊ฒ์ ๋๋ค.
2016๋ 10์ 12์ผ ์์์ผ ์คํ 2์ 56๋ถ +0200์ "Tom Christie" [email protected] ์ด ์์ฑํ์ต๋๋ค.
๊ฐ์ ์ ์์ ํ๋ ค๊ณ ํ์ด, ๋ค.
โ
๋น์ ์ด ์ธ๊ธ๋์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์
๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ฑฐ๋ GitHub์์ ๋ณด๊ฑฐ๋ ์ค๋ ๋๋ฅผ ์์๊ฑฐํ์ธ์.
default
์์ฑ(๋ด ๊ฒฝ์ฐ์๋ CurrentUserDefault()
๊ฐ ํฌํจ)์ ์ฒ๋ฆฌํ๋ __init__
๋ฉ์๋๋ฅผ ์๋ธํด๋์ฑํ (์ค์ฒฉ) ์ง๋ ฌ ๋ณํ๊ธฐ ํ๋๋ก ์ค์ ํ ๋๋ ๋์ผํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. self.context
์์ฑ์์ KeyError
$๋ ์ปจํ
์คํธ์์ request
๋ฅผ ์ฐพ์ ์ ์์์ ๋ณด์ฌ์ค๋๋ค(์๋ฌด ๊ฒ๋ ํฌํจํ์ง ์์, ์ฆ ์์์์ ์ ๋ฌ๋์ง ์์).
์์ ์ฝ๋:
class UserSerializer(serializers.ModelSerializer):
# ...
def __init__(self, *args, **kwargs):
kwargs.pop('fields', None)
super().__init__(*args, **kwargs)
if 'list' in self.context: # Once I remove this, it will work
self.fields['friend_count'] = serializers.SerializerMethodField()
# ...
class CommentSerializer(serializers.ModelSerializer):
person = UserSerializer(read_only=True, default=serializers.CurrentUserDefault())
# ...
์ค๋ฅ ๊ธฐ๋ก:
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/api/comments/98/
Django Version: 1.9.7
Python Version: 3.4.3
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'rest_framework',
'rest_framework.authtoken',
'generic_relations',
'decorator_include',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.google',
'phonenumber_field',
'bootstrap3',
'stronghold',
'captcha',
'django_settings_export',
'main']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'stronghold.middleware.LoginRequiredMiddleware']
Traceback:
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
149. response = self.process_exception_by_middleware(e, request)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
147. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/django/views/decorators/csrf.py" in wrapped_view
58. return view_func(*args, **kwargs)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/django/views/generic/base.py" in view
68. return self.dispatch(request, *args, **kwargs)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/views.py" in dispatch
466. response = self.handle_exception(exc)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/views.py" in dispatch
463. response = handler(request, *args, **kwargs)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/generics.py" in post
246. return self.create(request, *args, **kwargs)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/mixins.py" in create
20. serializer.is_valid(raise_exception=True)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/serializers.py" in is_valid
213. self._validated_data = self.run_validation(self.initial_data)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/serializers.py" in run_validation
407. value = self.to_internal_value(data)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/serializers.py" in to_internal_value
437. validated_value = field.run_validation(primitive_value)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/serializers.py" in run_validation
403. (is_empty_value, data) = self.validate_empty_values(data)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/fields.py" in validate_empty_values
453. return (True, self.get_default())
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/fields.py" in get_default
437. self.default.set_context(self)
File "/home/mikisoft/django-dev/lib/python3.4/site-packages/rest_framework/fields.py" in set_context
239. self.user = serializer_field.context['request'].user
Exception Type: KeyError at /api/comments/98/
Exception Value: 'request'
์ถ๊ฐ๋ ํ๋์๋ ์ง๋ ฌ ๋ณํ๊ธฐ๊ฐ ๋ฉํํด๋์ค๋ฅผ ํตํด ์ํํ๋ ๋ช ๊ฐ์ง ์์ ์ด ๋ถ์กฑํฉ๋๋ค. ๊ทธ๊ฒ๋ค์ ์ค์ ํ๋ ๊ฒ์ ๋น์ ์๊ฒ ๋ฌ๋ ค ์์ต๋๋ค.
์์์ ์ด๋ฏธ ๋งํ๋ฏ์ด __init__
๋ฉ์๋์์ ์ปจํ
์คํธ ๊ฒ์์ ์ ๊ฑฐํ๋ฉด ๋ชจ๋ ๊ฒ์ด ์ ์๋ํฉ๋๋ค. ๋ฐ๋ผ์ ์ปจํ
์คํธ๊ฐ ๋ถ๋ชจ๋ก๋ถํฐ ์ ๋ฌ๋๊ธฐ ๋๋ฌธ์ ํ๋ ์ค์ ์ ํ์คํ ๋ฌธ์ ๊ฐ ๋์ง ์์ต๋๋ค. ์ฌ๋ก.
๋ง์, ๋ด ์ ํ์ ์์๋ค, ๊ทธ ๋ถ๋ถ์ ๋์น ์ฑ์ง ๋ชปํ์ต๋๋ค.
๊ทธ๋ฐ ๋ค์ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ๊ฐ ์ต์์ ์ง๋ ฌ ๋ณํ๊ธฐ๋ณด๋ค ํจ์ฌ ์ ์ ์ธ์คํด์คํ๋๊ธฐ ๋๋ฌธ์ ์ ์์
๋๋ค.
์ปจํ
์คํธ์ ๋ชฉ์ ์ ์ค์ ๋ก ์ธ์คํด์คํ๊ฐ ๋ฐ์ํ ํ์ ์ปจํ
์คํธ๋ฅผ ์ ๋ฌํ๋ ๊ฒ์
๋๋ค. ๋ฐ๋ผ์ init์์ ์๋ํ์ง ์๋ ๊ฒ์ ๋๋ผ์ด ์ผ์ด ์๋๋๋ค.
๊ด์ฐฎ์. ๋ง์ง๋ง ์ฐธ๊ณ ๋ก, ์ค๋ฅ ๋ก๊ทธ๊ฐ ํ์ํ๋ ๊ฒ์ฒ๋ผ __init__
๋ฉ์๋์ ๋ํ๋์ง ์๊ณ CurrentUserDefault()
๊ฐ์ฒด( set_context
๋ฐฉ๋ฒ). ๊ทธ๋ฌ๋ ์ปจํ
์คํธ ๋งค๊ฐ๋ณ์์ ๋ํ ์ง์ ํธ์ถ์ด ์๋ ๊ฒฝ์ฐ, ์ฆ ์กด์ฌ๋ฅผ ํ์ธํ์ง ์๊ณ __init__
์๋ ํ์๋๋์ง ํ์ธํ์ญ์์ค(์ ์ฒด ์ปจํ
์คํธ๊ฐ ๋น์ด ์๊ธฐ ๋๋ฌธ์ ์ ๋ฌ๋์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค).
๋ํ ๋ค๋ฅธ ์ฌ๋์ด ์์์ ๊ฒฐ๋ก ์ ๋ด๋ฆฐ ๊ฒ์ ๋ฐ๋ณตํ๊ณ ์ถ์ต๋๋ค. ์ค์ฒฉ์ด ์๋ ๊ฒฝ์ฐ(๊ทธ๋ฆฌ๊ณ ์ปจํ
์คํธ๊ฐ ์ธ์คํด์คํ ๋ฐฉ๋ฒ์ ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ) ๋ชจ๋ ๊ฒ์ด ์์๋๋ก ์๋ํฉ๋๋ค.
์ปจํ
์คํธ๋ฅผ ์ด๋ฆฐ์ด์๊ฒ ์ ํํ๋ ๋ฐ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค. https://github.com/encode/django-rest-framework/pull/5304 ์์ ํด๊ฒฐํด์ผ ํฉ๋๋ค None
์บ์ฑ๊ณผ ๊ด๋ จ๋ ๋ง์ ๋ฌธ์ ๊ฐ ์์์ ๊ฒ ๊ฐ์ต๋๋ค.
์ต๊ทผ์ ์ด ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ์ด๊ฒ์ ๋๋ฅผ ์ํด ์ผํ ์๋ฃจ์ ์ ๋๋ค.
class MyBaseSerializer(serializers.HyperlinkedModelSerializer):
def get_fields(self):
'''
Override get_fields() method to pass context to other serializers of this base class.
If the context contains query param "omit_data" as set to true, omit the "data" field
'''
fields = super().get_fields()
# Cause fields with this same base class to inherit self._context
for field_name in fields:
if isinstance(fields[field_name], serializers.ListSerializer):
if isinstance(fields[field_name].child, MyBaseSerializer):
fields[field_name].child._context = self._context
elif isinstance(fields[field_name], MyBaseSerializer):
fields[field_name]._context = self._context
# Check for "omit_data" in the query params and remove data field if true
if 'request' in self._context:
omit_data = self._context['request'].query_params.get('omit_data', False)
if omit_data and omit_data.lower() in ['true', '1']:
fields.pop('data')
return fields
์์์ get_fields()
๋ฅผ ์ฌ์ ์ํ๊ณ ๋์ผํ ๊ธฐ๋ณธ ํด๋์ค๊ฐ ์๋ ์์ ์ง๋ ฌ ๋ณํ๊ธฐ์ self._context
๋ฅผ ์ ๋ฌํ๋ ์ง๋ ฌ ๋ณํ๊ธฐ ๊ธฐ๋ณธ ํด๋์ค๋ฅผ ๋ง๋ญ๋๋ค. ListSerializers์ ๊ฒฝ์ฐ ์ปจํ
์คํธ๋ฅผ ์์์ ์ฐ๊ฒฐํฉ๋๋ค.
๊ทธ๋ฐ ๋ค์ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์ "omit_data"๋ฅผ ํ์ธํ๊ณ "data" ํ๋๊ฐ ์์ฒญ๋๋ฉด ์ ๊ฑฐํฉ๋๋ค.
์ฌ์ ํ ์ด์ ๋ํ ๋ต๋ณ์ ์ฐพ๊ณ ์๋ ์ฌ๋์๊ฒ ์ด๊ฒ์ด ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
์ด๊ฒ์ด ์ค๋๋ ์ค๋ ๋๋ผ๋ ๊ฒ์ ์๊ณ ์์ง๋ง __init__ ์กฐํฉ์ ์ฌ์ฉํ๊ณ ๋ด ์ฌ์ฉ์ ์ง์ ๋ฏน์ค์ธ์ ๋ฐ์ธ๋ฉํ์ฌ ์์ฒญ(์ปจํ ์คํธ์์)์ ๊ธฐ๋ฐ์ผ๋ก ์ฟผ๋ฆฌ ์ธํธ๋ฅผ ํํฐ๋งํ๋ค๋ ์ฌ์ค์ ๊ณต์ ํ๊ณ ์ถ์์ต๋๋ค. ์ด ๋ฏน์ค์ธ์ get_objects_for_user ๋ฉ์๋๊ฐ ์ฟผ๋ฆฌ ์ธํธ์ ์กด์ฌํ๋ค๊ณ ๊ฐ์ ํ๊ฑฐ๋ ๊ทธ๋ ์ง ์์ผ๋ฉด Guardian.shortcuts.get_objects_for_user๋ฅผ ์ฌ์ฉํฉ๋๋ค. POST ๋๋ PATCHing ์์๋ ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ์ ๋ํด ๋งค์ฐ ์ ์๋ํฉ๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
ํ ๊ฐ์ง ํด๊ฒฐ ๋ฐฉ๋ฒ์ ํ๋ ์ ์ธ ๋์
ParentSerializer
์__init__
๋ฉ์๋์์ ํญ์ ์ค์ฒฉ๋ChildSerializer
์ ์ธ์คํด์คํํ๋ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ฉด ์ปจํ ์คํธ๋ฅผ ์๋์ผ๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.์ด์ ๊ฐ์ด:
์ด๊ธฐ ํ ์คํธ์์ ์๋ํ๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ด๊ฒ์ ์ด๋ค ์บ์น๊ฐ ์๋์ง ํ์คํ์ง ์์ต๋๋ค.