Search code examples
djangoauthenticationdjango-rest-frameworkjwtdjango-registration

"This field is required." DRF


Began to deal with DRF. I want to authenticate users with jwt token. But nothing happens. I’ve come across the problem that when creating a user, I get: {"user": {"email": ["This field is required."], "Username": ["This field is required."], "Password": ["This field is required."]}}. Although all the fields are filled.

models

class UserManager(BaseUserManager):
    def create_user(self, username, email, password=None):
        if username is None:
            raise TypeError('Users must have a username.')
​
        if email is None:
            raise TypeError('Users must have an email address.')
​
        user = self.model(username=username, email=self.normalize_email(email))
        user.set_password(password)
        user.save()

        return user
​
​
class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(db_index=True, max_length=255, unique=True)
    email = models.EmailField(db_index=True, unique=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    bio = RichTextUploadingField(_('bio'), blank=True)
    city = models.CharField(_('city'), max_length=100, blank=True)
​
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']
​
    objects = UserManager()
​
    @property
    def token(self):
        return self._generate_jwt_token()
​
    def _generate_jwt_token(self):
        dt = datetime.now() + timedelta(days=60)
​
        token = jwt.encode({
            'id': self.pk,
            'exp': int(dt.strftime('%s'))
        }, settings.SECRET_KEY, algorithm='HS256')

        return token.decode('utf-8')

serializers

class RegistrationSerializer(serializers.ModelSerializer):
    password = serializers.CharField(
        max_length=128,
        min_length=8,
        write_only=True
    )
    token = serializers.CharField(max_length=255, read_only=True)
​
    class Meta:
        model = User
        fields = ['email', 'username', 'password', 'token']

​
    def create(self, validated_data):
        return User.objects.create_user(**validated_data)

view

class RegistrationAPIView(APIView):
    permission_classes = (AllowAny,)
    renderer_classes = (UserJSONRenderer,)
    serializer_class = RegistrationSerializer
​
    def post(self, request):
        print('request.data: \t\t', request.data)         #<QueryDict: {'{\n    "email": "sasasas@sasas.ssas",\n    "username": "sasasasas",\n    "password": "12345678"\n}': ['']}>
        print('request.data.get("user", {}): \t', request.data.get('user', {}))            #{}
        user = request.data.get('user', {})
​
        serializer = self.serializer_class(data=user)
        serializer.is_valid(raise_exception=True)     #Error
        serializer.save()
​
        return Response(serializer.data, status=status.HTTP_201_CREATED)

Solution

  • I didn't read the tutorial in detail, but it seems that for some reason the payload you post to the endpoint should be of a different shape than what DRF usually expects.

    There is one thing we need to fix though. Notice how the response from the "Register" request has all of the user's information at the root level. Our client expects this information to be namespaced under "user." To do that, we'll need to create a custom DRF renderer.

    So instead of posting this:

    { 
      "email": "user@example.com",
      "username": "user",
      "password": "hunter2"
    }
    

    You have to nest it all under the "user" key.

    {
      "user": { 
        "email": "user@example.com",
        "username": "user",
        "password": "hunter2"
      }
    }