Melissa Stewart
Melissa Stewart

Reputation: 3605

Attribute Error while creating new User object in django

This is my customized User Object in django.

class User(AbstractBaseUser, PermissionsMixin):
    mobile = models.CharField(max_length=100, unique=True)
    email = models.EmailField(max_length=255, null=True)
    username = models.CharField(max_length=255, null=True)
    full_name = models.CharField(max_length=255, blank=True, null=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    location = models.ForeignKey(Location, on_delete=models.SET_NULL, null=True)

    USERNAME_FIELD = 'mobile'
    REQUIRED_FIELDS = []
    objects = UserManager()

And this is the UserManager,

class UserManager(BaseUserManager):

    def create_user(self, mobile, email=None, username=None, full_name=None, password=None, is_staff=False,
                    is_superuser=False):
        if not mobile:
            raise ValueError("Can't create User without a mobile number!")
        if not password:
            raise ValueError("Can't create User without a password!")
        user = self.model(
            mobile=mobile,
            email=self.normalize_email(email),
            username=username,
            full_name=full_name,
            is_staff=is_staff,
            is_superuser=is_superuser,
        )
        user.set_password(password)
        user.save(self._db)
        return user

This is my UserSerializer Class

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)
    id = serializers.IntegerField(read_only=True)

    class Meta:
        model = models.User
        fields = (
            'id',
            'mobile',
            'email',
            'username',
            'full_name',
            'password',
        )

And this is the view where I'm trying to register a User.

class RegisterView(views.APIView):
    def post(self, request):

        serialized = UserSerializer(data=request.data)
        if serialized.is_valid():
            user = UserManager().create_user(mobile=serialized.mobile, email=serialized.email, username=serialized.email, full_name=serialized.full_name, password=serialized.password)
            if user:
                return Response(serialized.data, status=status.HTTP_201_CREATED)
            else:
                return Response(serialized.errors, status=status.HTTP_400_BAD_REQUEST)

I end up getting the following error message,

AttributeError at /api/v1/bouncer/register/
'UserSerializer' object has no attribute 'mobile'

But of course I've a mobile attribute. What am I doing wrong here?

Upvotes: 0

Views: 174

Answers (1)

Antoine Catton
Antoine Catton

Reputation: 391

.mobile, .email, ... are not on the Serializer object but on the instance. UserSerializer(data=...) returns a Serializer instance. (not a model instance)

If you want to keep your code the solution is to do:

UserManager().create_user(
    mobile=serialized.validated_data['mobile'],
    email=serialized.validated_data['email'],
    ...

But this way, you're not taking advantage of the serializer.

Personally, I would get rid of the UserManager. (By the way, the best way to create a manager is to inherit from django.db.models.Queryset and then do object = UserQueryset.as_manager())

I would write a serializer, like yours:

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)
    id = serializers.IntegerField(read_only=True)

    class Meta:
        model = models.User
        fields = (
            'id', 'mobile', 'email', 'username', 'full_name', 'password',
        )

    def create(self, validated_data):
        password = validated_data.pop('password')
        user = super().create(validated_data)
        user.set_password(password)
        return user

Then in your view, you just have to do:

serializer = UserSerializer(data=request.data)
if serializer.is_valid():
    serializer.save()
    return Response(...)
else:
    return Response(...)

Also, instead of writing a full function, you could use a generic API view. (CreateAPIView is most likely what you want.)

N.B.: Everything is pseudocode, I have not tested it, but the solution should be extremely similar delta some small changes

Upvotes: 1

Related Questions