1

Django QuerySets return the PK of the associated objects but I need the attribute name. For example Sitting.objects.filter(complete=True).values('user') would return a QuerySet like: <QuerySet[{'user':2}, {'user':3}]>. How do I easily convert the 2 to user.get_full_name() or such?

To give an idea of what I was trying:

tests_taken = Sitting.objects.filter(complete=True).values('user')
for test in tests_taken:
   try:
      tests_taken[test]['user'] = Employee.objects.get(pk=test['user'])
   except TypeError:
      pass
print(tests_taken)

It just seems really inefficient and I am not really sure what to do from here.

This:

qs = Sitting.objects.filter(complete=True).values('user')
tests_taken = [{'user': Employee.objects.filter(pk=user).get_full_name()} for user in qs]
print(tests_taken)

Gives this error:

int() argument must be a string, a bytes-like object or a number, not 'dict'

Models:

class Sitting(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        verbose_name=_("User"),
        on_delete=models.CASCADE)

class Employee(AbstractUser):
    first_name = models.CharField(max_length=64)
    last_name = models.CharField(max_length=64)

    def get_full_name(self):
        # returns self.first_name + self.last_name

My goal:

{'user':2} should be converted to {'user':'John Doe'}

David Alford
  • 2,044
  • 3
  • 12
  • 35
  • Please show the models that you are referencing, I'm not sure what `.get_full_name()` is. It looks like this would be trivial to implement via a subquery. – Lord Elrond Jan 11 '20 at 03:15
  • `Sitting` is the model being referenced with the attribute `user`. `Employee` is the my `AbstractBaseUser` model so I would like to call `get_full_name()`, which is a built-in Django method for retrieving the users full name, and then switch the Id in the dictionary to the Employee's full name. Then I am going to pass this data to a JS chart. – David Alford Jan 11 '20 at 03:18

2 Answers2

1

Here is the optimized version of your code:

qs = Sitting.objects.filter(complete=True).values('user_id')
employees = Employee.objects.filter(id__in=qs)

Edit:

you don't really need that map statement, and It would be most efficient to call get_full_name as you iterate over your queryset. For example:

qs = Sitting.objects.filter(complete=True).values('user_id')
employees = Employee.objects.filter(id__in=qs)

for employee in employees:
    print(employee.get_full_name())

Or in a template:

{% for employee in employees %}
    <h1> {{ employee.get_full_name }}
{% endfor %}

Edit 2:

I'm confused about why you want to convert your queryset into a list of objects, but alas, Here you go:

employees = [
    {'user':user.get_full_name()} 
    for user in Employee.objects.filter(id__in=qs)
]

I'm going to assume you don't actually want your data to look like:

[{'user':'john doe'}, {'user':'jane doe'}]

Here is what I think you want:

employees = Employee.objects.filter(id__in=qs)

[{'id':user.id, 'full_name':user.full_name} for user in employees]

Or as a list of tuples:

[(user.id, user.full_name) for user in employees]
Lord Elrond
  • 13,430
  • 7
  • 40
  • 80
  • Updated it to include models – David Alford Jan 11 '20 at 03:29
  • @DavidAlford What happens when you run my query? It looks like it should be working. – Lord Elrond Jan 11 '20 at 03:35
  • Yes, it does - thank you. Would you mind explaining it a little bit? I don't know `map()` and `lambda` very well but will look into them. If you give give context that may help though. – David Alford Jan 11 '20 at 03:37
  • @DavidAlford I added an update. I'm not really sure why I used `map` ;P – Lord Elrond Jan 11 '20 at 03:42
  • 1
    @DavidAlford If you still want to learn what map does, [this question](https://stackoverflow.com/questions/10973766/understanding-the-map-function) gives a few good explanations. – Lord Elrond Jan 11 '20 at 03:44
  • This switches the user id to the user name which is fine. But I am wondering how to switch it *inside the dictionary*. I probably should have stated that before I accepted the answer since I already know how to do a lookup with the PK. – David Alford Jan 11 '20 at 03:45
  • Basically, it takes a callable (function, lambda, etc), and applies that callable to every item in a list. – Lord Elrond Jan 11 '20 at 03:45
  • @DavidAlford what do you mean? – Lord Elrond Jan 11 '20 at 03:45
  • See: `My goal:` above. – David Alford Jan 11 '20 at 03:46
  • My whole goal is to convert the QS to the proper format then convert to an array to pass to a Javascript HighChart for displaying the data. – David Alford Jan 11 '20 at 03:51
  • This has probably been my worst-phrased question yet and I apologize for that XD. I am not trying to get a list of objects, just converting the PK to the string format of the username for display. But I need it in a list of lists which I was going to handle after the dictionary looked correct :P – David Alford Jan 11 '20 at 03:54
0

You cannot use methods in values but instead, you can do like this,

queryset = Sitting.objects.filter(complete=True)
tests_taken = [{'full_name':user.get_full_name()} for user in queryset]
Saroj Rai
  • 1,419
  • 14
  • 13
  • I have updated my answer to try to use your method though I may have not given enough information at the start which resulted in shooting myself in the foot. – David Alford Jan 11 '20 at 03:20
  • Please change your qs = Sitting.objects.filter(complete=True).values('user') to qs = Sitting.objects.filter(complete=True) – Saroj Rai Jan 11 '20 at 03:24