I am trying to create the view that will create new user using my API. I am using a custom model for my User and also created a model called Profile to manage data that are not authentication related.
I am new to the Django world and it can be quite difficult.
Here is my models.py
class UserManager(BaseUserManager): def create_user(self, email, password=None): if not email: raise ValueError('User must have an email address') user = self.model( email = self.normalize_email(email), ) user.set_password(password) user.save() return user def create_superuser(self, email, password): user = self.create_user(email, password=password) user.is_admin = True user.save() return user class User(AbstractBaseUser): objects = UserManager() email = models.EmailField(unique=True, db_index=True) created = models.DateTimeField('created', auto_now_add=True) modified = models.DateTimeField(auto_now=True) is_active = models.BooleanField('active', default=True) is_admin = models.BooleanField('admin', default=False) USERNAME_FIELD = 'email' ordering = ('created',) def is_staff(self): return self.is_admin def has_perm(self, perm, obj=None): return True def has_module_perms(self, app_label): return True def get_short_name(self): return self.email def get_full_name(self): return self.email def __unicode__(self): return self.email class Profile(models.Model): GENDER = ( ('M', 'Homme'), ('F', 'Femme'), ) user = models.OneToOneField(settings.AUTH_USER_MODEL) first_name = models.CharField(max_length=120, blank=False) last_name = models.CharField(max_length=120, blank=False) gender = models.CharField(max_length=1, choices=GENDER) zip_code = models.CharField(max_length=5, validators=[MinLengthValidator(5)], blank=False) def __unicode__(self): return u'Profile of user: {0}'.format(self.user.email) def create_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance) post_save.connect(create_profile, sender=User) def delete_user(sender, instance=None, **kwargs): try: instance.user except User.DoesNotExist: pass else: instance.user.delete() post_delete.connect(delete_user, sender=Profile)
Here is my serializers.py
class ProfileSerializer(serializers.ModelSerializer): class Meta: model = Profile fields = ('first_name', 'last_name', 'gender', 'zip_code',) class UserSerializer(serializers.ModelSerializer): profile = ProfileSerializer(required=True) class Meta: model = User fields = ('url', 'email', 'profile', 'created',)
And here is the views.py
class UserList(generics.ListCreateAPIView): permission_classes = (IsAuthenticatedOrWriteOnly,) serializer_class = UserSerializer def get_queryset(self): if self.request.user.is_staff: return User.objects.all() else: return self.request.user def post(self, request, format=None): serializer = ProfileSerializer(data=request.data) print serializer.__dict__ if serializer.is_valid(): print "valid" return Response("placeholder", status=status.HTTP_201_CREATED)
My goal is to be able to create the User and the Profile at the same time while being able to valid everything, how could I achieve this ?
Answer
Indeed, Django Rest Framework can’t handle this job with nested relationships then you have to implement these methods yourself. I will give you some example of what your code should look like.
Your view :
class UserList(generics.ListCreateAPIView): permission_classes = (IsAuthenticatedOrWriteOnly,) serializer_class = UserSerializer def post(self, request, format=None): 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)
Now, the save
method of your serializer will call a create
method when you want to create an object and update
method when you want to update an object. So let’s implement the create
method of your UserSerializer
which will create the profile and the user. Here is a simple example of what your UserSerializer
should look like :
class UserSerializer(serializers.ModelSerializer): profile = ProfileSerializer(required=True) class Meta: model = User fields = ('url', 'email', 'profile', 'created',) def create(self, validated_data): # create user user = User.objects.create( url = validated_data['url'], email = validated_data['email'], # etc ... ) profile_data = validated_data.pop('profile') # create profile profile = Profile.objects.create( user = user first_name = profile_data['first_name'], last_name = profile_data['last_name'], # etc... ) return user
As I said, this is an example, you have to complete it to do what you want to do but now, you know how to do it 🙂 To define the behavior during an update, implement an update
method.