Django-rest框架如何定义每个ViewSet操作所需的字段

2024-04-26 01:08:35 发布

您现在位置:Python中文网/ 问答频道 /正文

我在尝试DRF的教程,发现有些东西很混乱。 我有一个简单扩展的用户模型授权用户像这样

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:

^{pr2}$

在我的视图.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时,它告诉我用户名和email也是必需的。在

然后我将以下代码添加到我的UserSerializer

        extra_kwargs = {'country': {'required': False},
                    'password': {'required': False},
                    'username': {'required': False},
                    }

现在上面的问题已经解决了,但是当我试图创建一个用户时,我确实需要用户名和电子邮件。在

有没有方法可以指定每个操作需要哪些字段? 例如,对于my set_password(),我需要密码字段。在

谢谢


Tags: 用户selffalsetrueismodelsemailrequest
2条回答

结果我自己实现了它,所以我基本上选择了all fields选项,但是在actions中,我添加了一个decorator来确保请求主体中有那些指定的键。在

装饰工:

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

使用方法:

^{pr2}$

我想这不太雅致,但对我来说可能已经足够了。在

尝试重写序列化程序构造函数以基于额外参数修改字段。没有测试,但它应该可以工作:

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参数:

^{pr2}$

相关问题 更多 >