#30653: Annotate a whole ORM object with Subquery instead of just a column
-------------------------------------+-------------------------------------
               Reporter:  Ivaylo     |          Owner:  Ivo Donchev
  Donchev                            |
                   Type:  New        |         Status:  assigned
  feature                            |
              Component:  Database   |        Version:  2.2
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 It would be very useful to have a QuerySet method that annotates whole ORM
 object instead of separate columns.


 == Use cases

 1. `select_related` does not support using annotations of selected object.
 Example:

 {{{
 class User(Model):
     first_name = CharField(max_length=20)
     last_name = CharField(max_length=20)

     objects = UserQueySet.as_manager()


 class UserQuerySet(QuerySet):
     def with_annotated_full_name(self):
         return self.annotate(
             full_name=ExpressionWrapper(
                 expression=OuterRef('first_name') + Value(' ') +
 OuterRef('last_name')
                 output_field=CharField()
             )
         )


 class Car(Model):
     brand = CharField(max_length=20)
     owner = ForeignKey(User)


 users_with_annotated_full_names = User.objects.with_annotated_full_name()
 # each user will have `.full_name`
 cars_with_users = Car.objects.select_realted('user')  # No way to get the
 `user.full_name` annotation :(


 # With annotated object it'll look like:
 cars_with_users_with_annotated_full_name = Car.objects.annotate_object(
     field_name='owner_with_annotated_full_name',
     queryset=User.objects.filter(id=OuterRef('owner'))[:1]
 )



 for car in cars_with_users_with_annotated_full_name:
     print(car.user.full_name)  # :)
 }}}


 2. You need to make a query for every object to get some non-related
 object. Example:

 {{{
 class Teacher(Model):
     name = CharField(max_length=20)
     grade = IntegerField()


 class Student(Model):
     name = CharField(max_length=20)
     grade = IntegerField()


 def get_students_with_their_teacher():
     # Students must be ditributed between 3 teachers according to `grade`
 so each student should know it's teacher
     return Student.objects.annotate_object(
         field_name='teacher',
         queryset=Teacher.objects.filter(grade=OuterRef('grade'))[:1]
     )

 students = get_students_with_their_teacher()

 for student in students:
     print(student.teacher)  # == Teacher.objects.get(grade=...) but not
 making an extra query
 }}}


 Having this as additional method in the ORM may be really useful when you
 want to preserve the declarative syntax of Django ORM when using List
 APIs/Views and avoid additional methods that attaches objects dynamically
 and mutates the ORM objects

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30653>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/054.c582ecbb0b281feccd4198e1b0108d96%40djangoproject.com.

Reply via email to