2

I am using getattr to access properties of a model dynamically like so (Assuming the Student model has a property called name):

students = Student.objects.all()
property = 'name'


for student in students:
    print getattr(student, property)

This works fine, however I'm wondering if it is possible to access a property of a related record in the same way, for example (Assuming each Student has a related Group with a property called title):

students = Student.objects.selected_related()
property = 'group.title'


for student in students:
    print getattr(student, property)

With this, I just get the error 'Student has no attribute group.title'

Is there anyway to achieve this?

Any advice appreciated.

Thanks

Dan
  • 6,265
  • 8
  • 40
  • 56
  • 1
    Don't forget to check if the object in question has the property you're looking for with `hasattr` or handle the potential exception when you try to access an attribute that doesn't exist. – apiguy Jan 05 '12 at 23:44

4 Answers4

6

While the following code will do what you asked:

students = Student.objects.all()
attr_chain = "group.title".split(".")

for student in students:
    item = student
    for attr in attr_chain:
        item = getattr(item, attr)

    print "%s is in the group %s" % (student, item)

Depending on your needs I would suggest that you look into Django's values_list function on the Queryset class, it can shorten and simplify code in many cases.

name_attr = "name"

#If you look in the documentation you will see why I use "__" here
group_title_attr = "group__title" 

for student_name, group_title in Student.objects.all().values_list(name_attr, group_title_attr):
    print "%s is in the group %s" % (student_name, group_title)

The relevant docs are here and here.

Tom Neyland
  • 6,860
  • 2
  • 36
  • 52
  • Thanks. All good answers. This one was particularly useful for what I was working on though. – Dan Jan 06 '12 at 22:04
3

looks like you are looking for

getattr(getattr(student, property), subproperty)

you may be able to do with by looping over property.split('.')

second
  • 28,029
  • 7
  • 75
  • 76
2

you can always use from functools import reduce

and procced like this:

reduce(getattr, "some.nested.property".split("."), student)
andilabs
  • 22,159
  • 14
  • 114
  • 151
0

Unless every object in select_related has a property called 'title', that's going to blow up. What does select_related bring back for a Student model? I'm guessing it's more than just a Group object. You'd need to wrap this in a try/ except block or test the object to see if it is of the same type as Group (e.g., isinstance(x, Group)).

What are you actually trying to achieve? This seems a little tortured. Also, I would suggest a re-labelling to make things clearer:

for obj in student_related:
    # print type(obj)
    print getattr(obj, property)

You're not actually getting Student objects in that list.

Tom
  • 22,301
  • 5
  • 63
  • 96