Получение нового объекта для людей - PullRequest
1 голос
/ 25 мая 2011

Итак, у меня есть модель Person, а затем у меня есть название модели Carusage.Соответствующая часть Carusage такова:

class Carusage(models.Model):
    person = models.ForeignKey(Person)
    start = models.DateTimeField()
    end = models.DateTimeField(null=True, blank=True)

Человек может взять машину, а затем система создает новый экземпляр Carusage и сохраняет его при запуске в качестве текущего времени.Затем, когда человек возвращает автомобиль, текущее время сохраняется до конца.

Теперь в моем коде у меня есть список моделей людей, и я хочу получить самую новую дату в Carusage для каждого человека.Так что, если Человек только что вернул машину, я бы хотел, чтобы конечное поле новейшего Карусели было связано с этим человеком, и, если у Человека все еще есть машина, я хотел бы, чтобы начальное поле.

Предпочтительно яЯ хотел бы сделать это в одном SQL-операторе, так как мой Person-list может вырасти довольно большим (нижние границы ~ 10, верхние границы ~ 10.000).Я попробовал что-то вроде этого:

Carusage.objects.filter(person__in(person_list)).exclude(start__gte(time_now))

И тогда думал о комментировании, но не мог придумать, как мне поступить.

Так что в настоящее время я делаю это:

time_now = datetime.datetime.now()
time_list = []
for p in person_list:
    latest = Carusage.objects.filter(person=p).exclude(start__gte=time_now).only('start', 'end').latest('start')
    try:
        if latest.end<time_now:
            time=latest.end
        else:
            raise
    except:
        time=latest.start
    time_list.append(time)

Очевидно, мой код работает медленно (около 5 секунд для списка из 500 человек).Каков будет «способ django» для выполнения этих / этих запросов?Две вещи, которых я хотел бы достичь: только один раз попасть в базу данных для Carusage (по крайней мере, не в len (person_list) раз) и получить только соответствующее время из базы данных (нужно только самое новое время ...).Есть ли способ добиться этого?

Ответы [ 2 ]

2 голосов
/ 25 мая 2011

У вас есть два отдельных результата, которые вы объединяете с Союзом.

  1. Машины возвращены. И время начала и окончания. Есть (возможно) много машин на человека, и вы хотите только одну из этих машин. Даже в чистом SQL это довольно сложный запрос, требующий предложения HAVING и приводящий к (потенциально) низкой производительности.

  2. Автомобили еще не возвращены.

Часто вы будете счастливы с двумя отдельными запросами, когда у вас есть два отдельных правила.

На самом деле вы выполняете агрегацию CarUsage, сгруппированную по Person, у которого начало (или конец) равно максимальному значению группы.

returned = Person.objects.filter( carusage__end__isnull=True ).annotate(Max('carusage__start'))
not_returned = Person.objects.filter( carusage__end__isnull=False ).annotate(Max('carusage__end'))

Я думаю, это то, что вы ищете.

2 голосов
/ 25 мая 2011

Используя исключения таким образом, вы ГАРАНТИРУЕТЕСЬ, чтобы получить медленный код.Исключения ужасно дороги и потенциально могут сильно замедлить ваш код (до 5-10 раз медленнее с исключениями).Вместо того, чтобы вызывать и перехватывать исключение, просто используйте else:

if latest.end<time_now:
   time=latest.end
else:
   time=latest.start

Также не могли бы вы просто использовать фильтр order_by от Django?Например, чтобы получить все «Carusages» для данного заказа человека по начальному значению, вызовите:

Carusage.objects.filter(person=p).order_by('-start').all()
...