简体   繁体   中英

How to implement nested serializer in django rest framework?

I was extending User model of django auth table and implementing rest api for the same.

I'm not getting how to implement GET/POST request for the same.

My models.py code is:

class UserProfile(models.Model):
    """User profile model for information about user."""

    users = models.OneToOneField(User)
    phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'")
    phone_number = models.CharField(max_length=100, validators=[phone_regex], blank=True)
    created_timestamp = models.DateTimeField(auto_now_add=True, null=True)
    updated_timestamp = models.DateTimeField(auto_now=True, null=True)

My serializers.py code is:

class UserSerializer(serializers.ModelSerializer):
    """Serializer for users."""

    class Meta:
        model = User


class UserProfileSerializer(serializers.ModelSerializer):
    """Serializer for user profiles."""

    users = UserSerializer(many=True)

    class Meta:
        model = UserProfile

    def create(self, validated_data):
        users_data = validated_data.pop('users')
        print 'yes'
        print users_data
        user_profile = UserProfile.objects.create(**validated_data)
        for user_data in users_data:
            user_data, created = User.objects.get_or_create(first_name=user_data['first_name'], last_name=user_data['last_name'],
        username=user_data['username'], password=user_data['password'], email=user_data['email'], is_active=['is_active'])
        user_profile.users.add(user_data)
    return user_profile

My v1.py code is :

class UserProfileList(APIView):
    """Get and post user profiles data."""

    def get(self, request, format=None):
        """Get users."""
        user_profiles = UserProfile.objects.all()
        serialized_user_profiles = UserProfileSerializer(user_profiles, many=True)
        return Response(serialized_user_profiles.data)

    def post(self, request, format=None):
        """Post users."""
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

I think the problem is in v1.py file. I want to make GET and POST request, for POST request I want to send JSON data. Can someone help me out in its implementation. Actually, I want single endpoint for making POST request and storing data in both User model and UserProfile model.

Modifying the above code

models.py

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    phone_number = models.CharField(max_length=100, validators=[
    RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'")
    ], blank=True, null=True)
    created_timestamp = models.DateTimeField(auto_now_add=True, null=True)
    updated_timestamp = models.DateTimeField(auto_now=True, null=True)

Serializers.py

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User

class UserProfileSerializer(serializers.ModelSerializer):
    user = UserSerializer(required=True)
    class Meta:
        model = UserProfile

    def create(self, validated_data):
        user_data = validated_data.pop('user', None)
        user = User.objects.create_user(**user_data)
        return UserProfile.objects.create(user=user, **validated_data)

   def update(self, instance, validated_data):
       user_dict = validated_data.pop('user', None)
       if user_dict:
           user_obj = instance.user
           for key, value in user_dict.iteritems():
                setattr(user_obj, key, value)
           user_obj.save()
           validated_data["user"] = user_obj
       for key, value in validated_data.iteritems():
           setattr(instance, key, value)
       instance.save()
       return instance

viewsets.py

from rest_framework import mixins
from rest_framework import viewsets

class MyUserViewSet(mixins.UpdateModelMixin,
                    mixins.CreateModelMixin,
                    mixins.ListModelMixin,
                    viewsets.GenericViewSet):

    authentication_classes = (TokenAuthentication, SessionAuthentication)
    permission_classes = (AllowAny, )
    serializer_class = UserProfileSerializer
    queryset = UserProfile.objects.all()

Follow this link routers

routers.py

from rest_framework import routers
router = routers.SimpleRouter()
router.register('users', MyUserViewSet)

urls.py

from .routers import router
urlpatterns = patterns(
    url(r'^api/v1/', include(router.urls)),
)

I would suggest you extend the User model by subclassing AbstractBaseUser, which requires also subclassing BaseUserManager. That way you will be able to simplify your serializer by having only one class for it. It also requires subclassing of the BaseUserManager.

Custom users can have as many custom fields as you want. It is generally simpler to extend the user model this way than creating a one-to-one relation to the default user model. It saves you some logic and some time.

You can read more here: https://docs.djangoproject.com/ja/1.9/topics/auth/customizing/#specifying-a-custom-user-model

Here's an example of how you would do subclass BaseUserManager:

class MyUserManager(BaseUserManager):
    def create_user(self, email=None, password=None, dateofbirth=None, username=None):
        user = self.model(
            email=MyUserManager.normalize_email(email),    
            dateofbirth=dateofbirth,
            username=username
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, dateofbirth, email, password):
        user = self.create_user(email=email,
                    password=password,
                    username=username,
                    dateofbirth=dateofbirth,
                )
        user.is_superuser = True
        user.save(using=self._db)
        return user

Here's an example of subclassing AbstractBaseUser:

class MyUser(AbstractBaseUser):

    email = models.EmailField(
        verbose_name='email address',
        max_length=255,
        unique=True
    )

    dateofbirth = models.CharField(max_length=30, blank=True)

    is_active = models.BooleanField(default=True)
    is_superuser = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'

    objects = MyUserManager()

    def get_full_name(self):
        return self.email

    def get_short_name(self):
        return self.email

    def __unicode__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True

    @property
    def is_staff(self):
        return self.is_superuser

For this to work you will have to set the auth user model in your settings.py, to let your django app know you are using a custom user model:

AUTH_USER_MODEL = 'myapp.MyUser'

Here's the simple part - the serializer:

class MyUserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, required=True)

    class Meta:
        model = MyUser
        fields = (
            'email',
            'password',
            'dateofbirth',
            'username',
        )

    def create(self, validated_data):
        password = validated_data.pop("password", None)
        email = validated_data.pop("email", None)
        username = validated_data.pop("username", None)
        user = MyUser.objects.create_user(email=email, password=password, username=username, gender=gender, dateofbirth=dateofbirth)
        MyUser.objects.filter(id=user.id).update(**validated_data)
        return user

While you're at it, forget about the APIView and use the much simpler ViewSet:

class MyUserViewSet(viewsets.ModelViewSet):
    authentication_classes = (TokenAuthentication, SessionAuthentication)
    permission_classes = (AllowAny, )
    serializer_class = MyUserSerializer

And with this alone you have GET, POST, PUT, DELETE requests all handled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM