我正在尝试DRF的教程,我发现了一些困惑。 我有一个用户模型,它只是像这样扩展auth.User
class User(DefaultUser):
"""
Represents a registered User
"""
EMAIL_VALIDATOR_LENGTH = 6
email_validated = models.BooleanField(default=False)
# using a 6-digit numbers for email validation
email_validator = models.CharField(
max_length=6,
default=_get_random_email_validator(EMAIL_VALIDATOR_LENGTH),
editable=False
)
phone_number = models.CharField(validators=[phone_regex],
blank=True, null=True, max_length=64)
# country is required
country = models.ForeignKey('Country', null=False, blank=False)
# subdivision is optional
subdivision = models.ForeignKey('Subdivision', null=True, blank=True)
然后我有我的基本UserSerializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'email', 'password', 'email_validated',
'email_validator', 'country', 'subdivision', 'phone_number',
'last_login', 'is_superuser', 'username', 'first_name',
'last_name', 'is_staff', 'is_active', 'date_joined')
在我的views.py中,我有UserViewSet:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@detail_route(methods=['get', 'post'], url_path='validate-email')
def validate_email(self, request, pk):
user = self.get_object()
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user.is_active = True
user.save()
return Response({'status': 'email validated'})
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@detail_route(methods=['post'], url_path='set-password')
def set_password(self, request, pk):
pass
@list_route()
def test_list_route(self, request):
pass
问题是,在validate_email中,我实际上只需要pk但是当我测试API时,它告诉我还需要用户名和电子邮件。
然后我将以下代码添加到我的UserSerializer
extra_kwargs = {'country': {'required': False},
'password': {'required': False},
'username': {'required': False},
}
现在上述问题已经消失,但是当我尝试创建用户时,我确实想要用户名和电子邮件。
有没有办法可以指定每个操作需要哪些字段? 例如,对于我的set_password(),我想要输入密码字段。
谢谢,
答案 0 :(得分:0)
尝试覆盖序列化器构造函数以根据额外参数修改字段。没有测试,但它应该工作:
class UserSerializer(ModelSerializer):
def __init__(self, *args, **kwargs):
super(UserSerializer, self).__init__(*args, **kwargs)
require_password = kwargs.get('require_password', False)
require_email = kwargs.get('require_email', False)
if require_password:
self.fields['password'].required = True
if require_email:
self.fields['email'].required = True
然后在需要时传递require_password
或/和require_email
个参数:
serializer = UserSerializer(data=request.data, require_password=True, require_email=True)
答案 1 :(得分:0)
我自己实现了它,所以我基本上都选择了所有字段,但是在动作中,我添加了一个装饰器来确保请求体中有指定的键。
装饰:
class AssertInRequest(object):
"""
A decorator to decorate ViewSet actions, this decorator assumes the first
positional argument is request, so you can apply this decorator any methods
that the first positional argument is request.
This decorator itself takes a list of strings as argument, and will check
that the request.data.dict() actually contains these keys
For example, if you have a action to set user password which you expect that
in the request body should have 'password' provided, use this decorator for
the method like
@detail_route()
@AssertInRequest(['password'])
def set_password(self, request, pk):
pass
"""
def __init__(self, keys):
self.keys = []
for key in keys:
if hasattr(key, 'upper'):
self.keys.append(key.lower())
def __call__(self, func):
def wrapped(*args, **kwargs):
if self.keys:
try:
request = args[1]
except IndexError:
request = kwargs['request']
if request:
json_data = get_json_data(request)
for key in self.keys:
if key not in json_data or not json_data[key]:
return DefaultResponse(
'Invalid request body, missing required data [%s]' % key,
status.HTTP_400_BAD_REQUEST)
return func(*args, **kwargs)
return wrapped
如何使用它:
@detail_route(methods=['post'], url_path='set-password', permission_classes=(IsAuthenticated,))
@AssertInRequest(['password'])
def set_password(self, request, pk):
user = self.get_object()
json_data = get_json_data(request)
user.set_password(json_data['password'])
user.save()
return DefaultResponse(_('Successfully set password for user %s'
% user.email), status.HTTP_200_OK)
我想这不是很优雅,但对我来说可能已经足够了。