Not getting value of other attributes using select_related in django
Question:
I am trying to fetch the values from TypingTest model. The foreign key is referencing to the UserProfile model. While fetching the data I want the username to be displayed in the result of the query as well. So, after researching a bit I found out that select_related could be a way of doing this. But when I am trying to use select_related. I am not able to see the username, Only the value of the primary key is getting shown.
These are the two models that I have.
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
userName = models.CharField(max_length=26,unique=True)
email = models.EmailField(max_length=254,unique=True)
profilePhoto = models.ImageField(default="/default.png")
rank = models.CharField(max_length=26,default="Novice")
def __str__(self):
return self.userName
class TypingTest(models.Model):
user = models.ForeignKey(UserProfile,on_delete=models.CASCADE)
time = models.IntegerField()
wpm = models.IntegerField()
accuracy = models.IntegerField()
raw = models.IntegerField()
dateTaken = models.DateTimeField(auto_now_add=True)
Correspoding serializer class:
class TypingTestSerializer(serializers.ModelSerializer):
class Meta :
model = TypingTest
fields = ('id','user','time','wpm','accuracy','raw','dateTaken')
class UserSerializer(serializers.ModelSerializer):
class Meta :
model = UserProfile
fields = ('userName','email','profilePhoto','rank')
This is the view code that I have written.
@protected_resource()
@api_view(['GET'])
def getAllTests (request):
testListQuerySet = models.TypingTest.objects.all()
selectRelated = models.TypingTest.objects.select_related('user').only("userName")
print(selectRelated.values())
serializer=serializers.TypingTestSerializer(models.TypingTest.objects.select_related('user'),many=True)
return Response(serializer.data)
The output that I am getting is :
<QuerySet [{'id': 1, 'user_id': 3, 'time': 60, 'wpm': 60, 'accuracy': 91, 'raw': 79, 'dateTaken': datetime.datetime(2023, 2, 14, 21, 7, 32, 721899, tzinfo=datetime.timezone.utc)}, {'id': 2, 'user_id': 4, 'time': 30, 'wpm': 82, 'accuracy': 99, 'raw': 85, 'dateTaken': datetime.datetime(2023, 2, 14, 21, 33, 45, 326814, tzinfo=datetime.timezone.utc)}]>
Please let me know if this is the right way of doing the thing intended and if not what is?
Answers:
select_related
is just to fetch related in the same query and preventing to make a new DB hit for accessing FK. Read about it here.
In order to get user data, you need no get it, and select_related
cause no more DB hit. You can get it by this way for example:
user = models.TypingTest.objects.select_related('user').first().user
And if you want to represent it in your response, you have to add user
to your seriliazer fields:
class TypingTestSerializer(serializers.ModelSerializer):
class Meta:
model = TypingTest
fields = ("user", ...)
if you want to get a username of all objects then you have to query like this
TypingTest.objects.select_related('user').values('userName')
To include the userName
field in the output, you should use the UserSerializer
to serialize the related UserProfile
object instead. Currently, you should use the source
argument to specify the related field name in the TypingTest
model, and the UserSerializer
to serialize the related object so:
class TypingTestSerializer(serializers.ModelSerializer):
userName = serializers.CharField(source='user.userName')
class Meta:
model = TypingTest
fields = ('id','user','userName','time','wpm','accuracy','raw','dateTaken')
Then in view, remove the select_related
method call as it is not necessary so:
@protected_resource()
@api_view(['GET'])
def getAllTests (request):
testListQuerySet = models.TypingTest.objects.all()
serializer = serializers.TypingTestSerializer(testListQuerySet, many=True)
return Response(serializer.data)
I am trying to fetch the values from TypingTest model. The foreign key is referencing to the UserProfile model. While fetching the data I want the username to be displayed in the result of the query as well. So, after researching a bit I found out that select_related could be a way of doing this. But when I am trying to use select_related. I am not able to see the username, Only the value of the primary key is getting shown.
These are the two models that I have.
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
userName = models.CharField(max_length=26,unique=True)
email = models.EmailField(max_length=254,unique=True)
profilePhoto = models.ImageField(default="/default.png")
rank = models.CharField(max_length=26,default="Novice")
def __str__(self):
return self.userName
class TypingTest(models.Model):
user = models.ForeignKey(UserProfile,on_delete=models.CASCADE)
time = models.IntegerField()
wpm = models.IntegerField()
accuracy = models.IntegerField()
raw = models.IntegerField()
dateTaken = models.DateTimeField(auto_now_add=True)
Correspoding serializer class:
class TypingTestSerializer(serializers.ModelSerializer):
class Meta :
model = TypingTest
fields = ('id','user','time','wpm','accuracy','raw','dateTaken')
class UserSerializer(serializers.ModelSerializer):
class Meta :
model = UserProfile
fields = ('userName','email','profilePhoto','rank')
This is the view code that I have written.
@protected_resource()
@api_view(['GET'])
def getAllTests (request):
testListQuerySet = models.TypingTest.objects.all()
selectRelated = models.TypingTest.objects.select_related('user').only("userName")
print(selectRelated.values())
serializer=serializers.TypingTestSerializer(models.TypingTest.objects.select_related('user'),many=True)
return Response(serializer.data)
The output that I am getting is :
<QuerySet [{'id': 1, 'user_id': 3, 'time': 60, 'wpm': 60, 'accuracy': 91, 'raw': 79, 'dateTaken': datetime.datetime(2023, 2, 14, 21, 7, 32, 721899, tzinfo=datetime.timezone.utc)}, {'id': 2, 'user_id': 4, 'time': 30, 'wpm': 82, 'accuracy': 99, 'raw': 85, 'dateTaken': datetime.datetime(2023, 2, 14, 21, 33, 45, 326814, tzinfo=datetime.timezone.utc)}]>
Please let me know if this is the right way of doing the thing intended and if not what is?
select_related
is just to fetch related in the same query and preventing to make a new DB hit for accessing FK. Read about it here.
In order to get user data, you need no get it, and select_related
cause no more DB hit. You can get it by this way for example:
user = models.TypingTest.objects.select_related('user').first().user
And if you want to represent it in your response, you have to add user
to your seriliazer fields:
class TypingTestSerializer(serializers.ModelSerializer):
class Meta:
model = TypingTest
fields = ("user", ...)
if you want to get a username of all objects then you have to query like this
TypingTest.objects.select_related('user').values('userName')
To include the userName
field in the output, you should use the UserSerializer
to serialize the related UserProfile
object instead. Currently, you should use the source
argument to specify the related field name in the TypingTest
model, and the UserSerializer
to serialize the related object so:
class TypingTestSerializer(serializers.ModelSerializer):
userName = serializers.CharField(source='user.userName')
class Meta:
model = TypingTest
fields = ('id','user','userName','time','wpm','accuracy','raw','dateTaken')
Then in view, remove the select_related
method call as it is not necessary so:
@protected_resource()
@api_view(['GET'])
def getAllTests (request):
testListQuerySet = models.TypingTest.objects.all()
serializer = serializers.TypingTestSerializer(testListQuerySet, many=True)
return Response(serializer.data)