Reputation: 78
I am working on genericAPIViews in DRF. I am using a built in user model with UserProfile model having one to one relation with it. But I am unable to create user due to nested serializer. My question is that how I can create my built in User model and Profile User model at the same time as UserProfile model is nested in User model.Here is my code:
Models.py
USER_CHOICE = (
('SS', 'SS'),
('SP', 'SP')
)
LOGIN_TYPE = (
('Local', 'Local'),
('Facebook', 'Facebook'),
('Google', 'Google')
)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
cell_phone = models.CharField(max_length=15, blank=True, default="", null=True)
country = models.CharField(max_length=50, blank=True, default="", null=True)
state = models.CharField(max_length=50, blank=True, default="", null=True)
profile_image = models.FileField(upload_to='user_images/', default='', blank=True)
postal_code = models.CharField(max_length=50, blank=True, default="", null=True)
registration_id = models.CharField(max_length=200, null=True, blank=True, default=None)
active = models.BooleanField(default=True)
# roles = models.ForeignKey(Role, null=True, on_delete=models.CASCADE, related_name='role', blank=True)
user_type = models.CharField(max_length=50, choices=USER_CHOICE, null=True, blank=True)
login_type = models.CharField(max_length=40, choices=LOGIN_TYPE, default='local')
reset_pass = models.BooleanField(default=False)
confirmed_email = models.BooleanField(default=False)
remember_me = models.BooleanField(default=False)
reset_code = models.CharField(max_length=200, null=True, blank=True, default="")
reset_code_time = models.DateTimeField(auto_now_add=True, blank=True)
longitude = models.DecimalField(max_digits=80, decimal_places=10, default=0.00)
latitude = models.DecimalField(max_digits=80, decimal_places=10, default=0.00)
r_code = models.CharField(max_length=15, null=True, blank=True)
refer_user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="user_refer")
referred = models.ManyToManyField(User, related_name="user_referred", null=True, blank=True)
otp = models.CharField(max_length=6, blank=True, default="", null=True)
def __str__(self):
return self.user.username
Seralizers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import UserProfile
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer()
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name', 'profile']
Views.py
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]
I know there is .create() method which can be override according to DRF documentation. Plus I want to override this method in views.py. Is there any approach to do this. Thanks in advance for your addition in my knowledge.
Upvotes: 0
Views: 1790
Reputation: 1964
There are two ways of doing that in my knowledge.
The first one is overriding the create method of the generic view. Which is as follows:
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
#let django create the user with generic view
response = super().create(request, *args, **kwargs)
# response.data contains the serialized data of the created user
user_data = response.data
user_id = response.data.get("id")
profile = UserProfile()
profile.user = User.objects.get(id=user_id)
profile.save()
# return the response created by the generic view
return response
Be careful that Django Rest Framework does not support the create
method for nested serializers. Set the profile field to read-only in UserSerializer
class UserSerializer(serializers.ModelSerializer):
profile = SerializerMethodField()
class Meta:
model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name', 'profile']
read_only_fields = ('profile',)
def get_profile(self, user):
profile = UserProfile.objects.filter(user=user).first()
return UserProfileSerializer(profile).data if profile is not None else None
The second way is using signals. Signals is an event dispatcher that keeps track of events such as update, delete, m2m changed, etc. Here, we can use post_save
signals which trigger whenever a model gets updated. So if the User
model is created, we will receive a signal and therefore crate a UserProfile
for that user. You can learn more about signals from the Django Signals page.
@receiver(post_save, sender=User, dispatch_uid="create_user_profile")
def on_user_create(sender, instance, created, **kwargs):
# here instance is the user object that just got created
# created is a boolean value which will be true if the record is newly created
if created:
profile = UserProfile()
profile.user = instance
profile.save()
Upvotes: 1