Django Rest Framework : 개체 생성 후 필드 업데이트 비활성화
Django Rest Framework API 호출을 통해 내 사용자 모델을 RESTful로 만들려고합니다. 그래야 사용자를 만들고 프로필을 업데이트 할 수 있습니다.
그러나 사용자에 대해 특정 확인 프로세스를 진행할 때 사용자가 계정을 만든 후에 사용자 이름을 업데이트 할 수있는 기능을 원하지 않습니다. read_only_fields를 사용하려고했지만 POST 작업에서 해당 필드를 비활성화하는 것처럼 보였으므로 사용자 개체를 만들 때 사용자 이름을 지정할 수 없었습니다.
이것을 구현하려면 어떻게해야합니까? 현재 존재하는 API 관련 코드는 다음과 같습니다.
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'password', 'email')
write_only_fields = ('password',)
def restore_object(self, attrs, instance=None):
user = super(UserSerializer, self).restore_object(attrs, instance)
user.set_password(attrs['password'])
return user
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
serializer_class = UserSerializer
model = User
def get_permissions(self):
if self.request.method == 'DELETE':
return [IsAdminUser()]
elif self.request.method == 'POST':
return [AllowAny()]
else:
return [IsStaffOrTargetUser()]
감사!
POST 및 PUT 메서드에 대해 다른 serializer가 필요한 것 같습니다. PUT 메서드 용 serializer에서 사용자 이름 필드를 제외하거나 사용자 이름 필드를 읽기 전용으로 설정할 수 있습니다.
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
serializer_class = UserSerializer
model = User
def get_serializer_class(self):
serializer_class = self.serializer_class
if self.request.method == 'PUT':
serializer_class = SerializerWithoutUsernameField
return serializer_class
def get_permissions(self):
if self.request.method == 'DELETE':
return [IsAdminUser()]
elif self.request.method == 'POST':
return [AllowAny()]
else:
return [IsStaffOrTargetUser()]
이 질문을 확인하십시오 django-rest-framework : 동일한 URL에서 독립적 인 GET 및 PUT이지만 다른 일반보기
다른 옵션 (DRF3 만 해당)
class MySerializer(serializers.ModelSerializer):
...
def get_extra_kwargs(self):
extra_kwargs = super(MySerializer, self).get_extra_kwargs()
action = self.context['view'].action
if action in ['create']:
kwargs = extra_kwargs.get('ro_oncreate_field', {})
kwargs['read_only'] = True
extra_kwargs['ro_oncreate_field'] = kwargs
elif action in ['update', 'partial_update']:
kwargs = extra_kwargs.get('ro_onupdate_field', {})
kwargs['read_only'] = True
extra_kwargs['ro_onupdate_field'] = kwargs
return extra_kwargs
내 접근 방식은 perform_update
제네릭 뷰 클래스를 사용할 때 메서드 를 수정하는 것입니다. 업데이트가 수행 될 때 필드를 제거합니다.
class UpdateView(generics.UpdateAPIView):
...
def perform_update(self, serializer):
#remove some field
rem_field = serializer.validated_data.pop('some_field', None)
serializer.save()
최신 정보:
Rest Framework는 이미이 기능을 갖추고 있습니다. "만들기 전용"필드를 갖는 올바른 방법은 CreateOnlyDefault()
옵션 을 사용하는 것입니다.
남은 말은 Read the Docs !!! http://www.django-rest-framework.org/api-guide/validators/#createonlydefault
이전 답변 :
파티에 꽤 늦은 것 같지만 어쨌든 여기에 내 2 센트가 있습니다.
필자는 필드가 업데이트되는 것을 방지하기 위해 두 개의 서로 다른 serializer를 사용하는 것은 의미가 없습니다. 나는 이와 똑같은 문제가 있었고 내가 사용한 접근 방식 validate
은 Serializer 클래스에서 내 자신의 메서드 를 구현하는 것이 었습니다 . 제 경우에는 업데이트하고 싶지 않은 필드를이라고 owner
합니다. 다음은 관련 코드입니다.
class BusinessSerializer(serializers.ModelSerializer):
class Meta:
model = Business
pass
def validate(self, data):
instance = self.instance
# this means it's an update
# see also: http://www.django-rest-framework.org/api-guide/serializers/#accessing-the-initial-data-and-instance
if instance is not None:
originalOwner = instance.owner
# if 'dataOwner' is not None it means they're trying to update the owner field
dataOwner = data.get('owner')
if dataOwner is not None and (originalOwner != dataOwner):
raise ValidationError('Cannot update owner')
return data
pass
pass
다음은이를 검증하는 단위 테스트입니다.
def test_owner_cant_be_updated(self):
harry = User.objects.get(username='harry')
jack = User.objects.get(username='jack')
# create object
serializer = BusinessSerializer(data={'name': 'My Company', 'owner': harry.id})
self.assertTrue(serializer.is_valid())
serializer.save()
# retrieve object
business = Business.objects.get(name='My Company')
self.assertIsNotNone(business)
# update object
serializer = BusinessSerializer(business, data={'owner': jack.id}, partial=True)
# this will be False! owners cannot be updated!
self.assertFalse(serializer.is_valid())
pass
ValidationError
누군가가 잘못된 작업을 시도했다는 사실을 숨기고 싶지 않기 때문에 a를 제기 합니다. 이 작업을 원하지 않고 대신 필드를 업데이트하지 않고 작업을 완료하려면 다음을 수행하십시오.
줄을 제거하십시오.
raise ValidationError('Cannot update owner')
다음으로 대체하십시오.
data.update({'owner': originalOwner})
도움이 되었기를 바랍니다!
이 접근 방식을 사용했습니다.
def get_serializer_class(self):
if getattr(self, 'object', None) is None:
return super(UserViewSet, self).get_serializer_class()
else:
return SerializerWithoutUsernameField
Another solution (apart from creating a separate serializer) would be to pop the username from attrs in the restore_object method if the instance is set (which means it's a PATCH / PUT method):
def restore_object(self, attrs, instance=None):
if instance is not None:
attrs.pop('username', None)
user = super(UserSerializer, self).restore_object(attrs, instance)
user.set_password(attrs['password'])
return user
If you don't want to create another serializer, you may want to try customizing get_serializer_class()
inside MyViewSet
. This has been useful to me for simple projects.
# Your clean serializer
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
# Your hardworking viewset
class MyViewSet(MyParentViewSet):
serializer_class = MySerializer
model = MyModel
def get_serializer_class(self):
serializer_class = self.serializer_class
if self.request.method in ['PUT', 'PATCH']:
# setting `exclude` while having `fields` raises an error
# so set `read_only_fields` if request is PUT/PATCH
setattr(serializer_class.Meta, 'read_only_fields', ('non_updatable_field',))
# set serializer_class here instead if you have another serializer for finer control
return serializer_class
setattr(object, name, value)
This is the counterpart of getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it. For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123.
More universal way to "Disable field update after object is created" - adjust read_only_fields per View.action
1) add method to Serializer (better to use your own base cls)
def get_extra_kwargs(self):
extra_kwargs = super(BasePerTeamSerializer, self).get_extra_kwargs()
action = self.context['view'].action
actions_readonly_fields = getattr(self.Meta, 'actions_readonly_fields', None)
if actions_readonly_fields:
for actions, fields in actions_readonly_fields.items():
if action in actions:
for field in fields:
if extra_kwargs.get(field):
extra_kwargs[field]['read_only'] = True
else:
extra_kwargs[field] = {'read_only': True}
return extra_kwargs
2) Add to Meta of serializer dict named actions_readonly_fields
class Meta:
model = YourModel
fields = '__all__'
actions_readonly_fields = {
('update', 'partial_update'): ('client', )
}
In the example above client
field will become read-only for actions: 'update', 'partial_update' (ie for PUT, PATCH methods)
This post mentions four different ways to achieve this goal.
This was the cleanest way I think: [collection must not be edited]
class DocumentSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
if 'collection' in validated_data:
raise serializers.ValidationError({
'collection': 'You must not change this field.',
})
return super().update(instance, validated_data)
Another method would be to add a validation method, but throw a validation error if the instance already exists and the value has changed:
def validate_foo(self, value):
if self.instance and value != self.instance.foo:
raise serializers.ValidationError("foo is immutable once set.")
return value
In my case, I wanted a foreign key to never be updated:
def validate_foo_id(self, value):
if self.instance and value.id != self.instance.foo_id:
raise serializers.ValidationError("foo_id is immutable once set.")
return value
See also: Level-field validation in django rest framework 3.1 - access to the old value
class UserUpdateSerializer(UserSerializer):
class Meta(UserSerializer.Meta):
fields = ('username', 'email')
class UserViewSet(viewsets.ModelViewSet):
def get_serializer_class(self):
return UserUpdateSerializer if self.action == 'update' else super().get_serializer_class()
djangorestframework==3.8.2
ReferenceURL : https://stackoverflow.com/questions/22124555/django-rest-framework-disable-field-update-after-object-is-created
'developer tip' 카테고리의 다른 글
Android Open and Save files to / from Google Drive SDK (0) | 2020.12.25 |
---|---|
특정 virtualenv에 설치된 패키지 목록을 어떻게 만들 수 있습니까? (0) | 2020.12.25 |
느린 요청이있을 때 Android Volley 이중 게시 (0) | 2020.12.25 |
http.Request에서 클라이언트의 IP 주소를 가져 오는 올바른 방법 (0) | 2020.12.25 |
특정 색상을 생성하기 위해 필요한 색상 회전을 계산하는 방법은 무엇입니까? (0) | 2020.12.25 |