'Get only certain fields of related object in Django

Say I have such models in Django:

class User(models.Model):
    name=models.CharField(...)
    email=models.EmailField(...)
    photo=...
    <other fields>

class Comment(models.Model):
    user=models.ForeignKey(User, ...)

I have a function that receives Comment object and needs just name and email fields to send email (it is just example, obvious)

def sendEmail(comment):
    name, email=comment.user.name, comment.user.email

In implementation presented above, Django will fetch all fields of related object User (something roughly equal to select * from users where id=comment.user_id) Using values_list I can fetch just needed fields:

Users.objects.filter(id=comment.user_id).values_list('name', 'email')

but values_list only applicable to QuerySet object, not to model instance. My question is: is there any way to do the same thing using only "comment" object? It must be equal to select name, email from users where id=comment.user_id by complexity (I don't want to transfer a lot of data stored in some fields over network when I don't need it)



Solution 1:[1]

If you want to have comments with some additional user data, without having to retrieve whole User objects I believe it's better to fetch additional data when you fetch the comments:

Comment.objects.filter(user=user).values_list('user__name', 'user__email')

Obviously you can fetch other useful Comment fields.

OR:

Comment.objects.filter(user=user).annotate(author_name=F('user__name'),\ 
                                           author_email=F('user__email'))

This still uses the QuerySet API, but both approaches allow you to span relationships to get additional data without extra queries.

Solution 2:[2]

It must be equal to select name, email from users where id=comment.user_id by complexity.

What is really equivalent to this query is what you already have:

Users.objects.filter(id=comment.user_id).values_list('name', 'email')

... but values_list only applicable to QuerySet object, not to model instance.

That is absolutely right. but what you need to consider is that once you have this queryset you can get the first element because with one user id there will just be one user:

name, email = Users.objects.filter(id=comment.user_id).values_list('name', 'email').first()

Solution 3:[3]

Users.objects.filter(id=comment.user_id).values_list('name', 'email').get()

That should do

Solution 4:[4]

queryset = ModelName.objects.filter().only('realed_obj__field1', 'related_obj__field2')

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 AKS
Solution 3 Otuoma Sanya
Solution 4 Md Fahad Hossain