5

I'm trying to create a custom user using the Django Rest Framework. I got it to the point to where I can create a regular user, but I'm unsure on how to extend it to the custom user model.

models.py:

from django.db import models
from django.contrib.postgres.fields import ArrayField
from django.contrib.auth.models import User


class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    languages = ArrayField(models.CharField(max_length=30, blank=True))

serializers.py:

from rest_framework import serializers
from django.contrib.auth.models import User


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

    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email', 'username', 'password')

    def create(self, validated_data, instance=None):
        user = super(UserSerializer, self).create(validated_data)
        user.set_password(validated_data['password'])
        user.save()
        return user

views.py:

@api_view(['POST'])
@permission_classes((AllowAny,))
def create_user(request):
    serialized = UserSerializer(data=request.data)
    if serialized.is_valid():
        serialized.save()
        return Response(serialized.data, status=status.HTTP_201_CREATED)
    else:
        return Response(serialized._errors, status=status.HTTP_400_BAD_REQUEST)

How do I extend the languages field to the UserSerializer to where it can process it? Would it be better to create a signal for every time a user is created it then creates a userprofile automatically?

Evan Bloemer
  • 1,051
  • 2
  • 11
  • 31

1 Answers1

4

You can do it two ways, either by creating a profile Serializer or without it. With Serializer,

class UserProfileSerializer(serializers.ModelSerializer):
    languages = serializers.ListField(child=serializers.CharField(max_length=30, allow_blank=True))

    class Meta:
        model = UserProfile
        fields = ('languages',)

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)
    userprofile = UserProfileSerializer(required=False)

    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email', 'username', 'password', 'userprofile')


    def create(self, validated_data, instance=None):
        profile_data = validated_data.pop('userprofile')
        user = User.objects.create(**validated_data)
        user.set_password(validated_data['password'])
        user.save()
        UserProfile.objects.update_or_create(user=user,**profile_data)
        return user

without a profile serializer

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)
    languages = serializers.ListField(child=serializers.CharField(max_length=30, allow_blank=True), source="userprofile.languages")

    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email', 'username', 'password', 'lanugages')


    def create(self, validated_data, instance=None):
        profile_data = validated_data.pop('userprofile')
        user = User.objects.create(**validated_data)
        user.set_password(validated_data['password'])
        user.save()
        UserProfile.objects.update_or_create(user=user,**profile_data)
        return user
Saji Xavier
  • 2,132
  • 16
  • 21
  • For the first solution, I'm getting a `{ "profile": [ "This filed is required"] }` response from postman when I send a post request with `languages` as a field and also with `profile` as a field. And then with the second solution, I'm getting a `module 'rest_framework.serializers' has no attribute 'ArrayField'` error. – Evan Bloemer Dec 07 '17 at 10:54
  • For the first solution, you need to send it as 'profile': {'languages':[]} , for second you need DRF 3.1 and above versions beacuse ArrayField support is included 3.1 . what is your DRF version ? – Saji Xavier Dec 07 '17 at 11:06
  • The version is 3.7.3. I'm trying your suggestion for the `profile`, but it still isn't working. I have the `key` as `profile` and the `value` as `{'languages': ["test"]}` and it gives me the same output. – Evan Bloemer Dec 07 '17 at 11:25
  • I got the second one to work by changing the `ArrayField` to `ListField`, but now it is saying `Cannot assign "{'languages': ['"test","test"']}": "User.userprofile" must be a "UserProfile" instance.` and it's a problem with the line `user = super(UserSerializer, self).create(validated_data)` – Evan Bloemer Dec 07 '17 at 12:23
  • I tried that and I'm still getting the same error. Any idea on why the `profile` solution isn't working? – Evan Bloemer Dec 07 '17 at 12:35
  • there is a typo .. it is 'userprofile' not 'profile' – Saji Xavier Dec 07 '17 at 12:54
  • Sorry, can you elaborate? If I change "profile" to "userprofile", it doesn't change anything but the variable names. I still end up getting the "This field is required" error. – Evan Bloemer Dec 07 '17 at 13:05
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160714/discussion-between-python-noob-and-saji-xavier). – Evan Bloemer Dec 07 '17 at 13:09