ํ์ฌ ๊ด๊ณ๋ ๋ณด๊ธฐ์ ์ ์ฉ๋๋ ๊ฒ๊ณผ ๋์ผํ ๊ถํ ๋ฐ ํํฐ๋ง ์งํฉ์ ์๋์ผ๋ก ์ ์ฉํ์ง ์์ต๋๋ค. ๊ด๊ณ์ ๋ํ ๊ถํ์ด๋ ํํฐ๊ฐ ํ์ํ ๊ฒฝ์ฐ ๋ช ์์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค.
๊ฐ์ธ์ ์ผ๋ก ์ด ๋ฌธ์ ๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ ์ข์ ๋ฐฉ๋ฒ์ ์์ง๋ง ์ ์ด๋ ๋ฌธ์ํ๋ฅผ ๋ ์ํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ถ๋ช ํ ์์ต๋๋ค.
์ง๊ธ ์ ์๊ฒฌ์ ์ฐ๋ฆฌ๊ฐ ๊ฐ๋จํ ์์ ์ฌ๋ก๋ฅผ ์ ์ํ๊ณ ์ด๋ฅผ ๋ช ์์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ฌธ์ํํด์ผ ํ๋ค๋ ๊ฒ์ ๋๋ค. ์ด๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๋ชจ๋ ์๋ ์ฝ๋๋ ํ์ฌ ํจํค์ง ์์ฑ์๊ฐ ์ฒ๋ฆฌํ๋๋ก ๋จ๊ฒจ๋์ด์ผ ํฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ค๋ฅธ ๊ธฐ์ฌ์๊ฐ ๋ฌธ์ ๋ฅผ ํ์ํ๊ณ ์ ์ฌ์ ์ผ๋ก ํต์ฌ์ ํฌํจ๋ ์ ์๋ ์ข์ ์๋ฃจ์ ์ ์๊ฐํด ๋ผ ์ ์๋์ง ํ์ธํ ์ ์์ต๋๋ค.
๋ฏธ๋์ ์ด ๋ฌธ์ ๋ '๋ฌธ์'์์ 'ํฅ์'์ผ๋ก ์น๊ฒฉ๋ ์ ์์ง๋ง ์ 3์ ํจํค์ง์ ์ํด ๋ฐฑ์ ๋๋ ๊ตฌ์ฒด์ ์ธ ์ ์์ด ์๋ ํ ์ด ์ํ๋ก ์ ์ง๋ฉ๋๋ค.
์ฝ๋๋ฅผ ํํค์ณ ๋ณด์์ง๋ง ์ฝ๊ฒ ํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ฐพ์ง ๋ชปํ์ต๋๋ค. ์ด์์ ์ผ๋ก๋ ๋ชจ๋ ๊ด๋ จ ๊ฐ์ฒด์ ๋ํด ๊ถํ์ "has_object_permission"์ ํธ์ถํ ์ ์์ด์ผ ํฉ๋๋ค. ํ์ฌ ์ง๋ ฌ ๋ณํ๊ธฐ๋ ๊ถํ ๊ฐ์ฒด์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
ํ์ฌ ์ง๋ ฌ ๋ณํ๊ธฐ๋ ๊ถํ ๊ฐ์ฒด์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
์ด๊ฒ์ด ๊ทธ๋ ๊ฒ ๊ฐ๋จํ์ง ์๋ค๋ ๊ฒ์ ์ ์ธํ๊ณ .
_์ด๋ค_ ๊ถํ ๊ฐ์ฒด? ์ด๋ _other_ ๊ฐ์ฒด์ ๋ํ ๊ด๊ณ์ด๋ฏ๋ก ํ์ฌ ๋ณด๊ธฐ์ ๊ถํ ๋ฐ ํํฐ ํด๋์ค๊ฐ ๊ฐ์ฒด ๊ด๊ณ์ ์ ์ฉํ๋ ค๋ ๊ท์น๊ณผ ๋ฐ๋์ โโ๋์ผํ์ง๋ ์์ต๋๋ค.
ํ์ดํผ๋งํฌ๋ ๊ด๊ณ์ ๊ฒฝ์ฐ ์ด๋ก ์ ์ผ๋ก ๊ทธ๋ค์ด (+)๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ณด๊ธฐ๋ฅผ ๊ฒฐ์ ํ๊ณ ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํํฐ๋ง/๊ถํ์ ๊ฒฐ์ ํ ์ ์์ง๋ง ํ์คํ ๋์ฐํ๊ฒ ๋ฐ์ ํ๊ฒ ์ฐ๊ฒฐ๋ ๋์์ธ์ผ๋ก ๋๋ ๊ฒ์ ๋๋ค. ํ์ดํผ๋งํฌ๋์ง ์์ ๊ด๊ณ์ ๊ฒฝ์ฐ์๋ ๊ทธ๋ ๊ฒ ํ ์๋ ์์ต๋๋ค. ๊ฐ ๋ชจ๋ธ์ด ๋จ์ผ ํ์ค ๋ณด๊ธฐ์ ํ ๋ฒ ๋ ธ์ถ๋๋ค๋ ๋ณด์ฅ์ ์์ผ๋ฏ๋ก ํ์ดํผ๋งํฌ๋์ง ์์ ๊ด๊ณ์ ์ฌ์ฉํ ๊ถํ์ ์๋์ผ๋ก ๊ฒฐ์ ํ๋ ค๊ณ ์๋ํ ์ ์์ต๋๋ค.
(+) ์ค์ ๋ก๋ _ํฉ๋ฆฌ์ ์ธ_ ๋ฐฉ์์ผ๋ก ๊ทธ๋ ๊ฒ ํ๋ ๊ฒ์ด ์ค์ ๋ก ๊ฐ๋ฅํ์ง ์์ ์ ์์ง๋ง, ์ ์ ๋์ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
์๋ง๋ "has_
์ฌ๋๋ค์ ํํฐ๋ง์ ์ด๋ป๊ฒ ์ฌ์ฉํฉ๋๊น? ์ฌ์ฉ์์๊ฒ ๊ถํ์ด ์๋ ๊ฐ์ฒด๋ฅผ ์จ๊ธฐ๋ ์ฉ๋๋ก๋ง ์ฌ์ฉํ๊ณ ์์ต๋๊น? ๊ทธ๊ฒ์ด ์ ์ค ์ผ์ด์ค๋ผ๋ฉด ํํฐ๊ฐ ํ์ํ์ง ์์ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ฐธ์กฐ๋ ๋ฌธ์ #1646 ์ค ํ๋๋ ๊ด๋ จ ํ๋์ ๋ํด ํ์ ๊ฐ๋ฅํ API ํ์ด์ง์ ํ์๋๋ ์ ํ์ ์ ํํ๋ ๊ฒ์ ๋๋ค.
์ ๋ ํ์ ๊ฐ๋ฅํ API๋ฅผ ์ข์ํ๊ณ ๋ฐฑ์๋ ๊ฐ๋ฐ์์ธ ์ ๋ฟ๋ง ์๋๋ผ REST API์ ํ๋ฐํธ ์๋ ๊ฐ๋ฐ์/์ฌ์ฉ์์๊ฒ๋ ํ๋ฅญํ ๋๊ตฌ๋ผ๊ณ ์๊ฐํฉ๋๋ค. ํ์ ๊ฐ๋ฅํ API๊ฐ ์ผ์ง ์ํ๋ก ์ ํ์ ๋ฐฐ์กํ๊ณ ์ถ์ต๋๋ค(์ฆ, DEBUG ๋ชจ๋์์ ์ฌ์ดํธ๊ฐ ์ธ๋กญ์ง ์์ ๊ฒฝ์ฐ์๋ ์คํ๋จ). ๊ทธ๋ ๊ฒ ํ๋ ค๋ฉด ํ์ ๊ฐ๋ฅํ API ํ์ด์ง๋ฅผ ํตํด ์ ๋ณด๊ฐ ๋์ถ๋ ์ ์์ต๋๋ค. (๋ฌผ๋ก ํด๋น ํ์ด์ง๊ฐ ์ผ๋ฐ์ ์ผ๋ก ํ๋ก๋์ ์ค๋น๊ฐ ๋์ด ์๊ณ ์์ ํด์ผ ํ๋ค๋ ์๊ตฌ ์ฌํญ์ ์ถ๊ฐ).
์ด๊ฒ์ด ์๋ฏธํ๋ ๋ฐ๋ ๊ด๋ จ ํ๋์ ์กด์ฌ์ ๋ํ ์ ๋ณด๊ฐ POST๋ฅผ ํตํด ๋ฐฐ์ธ ์ ์๋ ๊ฒ๋ณด๋ค HTML ํ์ด์ง๋ฅผ ํตํด ๋ ์ด์ ๋ฐฐ์ธ ์ ์์ด์ผ ํ๋ค๋ ๊ฒ์ ๋๋ค.
๊ฒฐ๊ตญ ๊ด๋ จ ํ๋์ View๋ฅผ ์ฌ์ฉํ์ฌ ํํฐ๋ง์ ์ ๊ณตํ๋ ์ง๋ ฌ ๋ณํ๊ธฐ์ฉ ๋ฏน์ค์ธ ํด๋์ค๋ฅผ ๋ง๋ค์์ต๋๋ค.
class RelatedFieldPermissionsSerializerMixin(object):
"""
Limit related fields based on the permissions in the related object's view.
To use, mixin the class, and add a dictionary to the Serializer's Meta class
named "related_queryset_filters" mapping the field name to the string name
of the appropriate view class. Example:
class MySerializer(serializers.ModelSerializer):
class Meta:
related_queryset_filters = {
'user': 'UserViewSet',
}
"""
def __init__(self, *args, **kwargs):
super(RelatedFieldPermissionsSerializerMixin, self).__init__(*args, **kwargs)
self._filter_related_fields_for_html()
def _filter_related_fields_for_html(self):
"""
Ensure thatk related fields are ownership filtered for
the browseable HTML views.
"""
import views
try:
# related_queryset_filters is a map of the fieldname and the viewset name (str)
related_queryset_filters = self.Meta.related_queryset_filters
except AttributeError:
related_queryset_filters = {}
for field, viewset in related_queryset_filters.items():
try:
self.fields[field].queryset = self._filter_related_qs(self.context['request'], getattr(views, viewset))
except KeyError:
pass
def _filter_related_qs(self, request, ViewSet):
"""
Helper function to filter related fields using
existing filtering logic in ViewSets.
"""
view = ViewSet()
view.request = request
view.action = 'retrieve'
queryset = view.get_queryset()
try:
return view.queryset_ownership_filter(queryset)
except AttributeError:
return queryset
Serializer์ View๋ฅผ ํผํฉํ๋ ๋์ View mixin: #1935๋ฅผ ์ฌ์ฉํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค. ์ฌ์ ์ด ํ์ํ์ง ์๊ณ View์์ secured_fields
๋ชฉ๋ก์ ์ฌ์ฉํ์ต๋๋ค.
์ด ๊ฐ๋จํ ์ง๋ ฌ ๋ณํ๊ธฐ ๋ฏน์ค์ธ์ ์ฌ์ฉํ์ฌ ๊ด๋ จ ํ๋์ ์ฟผ๋ฆฌ ์ธํธ๋ฅผ ํํฐ๋งํ์ต๋๋ค.
class FilterRelatedMixin(object):
def __init__(self, *args, **kwargs):
super(FilterRelatedMixin, self).__init__(*args, **kwargs)
for name, field in self.fields.iteritems():
if isinstance(field, serializers.RelatedField):
method_name = 'filter_%s' % name
try:
func = getattr(self, method_name)
except AttributeError:
pass
else:
field.queryset = func(field.queryset)
์ฌ์ฉ๋ฒ๋ ๊ฐ๋จํฉ๋๋ค.
class SocialPageSerializer(FilterRelatedMixin, serializers.ModelSerializer):
account = serializers.PrimaryKeyRelatedField()
class Meta:
model = models.SocialPage
def filter_account(self, queryset):
request = self.context['request']
return queryset.filter(user=request.user)
์ด๋์? ๋ฌธ์ ๊ฐ ์๋ค๊ณ ๋ณด์ญ๋๊น?
์ ์๊ฒ๋ ์ฌ์ฉ์ ๋ฐ ์์ฒญ์ ๋ํ ๋ ผ๋ฆฌ๋ฅผ Serializer ์ธ๋ถ๋ก ์ ์งํ๊ณ View์ ๋จ๊ฒจ๋๋ ๊ฒ์ ์ข์ํฉ๋๋ค.
๋ฌธ์ ๋ ๊ด๋ จ ๋ถ์ผ์
๋๋ค. ์ฌ์ฉ์๋ ๋ณด๊ธฐ์ ์ก์ธ์คํ ์ ์์ง๋ง
๋ชจ๋ ๊ด๋ จ ๊ฐ์ฒด๊ฐ ์๋๋๋ค.
2014๋
11์ 5์ผ ์์์ผ ์คํ 6์ 16๋ถ, Alex Rothberg [email protected]
์ผ๋ค:
์ ์๊ฒ๋ ์ฌ์ฉ์์ ์์ฒญ์ ๋ํ ๋ ผ๋ฆฌ๋ฅผ
Serializer๋ฅผ ์ฌ์ฉํ๊ณ View์ ๊ทธ๋๋ก ๋ก๋๋ค.โ
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ฑฐ๋ GitHub์์ ํ์ธํ์ธ์.
https://github.com/tomchristie/django-rest-framework/issues/1985#issuecomment -61873766
.
๊ด๋ จ ์ฃผ์ ์ธ ๊ฒ ๊ฐ์ผ๋ ์ฝ์ด์ฃผ์ธ์. OPTIONS ๋ฉ์๋์ POST ๋๋ PUT ๋ฉ์๋์ ๋ํด Meta(ModelSerializer)์ ํํฐ๋ง ๊ด๋ จ ํ๋ ๊ฐ์ฒด๋ฅผ ์ด๋ป๊ฒ ๋ถ๋ฆฌํ ์ ์์ต๋๊น?
https://groups.google.com/forum/#!topic/django -rest-framework/jMePw1vS66A
model = serializers.PrimaryKeyRelatedField(queryset=Model.objects.none())๋ก ์ค์ ํ๋ฉด sealizer PrimaryKeyRelatedField "ํ๋ ์ ๋ ฅ์ ํ์ธํ ๋ ๋ชจ๋ธ ์ธ์คํด์ค ์กฐํ์ ์ฌ์ฉ๋๋ ์ฟผ๋ฆฌ ์งํฉ"์ด๊ธฐ ๋๋ฌธ์ ํ์ฌ ๊ฐ์ฒด๋ฅผ ๊ด๋ จ ๋ชจ๋ธ ์ธ์คํด์ค์ ํจ๊ป ์ ์ฅํ ์ ์์ต๋๋ค.
model = serializers.PrimaryKeyRelatedField(queryset=Model.objects.all())(ModelSerializer์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ฃผ์์ ์ถ๊ฐํ ์ ์์), ๋ชจ๋ "๊ด๋ จ" ๊ฐ์ฒด(OPTIONS๊ฐ ์์ (POST, PUT)์ ํ์ํ๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก ๊ด๋ จ์ด ์์ต๋๋ค. "๋ชจ๋ธ" ํ๋(OPTIONS ๋ฉ์๋)์ ๋ํ ์ ํ ํญ๋ชฉ์ ํ์๋๋ ๊ธฐ๋ณธ ๋ชจ๋ธ ํด๋์ค์ ์์ฑ(๊ด๋ จ ๊ฐ์ฒด๊ฐ ์๋ ์ธ์คํด์ค๊ฐ ์๋).
์ ๋ฐ์ดํธ. @cancan101 +1 . ๊ทธ๋ฌ๋ "์ฌ์ฉ์"๋ง์ด ์๋๋๋ค. ์ง๋ ฌ ๋ณํ๊ธฐ์์ ์ฟผ๋ฆฌ ์งํฉ์ ๋ณผ ์ ์๋ฏ์ด ์ด๊ฒ์ ๋ ผ๋ฆฌ์ ์ง๋ ฌ ๋ณํ๊ธฐ๋ฅผ ํผํฉํ๋ ๊ฒ์ด ์ข์ง ์๋ค๊ณ ์๊ฐํฉ๋๋ค. "serializers.PrimaryKeyRelatedField(queryset=".
๋ฌผ๋ก ์ข์ต๋๋ค.
ํด๋์ค ModelSerializer:
ํด๋์ค ๋ฉํ:
๋ชจ๋ธ=๋ชจ๋ธ
Serializer๋ Model์์ ์๋์ผ๋ก ์์ฑ๋๋ ํ๋์ ๋ฐฉ๋ฒ์ ์์์ผ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ ๋ด๊ฐ ํ๋ฆด ์ ์์ต๋๋ค.
์ด๊ฒ์ ์๋ํ๋ ๊ฒ ๊ฐ์ต๋๋ค :
class BlogSerializer(serializers.ModelSerializer):
entries = serializers.SerializerMethodField()
class Meta:
model = Blog
def get_entries(self, obj):
queryset = obj.entries.all()
if 'request' in self.context:
queryset = queryset.filter(author=self.context['request'].user)
serializer = EntrySerializer(queryset, many=True, context=self.context)
return serializer.data
@dustinfarris ๊ทธ๊ฒ์ ๊ทธ๊ฒ์ ์ฝ๊ธฐ ์ ์ฉ ํ๋๋ก ๋ง๋ค์ง๋ง ... ์๋ํฉ๋๋ค.
์ด ์ค๋ ๋์ ๊ด๋ จ๋ ๊ฒ์ผ๋ก ๋ณด์ด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ํํฐ๋ง ๋ฐฑ์๋(์ ๊ฒฝ์ฐ Django Filter)๊ฐ ํ์ฑํ๋๋ฉด ํ์ ๊ฐ๋ฅํ API๊ฐ ์ธํฐํ์ด์ค์ Filters
๋ฒํผ์ ์ถ๊ฐํ๊ณ ๋๋กญ๋ค์ด์ด ํ๋์ ์ค์ ๋ ์ฟผ๋ฆฌ ์ธํธ๋ฅผ ์กด์คํ์ง ์๋๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. ๊ทธ๋์ผ ํ ๊ฒ ๊ฐ์ต๋๋ค.
์์:
class Item(models.Model):
project = models.ForeignKey(Project)
class ItemSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
request = kwargs.get('context', {}).get('request')
self.fields['project'].queryset = request.user.project_set.all()
super(ItemSerializer, self).__init__(*args, **kwargs)
์์ ์๋ ํญ๋ชฉ ์ถ๊ฐ/ํธ์ง ์์์ ํ๋ก์ ํธ ๋๋กญ๋ค์ด์ ์ฌ๋ฐ๋ฅธ ํ๋ก์ ํธ๋ก ์ ํํ์ง๋ง ๋ชจ๋ ํญ๋ชฉ์ ์ฌ์ ํ Filters
๋๋กญ๋ค์ด์ ํ์๋ฉ๋๋ค.
nailgun์ ์ ๊ทผ ๋ฐฉ์์ ์ ์๊ฒ ๊ฝค ํจ๊ณผ๊ฐ ์์์ง๋ง ์ผ๋๋ค ๊ด๊ณ์์๋ง ์๋ํ์ต๋๋ค. ์ด์ ๋ด ๊ด๊ณ๊ฐ ManyToManyField์ธ ๋ชจ๋ธ์ด ํ๋ ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ Mixin ๋ฐฉ์์ ์๋ํ์ง ์์ต๋๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์์ด๋์ด๊ฐ ์์ต๋๊น?
@fibbs๋ ๋ค์์ ์ถ๊ฐํ์ฌ nailgun์ ์ ๊ทผ ๋ฐฉ์์ ๋ณ๊ฒฝํฉ๋๋ค.
if isinstance(field, serializers.ManyRelatedField):
method_name = 'filter_%s' % name
try:
func = getattr(self, method_name)
except AttributeError:
pass
else:
field.child_relation.queryset = func(field.child_relation.queryset)
๋ถ๋ช ํ ๋๊ตฐ๊ฐ๊ฐ ์ด์ ๋ํ ๊นจ๋ํ ์๋ฃจ์ ์ ์ ๊ณตํ์ผ๋ฉฐ ์ด์ ์ด๊ธฐํ ๋ฐฉ๋ฒ์ ํดํนํ์ง ์๊ณ ๋ ๊ฐ๋ฅํฉ๋๋ค. https://medium.com/django-rest-framework/limit-related-data-choices-with-django-rest-framework-c54e96f5815e
์ด ์ค๋ ๋๊ฐ ์ ๋ฐ์ดํธ/๋ซํ์ง ์์ ์ด์ ๊ฐ ๊ถ๊ธํฉ๋๋ค.
๋๊ตฐ๊ฐ๊ฐ ๋๋ผ๋ ๊ฒ์ ;)
์ข์ ์ง์ ์
๋๋ค. #3605๊ฐ ์ด๋ฏธ ๋ฌธ์์ ์ด์ ๋ํ ๋ด์ฉ์ ์ถ๊ฐํ๊ณ ์์ผ๋ฏ๋ก ์ด ๊ธ์ ๋ซ๊ฒ ์ต๋๋ค.
๋๊ตฐ๊ฐ๊ฐ ๋ญ๊ฐ๋ฅผ ๊ฐ์ง๊ณ ์ฌ ์ ์๋ค๋ฉด ์ฐ๋ฆฌ๋ ์ฌ์ ํ ๊ทธ ๋ถ๋ถ์ ๋ํ ์ถ๊ฐ ๊ฐ์ ์ ๊ณ ๋ คํ ๊ฒ์
๋๋ค.
์ด์ get_queryset()
๋ฉ์๋๊ฐ RelatedFields์ ๋ํด ์กด์ฌํ๋ค๋ ์ฌ์ค์ด ์ข์ต๋๋ค. ์ค์ฒฉ๋ ์ง๋ ฌ ๋ณํ๊ธฐ์๋ ์ฌ์ฉํ ์ ์๋ค๋ฉด ์ ๋ง ์ข์ ๊ฒ์
๋๋ค!
์๋ง. ๊ณ์ ํ์๊ฒ ์ต๋๊น? :)
์... ๊ต์ฅํฉ๋๋ค. ์ด๊ฒ์ด ๋ฌธ์ํ๋์ด ์๋ค๋ฉด ์ฌ๋ฐ๋ฅธ ๋ฐฉํฅ์ผ๋ก ์๋ดํด ์ฃผ์๊ฒ ์ต๋๊น? ์ด๊ฑฐ ์์๋ด๋๋ฐ ์ค๋๊ฑธ๋ ธ์ด์!
๋ค์์ ๊ตํ๋ฅผ ์ํ ๋ด ๋ฌธ์ ์ ์์ฝ์ ๋๋ค.
์ด ์์์ ๋ด ๋ชจ๋ธ ์ด๋ฆ์ "deployedEnvs" ๋ฐ "Host"์
๋๋ค.
deployEnvs์๋ ํธ์คํธ ๋ชจ๋ธ์ ๋ํ ์ธ๋ ํค๊ฐ ํฌํจ๋์ด ์์ต๋๋ค(์ฆ, ์ฌ๋ฌ deployEnvs๊ฐ ๋์ผํ ํธ์คํธ๋ฅผ ๊ฐ๋ฆฌํฌ ์ ์์). ํธ์คํธ์ ๋ํ PK๊ฐ ์๋ HOST์ fqdn ํ๋๋ฅผ ํ์ํ๊ธฐ ์ํด ์ง๋ ฌ ๋ณํ๊ธฐ๊ฐ ํ์ํ์ต๋๋ค(๋งค์ฐ ๊ฐ๋จํ ์ฌ๋ฌ๊ทธ ๊ด๋ จ ํ๋๋ฅผ ์ฌ์ฉํ์ต๋๋ค). ๋ํ FQDN ํ๋๋ก ๊ด๋ จ HOST์ ๋ํ FK ๊ฐ์ ์กฐํํ์ฌ ํธ์คํธ๋ฅผ ์ง์ ํ ์ ์๋๋ก ํ๊ธฐ ์ํด deployEnv ํญ๋ชฉ(POST)์ ์์ฑํ ๋ ํ์ํ์ต๋๋ค. ์: ์ผ์นํ๋ host.fqdn ํ๋๊ฐ ์๋ ํธ์คํธ ๊ฐ์ฒด์ PK๋ฅผ ์กฐํํ์ฌ ํ๋ ํธ์คํธ(๊ด๋ จ ํธ์คํธ ๊ฐ์ฒด์ ์ผ์นํ๋ fqdn์ผ๋ก ์ค์ )์ ํจ๊ป deployEnv๋ฅผ ๋ง๋ญ๋๋ค.
๋ถํํ๋ ํ์ฌ ์ฌ์ฉ์๊ฐ ์์ ํ ํธ์คํธ ๊ฐ์ฒด์ ๋ํ ์ ํ ํญ๋ชฉ์ผ๋ก๋ง ๋๋กญ๋ค์ด ํ์์ค์ ๋ฐํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ ํํ ์ ์์ต๋๋ค.
slugRelatedField๋ฅผ ์ฌ์ฉํ๋๋ก ์กฐ์ ๋ ์์ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
class UserHostsOnly(serializers.SlugRelatedField):
def get_queryset(self):
user = self.context['request'].user
queryset = Host.objects.filter(owner=user)
return queryset
class deployEnvSerializer(serializers.ModelSerializer):
host = UserHostsOnly(slug_field='fqdn')
์ ๋ Django์ ๋ํด ์ฝ 5๊ถ์ ์ฑ ์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ(์ํ๋ ๊ฒฝ์ฐ ๋ชจ๋ ๋์ดํ ์ ์์ต๋๋ค) ์ฐธ์กฐ ํ ์คํธ ์ค ํ๋ ์์ํฌ์ ์ด ํน์ ์์ญ/๊ธฐ๋ฅ์ผ๋ก ์์ ํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ ์ฃผ๋ ๋ฌธ์๋ ์์ต๋๋ค. ์ฒ์์๋ ์ ๊ฐ ๋ญ๊ฐ ์๋ชปํ๊ณ ์๋ ์ค ์์์ด์, ๊ทธ๋ ์ฃ ? ๋ด๊ฐ ํ๋ ค๋ ์ผ์ ํ๋ ๋ ์ข์ ๋ฐฉ๋ฒ์ด ์์ต๋๊น? ์ด ๋ฌธ์ ์ ๋ํ ์๊ฒฌ์ ๋ ์กฐํ์ง ์๋๋ก OOB์ ์ธ์ ๋ ์ง ์ฐ๋ฝํด ์ฃผ์ญ์์ค. ์๊ฐ์ ๋ด์ด ๋ด ์๊ฒฌ์ ์ฝ์ด์ฃผ์ ๋ชจ๋ ๋ถ๋ค๊ป ๊ฐ์ฌ๋๋ฆฝ๋๋ค(django ์ด๋ณด์๋ก์ ์ด๊ฒ์ ์ดํดํ๊ธฐ๊ฐ ์ ๋ง ์ด๋ ค์ ์ต๋๋ค).
@Lcstyle
DRF์ ๊ฐ Field
( Serializers
์์ฒด ํฌํจ)์๋ ๋ฐ์ดํฐ๋ฅผ ์ํ์ผ๋ก ์ง๋ ฌํํ๊ธฐ ์ํ 2๊ฐ์ง ํต์ฌ ๋ฉ์๋๊ฐ ์์ต๋๋ค(์: JSON๊ณผ Python ์ ํ ๊ฐ).
to_representation
- ๋ฐ์ดํฐ๊ฐ "๋๊ฐ๊ธฐ"to_internal_value
- "๋ค์ด์ค๋" ๋ฐ์ดํฐ๊ทํ๊ฐ ์ ๊ณตํ ๋ชจ๋ธ์ ๋๋ต์ ์ธ ๊ฐ์๋ฅผ ๋ฒ์ด๋๋ฉด ์๋๋ RelatedFields๊ฐ ์๋ํ๋ ๋ฐฉ์์ ๋ํ ๊ฐ์์ด๋ฉฐ SlugRelatedField๋ ํน์ ๋ฒ์ ์ ๋๋ค.
class UserHostsRelatedField(serializers.RelatedField):
def get_queryset(self):
# do any permission checks and filtering here
return Host.objects.filter(user=self.context['request'].user)
def to_representation(self, obj):
# this is the data that "goes out"
# convert a Python ORM object into a string value, that will in turn be shown in the JSON
return str(obj.fqdn)
def to_internal_value(self, data):
# turn an INCOMING JSON value into a Python value, in this case a Django ORM object
# lets say the value 'ADSF-1234' comes into the serializer, you want to grab it from the ORM
return self.get_queryset().get(fqdn=data)
์ค์ ๋ก๋ ์ผ๋ฐ์ ์ผ๋ก ๋ณด์๊ณผ ๊ฐ์ ๊ฒ์ ์ํด get_queryset
๋๋ to_internal_value
๋ฉ์๋์ ๋ง์ ์ํ๋ฅผ ๋ฃ๊ณ ์ถ์ต๋๋ค( django-guardian
๋๋ rules
์ ๊ฐ์ ๊ฒ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
๋ ์์ ํ ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
from rest_framework.exceptions import (
ValidationError,
PermissionError,
)
class UserHostsRelatedField(serializers.RelatedField):
def get_queryset(self):
return Host.objects.filter(user=self.context['request'].user)
def to_representation(self, obj):
return str(obj.fqdn)
def to_internal_value(self, data):
if not isinstance(data, str):
raise ValidationError({'error': 'Host fields must be strings, you passed in type %s' % type(data)})
try:
return self.get_queryset().get(fqdn=data)
except Host.DoesNotExist:
raise PermissionError({'error': 'You do not have access to this resource'})
@cancan101 ์ด ์ผ๋ง ์ ์ ์ด ๋ด์ฉ๊ณผ ๊ด๋ จํ์ฌ:
์ด ์ค๋ ๋์ ๊ด๋ จ๋ ๊ฒ์ผ๋ก ๋ณด์ด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ํํฐ๋ง ๋ฐฑ์๋(์ ๊ฒฝ์ฐ Django Filter)๊ฐ ํ์ฑํ๋๋ฉด ํ์ ๊ฐ๋ฅํ API๊ฐ ์ธํฐํ์ด์ค์ ํํฐ ๋ฒํผ์ ์ถ๊ฐํ๊ณ ๋๋กญ๋ค์ด์ด ํ๋์ ์ค์ ๋ ์ฟผ๋ฆฌ ์ธํธ๋ฅผ ์กด์คํ์ง ์๋๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. ๊ทธ๋์ผ ํ ๊ฒ ๊ฐ์ต๋๋ค.
๋ด๊ฐ ๋ณผ ์์๋ ํ ์ด๊ฒ์ ์ฌ์ ํ โโ์ฌ์ค์
๋๋ค. ์ด๊ฒ์ ๋ฐ์ดํฐ๊ฐ ๋์ถ๋๋ foreignkey ํ๋์ ๋ํ ์ฌ์ฉ์ ์ ์ Filterset
ํ๋๋ฅผ ํตํด ํด๊ฒฐํ ์ ์์ง๋ง @tomchristie ์ ๋ ์ด๊ฒ์ด ์ฌ์ ํ '์๋์ผ๋ก' ํด๊ฒฐ๋์ด์ผ ํ๊ณ ํํฐ modelchoice๋ get_queryset
๋ฐฉ๋ฒ์ ์ค์ํด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค ์ง๋ ฌ ๋ณํ๊ธฐ์ ์ฌ์ฉ์ ์ ์ ํ๋ ์ ์ธ.
์ด์จ๋ ์ถ๊ฐ ๋ฌธ์๊ฐ ํ์ํฉ๋๋ค.
์ฌ์ฉ์ ์ ์ ํํฐ ์ธํธ๋ฅผ ํตํด ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ์๋์ ๋ฌธ์ํํ๊ณ ์์ต๋๋ค.
์ํ ์์ ์ ๋ชจ๋ธ:
class WorkEntry(models.Model):
date = models.DateField(blank=False, null=True, default=date.today)
who = models.ForeignKey(User, on_delete=models.CASCADE)
...
๊ธฐ๋ณธ ๋ชจ๋ธ ๋ณด๊ธฐ ์ธํธ:
class WorkEntryViewSet(viewsets.ModelViewSet):
queryset = WorkEntry.objects.all().order_by('-date')
# only work entries that are owned by request.user are returned
filter_backends = (OnlyShowWorkEntriesThatAreOwnedByRequestUserFilterBackend, ...)
#
filter_fields = (
# this shows a filter dropdown that contains User.objects.all() - data leakage!
'who',
)
# Solution: this overrides filter_fields above
filter_class = WorkentryFilter
Custom FilterSet(๊ธฐ๋ณธ ๋ชจ๋ธ์ ๋ทฐ ์ธํธ์์ filter_class๋ฅผ ํตํด filter_fields
์ฌ์ ์)
class WorkentryFilter(FilterSet):
"""
This sets the available filters and filter types
"""
# foreignkey fields need to be overridden otherwise the browseable API will show User.objects.all()
# data leakage!
who = ModelChoiceFilter(queryset=who_filter_function)
class Meta:
model = WorkEntry
fields = {
'who': ('exact',),
}
์ฌ๊ธฐ์ ๋ฌธ์ํ๋ ์ฟผ๋ฆฌ ์ธํธ ํธ์ถ ๊ฐ๋ฅ: http://django-filter.readthedocs.io/en/latest/ref/filters.html#modelchoicefilter
def who_filter_function(request):
if request is None:
return User.objects.none()
# this solves the data leakage via the filter dropdown
return User.objects.filter(pk=request.user.pk)
@macolo
์ด ์ฝ๋๋ฅผ ์ดํด๋ณด์ญ์์ค.
์ด๊ฒ์ ๋น์ ์ด ๋งํ๋ ๋ฐ์ดํฐ ์ ์ถ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ง ์์ต๋๊น? ๋ด ๊ฒ์ ํ๋๋ ํ์ ๊ฐ๋ฅํ API์ ์์ง๋ง ๊ฒฐ๊ณผ๋ ์ฌ์ ํ ์์ ์๊ฐ ํํฐ๋งํ ์ฟผ๋ฆฌ ์ธํธ๋ก ์ ํ๋ฉ๋๋ค.
class HostsViewSet(DefaultsMixin, viewsets.ModelViewSet):
search_fields = ('hostname','fqdn')
def get_queryset(self):
owner = self.request.user
queryset = Host.objects.filter(owner=owner)
return queryset
@Lcstyle ํธ์คํธ๋ฅผ ํํฐ๋งํ๋ ค๋ ๊ฒ์ด ์๋๋ผ ๊ด๋ จ ํ๋์ ์ธ์คํด์ค๋ฅผ ํํฐ๋งํ๋ ค๊ณ ํฉ๋๋ค(์: ํธ์คํธ์ ์์ ์)
REST์์ ํด๊ฒฐํ๊ณ ์ถ์ ์ด ํน์ ๋ฌธ์ ๋ฅผ ๋ณด๊ณ ์์ต๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์์ ๋ request.user
๊ธฐ๋ฐ์
๋๋ค. ์กฐ๊ธ ๋ ๋ณต์กํ ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์ถ์ต๋๋ค.
Company
๋ฅผ ๊ฐ์ง Employees
์๊ณ Company
๊ฐ ์ด ๋ฌ์ ์ง์ ์์ฑ์ ๊ฐ์ง๊ณ ์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
class Company(Model):
employee_of_the_month = ForeignKey(Employee)
...
class Employee(Model):
company = ForeignKey(Company)
๋๋ ์ ํํ๊ธฐ ์ํด REST ์ธํฐํ์ด์ค ์ถ์ต๋๋ค employee_of_the_month
ํ์ฌ Employee
์ ๊ฐ์ company.id
๋ AS Company
.
์ด๊ฒ์ ๋ด๊ฐ ์ง๊ธ๊น์ง ์๊ฐํด ๋ธ ๊ฒ์ ๋๋ค.
class CompanySerializer(ModelSerializer):
employee_of_the_month_id = PrimaryKeyRelatedField(
source='employee_of_the_month',
queryset=Employee.objects.all())
def __init__(self, *args, **kwargs):
super(CompanySerializer, self).__init__(*args, **kwargs)
view = self.context.get('view', None)
company_id = None
if view and isinstance(view, mixins.RetrieveModelMixin):
obj = view.get_object()
if isinstance(obj, Company): # We could get the model from the queryset.
company_id = obj.id
q = self.fields['employee_of_the_month_id'].queryset
self.fields['employee_of_the_month_id'].queryset = q.filter(company_id=company_id)
...์ด ๋ฉ์๋๊ฐ ์ถ์ํ๋ ์ ์์ต๋๊น? @nailgun ์ https://github.com/encode/django-rest-framework/issues/1985#issuecomment -61871134๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค.
๋๋ ๋ํ ์๊ฐํ๊ณ ๊ทธ I ์๋ validate()
๊ฒ์ employee_of_the_month
๋ง์กฑ์ ๊ฒ์์ด๋ ์ผ์ํ๋ ค๊ณ ์ด์ ๋ด์ฅ get()
์์ ๊ฒ์์ด์ ๋ํด employee_of_the_month.id
#3605๋ฅผ ๋ณด๋ฉด ํ๋์ ๋ํ ์ฌ์ฉ์ ์ง์ ์ง๋ ฌ ๋ณํ๊ธฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ด ์์ ์ ์ํํ ์๋ ์์์ ์ ์ ์์ต๋๋ค. ์ด ๋ฌ์ ์ง์ ๋์ CEO๋ฅผ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
class CEOField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
company_id = None
view = self.context.get('view', None)
if view and isinstance(view, mixins.RetrieveModelMixin):
obj = view.get_object()
if isinstance(obj, Company):
dashboard_id = obj.id
return Employee.objects.filter(company_id=company_id)
์ด๊ฒ์ ์ฐ๋ฆฌ๊ฐ ํน์ ํ์ฌ๋ฅผ ๋ณด๊ณ ์์ง ์๋ ํ ์ ํํ ๊ฐ์ฒด๋ฅผ ๋ฐํํ์ง ์๋๋ก ํน๋ณํ ์ค๊ณ๋์์ต๋๋ค. ์ฆ, ์๋ก์ด ํ์ฌ์๋ ์ง์์ด ์๊ธฐ ์ ์๋ CEO๊ฐ ์์ ์ ์์๊ณ ํ์ฌ๊ฐ ๋ง๋ค์ด์ง๊ธฐ ์ ์๋ CEO๋ฅผ ๊ฐ์ง ์ ์์์ต๋๋ค.
์ด ์ ๊ทผ ๋ฐฉ์์ ๋ํ ๋์ ์ ์ผํ ํํ๋ ์ด๊ฒ์ด DRYer/generic์ผ๋ก ๋ง๋ค์ด์ง ์ ์๋ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค๋ ๊ฒ์ ๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ด ๊ฐ๋จํ ์ง๋ ฌ ๋ณํ๊ธฐ ๋ฏน์ค์ธ์ ์ฌ์ฉํ์ฌ ๊ด๋ จ ํ๋์ ์ฟผ๋ฆฌ ์ธํธ๋ฅผ ํํฐ๋งํ์ต๋๋ค.
์ฌ์ฉ๋ฒ๋ ๊ฐ๋จํฉ๋๋ค.
์ด๋์? ๋ฌธ์ ๊ฐ ์๋ค๊ณ ๋ณด์ญ๋๊น?