2

I have following Schema in Django with PostgreSQL.

Class Person (models.Model):
    name = models.CharField(max_length=255)
    email= models.CharField(max_legth = 255)
    created_at = models.DateTimeField()

Class PersonTask(models.Model):
   person = models.ForeignKey(Person)
   title = models.TextField()
   created_at = models.DateTimeField()

Class PersonHistory(models.Model):
   person = models.ForeignKey(Person)
   note = models.TextField()
   created_at = models.DateTimeField()

Now I need to query the DB like all values of Person with latest PersonTask__title as max_task and latest PersonHistory__note as max_note

Eg:

<Queryset: [
{name: "abc" ,email:"abc@gmail.com",created_at :"2019-01-02", max_task:"This is my latest tasktitle" , max_note: "This is my latest history note"},
{name: "abcd" ,email:"abcd@gmail.com",created_at :"2019-03-02", max_task:"This is my latest tasktitle for abcd" , max_note: "This is my latest history note for abcd"}
]>

But, I could max get is either id of Latest Task and Latest History by

Person.objects.filter(customer_id= 1).\
               annotate( max_task = Max('persontask')).\
               annotate(max_note = Max('personhistory')).\
               order_by('-id')

Or a random task or note texts using below query

Person.objects.filter(customer_id= 1).\
               annotate( max_task = Max('persontask__title')).\
               annotate(max_note = Max('personhistory__note')).\
               order_by('-id')

How this can be tackled??

JithPS
  • 1,167
  • 1
  • 7
  • 19

2 Answers2

1

As you did not mention the ForeignKeys between these models, I suspect that Task and History have FK to Person in field named person.

I would use Subquery with combination of OuterRef to tackle this query

from django.db.models import OuterRef, Subquery

result = (
    Person.objects
        .filter(customer_id=1)
        .annotate(
            task_title=Subquery(Task.objects.filter(person=OuterRef('pk')).order_by('-created_at').values('title')[:1]),
            history_note=Subquery(HistoryNote.objects.filter(person=OuterRef('pk')).order_by('-created_at').values('note')[:1])
        )
        .order_by('-id')
)
Kryštof Řeháček
  • 1,965
  • 1
  • 16
  • 28
  • Yeah you are right about the DB models. I have updated the Models. But May I know, which version of Django will support this one? – JithPS Apr 17 '20 at 17:39
  • I've looked to the django docs and it looks it's supported even in 1.11 :-) https://docs.djangoproject.com/en/1.11/ref/models/expressions/#referencing-columns-from-the-outer-queryset – Kryštof Řeháček Apr 17 '20 at 17:42
  • Thank you so much. Yeah I have tried them and are working fine – JithPS Apr 17 '20 at 17:45
  • 1
    I did :-) . One more doubt is there any option to get date along with the title or task?? – JithPS Apr 17 '20 at 17:50
  • Sure, you can take more values from the subquery like Subquery(Task.objects.filter(person=OuterRef('pk')).order_by('-created_at').values('title', 'created_at')[:1]) – Kryštof Řeháček Apr 17 '20 at 21:50
  • I tried it but I was not able to assign the value to two variables. – JithPS Apr 18 '20 at 02:50
0

If tasks and history are related with person you need a relationship between your models. something like this.

class PersonTask(models.Model):
    person = models.ForeignKey(Person, related_name="tasks", on_delete=models.CASCADE)
    title = models.TextField()
    created_at = models.DateTimeField()

class PersonHistory(models.Model):
    person = models.ForeignKey(Person, related_name="histories", on_delete=models.CASCADE)
    note = models.TextField()
    created_at = models.DateTimeField()

And then you could get last task and last history:

person = Person.objects.get(name="name")

person.tasks.last()
person.histories.last()
bertinhogago
  • 339
  • 2
  • 6
  • Models Have been updated. But I need the latest task and history as an annotated qiueryset for all the lead objects – JithPS Apr 17 '20 at 17:38