Графа против лен на объект QuerySet в Django



в Django, учитывая, что у меня есть QuerySet, который я собираюсь перебирать и печатать результаты, какой лучший вариант для подсчета объектов? лен (qs) или qs.граф()?



(также учитывая, что подсчет объектов в одной итерации не является опцией)

566   3  

3 ответов:

хотя Django docs рекомендую использовать count, а не len:

Примечание: не используйте len() на QuerySets, если все, что вы хотите сделать, это определить количество записей в наборе. Гораздо эффективнее обрабатывать счетчик на уровне базы данных, используя SQL SELECT COUNT(*), и Django предоставляет count() способ именно по этой причине.

так как вы все равно повторяете этот запрос,результат будет кэшированный (если вы не используете iterator), и поэтому предпочтительнее будет использовать len С это позволяет избежать попадания в базу данных снова, а также, возможно, получения другого количества результатов!).
Если вы используете iterator, тогда я бы предложил включить переменную подсчета, когда вы повторяете (а не используете count) по тем же причинам.

Я думаю, что с помощью len(qs) имеет больше смысла здесь, как вам нужно перебирать результаты. qs.count() лучший вариант, если все, что вы хотите сделать, это распечатать счет и не перебрать результаты.

len(qs) попал в базу данных select * from table, тогда как qs.count() попадет в БД с select count(*) from table.

и qs.count() вернет целое число, и вы не можете перебирать его

выбирая между len() и count() зависит от ситуации, и стоит глубоко понять, как они работают, чтобы правильно их использовать.

позвольте мне дать вам несколько сценариев:

  1. (самое важное) когда вы только хотите знать количество элементов, и вы не планируете обрабатывать их в любом случае важно использовать count():

    DO:queryset.count() - это будет выполнять один SELECT COUNT(*) some_table запрос, все вычисления выполненный на стороне РСУБД, Python просто нужно получить номер результата с фиксированной стоимостью O (1)

    НЕТ:len(queryset) - это будет выполнять SELECT * FROM some_table запрос, извлекающий всю таблицу O(N) и требующий дополнительной памяти O (N) для ее хранения. это самое худшее, что можно сделать

  2. когда вы собираетесь получить queryset в любом случае это немного лучше использовать len() который не вызовет дополнительный запрос к базе данных как count() бы:

    len(queryset) # fetching all the data - NO extra cost - data would be fetched anyway in the for loop
    
    for obj in queryset: # data is already fetched by len() - using cache
        pass
    

    Граф:

    queryset.count() # this will perform an extra db query - len() did not
    
    for obj in queryset: # fetching data
        pass
    
  3. Реверсированный 2-й случай (когда queryset уже был извлечен):

    for obj in queryset: # iteration fetches the data
        len(queryset) # using already cached data - O(1) no extra cost
        queryset.count() # using cache - O(1) no extra db query
    
    len(queryset) # the same O(1)
    queryset.count() # the same: no query, O(1)
    

все станет ясно, как только вы взглянете "под капот":

class QuerySet(object):

    def __init__(self, model=None, query=None, using=None, hints=None):
        # (...)
        self._result_cache = None

    def __len__(self):
        self._fetch_all()
        return len(self._result_cache)

    def _fetch_all(self):
        if self._result_cache is None:
            self._result_cache = list(self.iterator())
        if self._prefetch_related_lookups and not self._prefetch_done:
            self._prefetch_related_objects()

    def count(self):
        if self._result_cache is not None:
            return len(self._result_cache)

        return self.query.get_count(using=self.db)

хорошие ссылки в Django docs:

Comments

    Ничего не найдено.