#python #django #orm
Вопрос:
У меня возникла путаница в том, как написать запрос django для получения моих данных. У меня есть 2 таблицы «билет» и «ticket_details«. Ниже приведена схема для них.
Ticket(id, name, type, user) TicketDetails(ticket_id, message, created_time)
Примечание. С одним идентификатором билета может быть связано несколько сообщений.
А ticket_id-это внешний ключ к таблице билетов.
Я хотел бы получить все столбцы из обеих таблиц, в которых для определенного идентификатора билета должно быть выбрано только последнее сообщение из таблицы TicketDetails.
Example: Ticket id, name, type, user 1,install, application, usr1 TicketDetails ticket_id, message, creted_time 1, lt;message1gt;, 12:00 PM 1, lt;message2gt;, 04:00 PM 2, lt;message3gt;, 05:00 PM --gt;latest entry Expected Output: id, name, type, user, message, created_time 1, install, application, usr1, lt;message3gt;, 05:00PM
Заранее спасибо
Комментарии:
1. Можете ли вы привести формальный пример ?
2. Вы имеете в виду, что хотите получать данные из обеих таблиц на основе
ticket_id
?3.
Ticket.objects.filter(id=1234).select_related('TicketDetails').latest('created_time')
??? дайте мне знать, работает это или нет4. @k33da_the_bug нет, это не работает. Я добавил пример, если это поможет вам понять, о чем я спрашиваю
Ответ №1:
Я сделал некоторые предположения о ваших моделях, вы не предоставили никаких:
class Ticket(models.Model): name = models.CharField(max_length=50) type = models.CharField(max_length=50) user = models.ForeignKey('auth.User', on_delete=models.CASCADE) # Model names should NEVER end with "s" class TicketDetail(models.Model): ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE) message = models.CharField(max_length=50) created_time = models.DateTimeField(auto_now_add=True)
У вас есть 2 варианта:
- вы можете написать это на чистом sql, вы потеряете возможность фильтровать
sql = """ SELECT ticket.id, ticket.name, ticket.type, ticket.user_id, detail.message FROM {ticket} ticket LEFT JOIN ( SELECT detail.ticket_id, detail.message FROM {detail} detail INNER JOIN ( SELECT MAX(id) id, ticket_id FROM {detail} GROUP BY ticket_id ) detail_message ON detail.id = detail_message.id ) detail ON detail.ticket_id = ticket.id """.format(ticket=Ticket._meta.db_table, detail=TicketDetail._meta.db_table) tickets = Ticket.objects.raw(sql) for ticket in tickets: print(ticket.id, ticket.message)
- Напишите это в стиле «джанго»
latest_messages = TicketDetail.objects.values('ticket_id').annotate(id=models.Max('id')).values('id') tickets = Ticket.objects.prefetch_related(models.Prefetch('ticketdetail_set', TicketDetail.objects.filter(id__in=latest_messages))).order_by('id') for ticket in tickets: print(ticket.id) # this iteration will only ever yield 1 result.. or nothing. for detail in ticket.ticketdetail_set.all(): print(detail.message)
Вот тесты:
# uses factoryboy and faker to fill in the data class UserFactory(factory.django.DjangoModelFactory): class Meta: model = auth.models.User django_get_or_create = ('username',) first_name = fake.first_name() last_name = fake.last_name() email = factory.LazyAttribute(lambda obj: "{}.{}@gmail.com".format(obj.last_name, obj.first_name).lower()) username = factory.Sequence(lambda n: 'user' str(n)) class SimpleTestCase(TestCase): def setUp(self): ticket1 = Ticket.objects.create(user=UserFactory(), type='A', name='Number 1') TicketDetail.objects.create(ticket=ticket1, message='you wont see this') TicketDetail.objects.create(ticket=ticket1, message='you wont see this either') TicketDetail.objects.create(ticket=ticket1, message='YES!!') ticket2 = Ticket.objects.create(user=UserFactory(), type='B', name='Number 2') TicketDetail.objects.create(ticket=ticket2, message='you also wont see this') TicketDetail.objects.create(ticket=ticket2, message='you also wont see this either') TicketDetail.objects.create(ticket=ticket2, message='also YES!!') def test_flatten_pure_sql(self): sql = """ SELECT ticket.id, ticket.name, ticket.type, ticket.user_id, detail.message FROM {ticket} ticket LEFT JOIN ( SELECT detail.ticket_id, detail.message FROM {detail} detail INNER JOIN ( SELECT MAX(id) id, ticket_id FROM {detail} GROUP BY ticket_id ) detail_message ON detail.id = detail_message.id ) detail ON detail.ticket_id = ticket.id """.format(ticket=Ticket._meta.db_table, detail=TicketDetail._meta.db_table) self.assertEquals(['YES!!', 'also YES!!'], [x.message for x in Ticket.objects.raw(sql)]) def test_orm_way(self): latest_messages = TicketDetail.objects.values('ticket_id').annotate(id=models.Max('id')).values('id') tickets = Ticket.objects.prefetch_related(models.Prefetch('ticketdetail_set', TicketDetail.objects.filter(id__in=latest_messages))).order_by('id') self.assertEquals(['Number 1', 'Number 2'], [x.name for x in tickets]) self.assertEquals(['YES!!'], [x.message for x in tickets[0].ticketdetail_set.all()]) self.assertEquals(['also YES!!'], [x.message for x in tickets[1].ticketdetail_set.all()])
Комментарии:
1. Спасибо за ваш ответ. Теперь я все понимаю.