2009-02-18 2 views
3

Хорошо, у меня есть представление Django, как это:Джанго - запросы, сделанные повтор/неэффективные

@render_to('home/main.html') 
def login(request): 
    # also tried Client.objects.select_related().all() 
    clients = Client.objects.all() 
    return {'clients':clients} 

И у меня есть шаблон, main.html, как это:

<ul> 
{% for client in clients %} 
<li>{{ client.full_name }}</li> 
    <ul> 
    {% for pet in client.pets.all %} 
     <li>{{ pet.full_name }}</li> 
    {% endfor %} 
    </ul> 
{% endfor %} 
</ul> 

Я также распечатать все запросы в sql_queries внизу моего базового шаблона. Когда я запускаю эту точку зрения, следующие запросы сделаны:

SELECT `home_client`.`id`, ... FROM `home_client`; 
SELECT `home_pet`.`id`, ... FROM `home_pet` WHERE `home_pet`.`client_id` = 1; 
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 1; 
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 1; 
SELECT `home_pet`.`id`, ... FROM `home_pet` WHERE `home_pet`.`client_id` = 2; 
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 2; 

Мой вопрос, почему все эти запросы делаются? Разве это не должно быть 1 запрос для извлечения всех клиентов и запроса на клиента для извлечения всех домашних животных от каждого клиента? У меня есть 2 клиента в таблице home_client, поэтому это должно быть 3 запросов. Больше всего беспокоит то, что запросы 3 и 4 на 100% идентичны. Я не хочу «преждевременно оптимизировать» или что-то еще, но я хочу убедиться, что Django не дико неэффективен. Любая помощь по этому поводу будет оценена по достоинству. Благодарю.

ответ

3

Есть ли у клиента 1 есть 2 домашних животных и клиент 2 есть 1 домашнее животное?

Если это так, это указывает на то, что Pet.full_name или что-то еще, что вы делаете в цикле просмотра домашних животных, пытается получить доступ к его связанным с ним деталям клиента. ORM Django не использует identity map, поэтому для доступа к внешнему ключу клиента из любого из ваших объектов Pet потребуется снова нажать базу данных для извлечения этого клиента.

P.S. select_related не повлияет на данные, которые вы используете в этом сценарии, поскольку он следует только за отношениями с внешними ключами, но отношения «один за другим» - это много-к-одному.

Update: если вы хотите, чтобы избежать необходимости изменить логику в Pet.full_name или необходимости выполнения указанной логики в шаблоне вместо для этого случая, вы можете изменить способ, вы получите ручку на домашних животных каждого клиента для того, чтобы предварительное заполнение кэша ForeignKey с для каждого питомца с его Клиентом:

class Client(models.Model): 
    # ... 
    def get_pets(self): 
     for pet in self.pets.all(): 
      setattr(pet, '_client_cache', self) 
      yield pet 

... где 'client''_client_cache' части является то, что имя атрибута используется в классе Pet для ForeignKey Клиента животного.Это использует то, как Django реализует доступ к объектам, связанным с ForeignKey, используя свой класс SingleRelatedObjectDescriptor, который ищет этот атрибут кэша перед запросом базы данных.

В результате использования шаблона:

{% for pet in client.get_pets %} 
... 
{% endfor %} 
+0

Да, у Pet есть только имя, фамилия от фамилии клиента. Является ли это большой проблемой, что это происходит, или кэш Django, и все, что делает хорошую работу, что я могу спать по ночам, чтобы получить доступ к последней фамилии так, как я, или мне лучше всего использовать собственное имя? –

+0

Django делает некоторое кэширование, но кеш для каждого клиента Pet находится в экземпляре Pet. Что вы делаете, у вас будет (1 + количество клиентов + количество домашних животных) запросов для этой страницы. С моим предложением на месте, вы закончите с (1 + количество запросов клиентов). С S.Lott's: 2 вопроса. –

+0

Благодарим за помощь. Пробовал код. Я буду взвешивать свои варианты здесь. –

7

В Django используется кеш. СУРБД использует кеш. Не досрочно оптимизируйте запросы.

Вы можете играть с объемными запросами в своей функции просмотра вместо одноразовых запросов в своем шаблоне.

@render_to('home/main.html') 
def login(request): 
    # Query all clients 
    clients = Client.objects.all() 
    # Assemble an in-memory table of pets 
    pets = collections.defaultdict(list) 
    for p in Pet.objects.all(): 
     pets[pet.client].append(p) 
    # Create clients and pets tuples 
    clientsPetTuples = [ (c,pets[c]) for c in clients ] 
    return {'clientPets': clientsPetTuples} 

Однако, похоже, у вас нет доказательств того, что ваш шаблон является самой медленной частью вашего приложения.

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

Не беспокойтесь о SQL, пока не получите доказательства.

+0

на объемных запросов вы имеете в виду делает select_related() в представлении? –

+0

Спасибо за образец. Я знаю, что преждевременная оптимизация - это зло, я в основном задавался вопросом, почему это происходит, хотел убедиться, что это не моя вина. –

+0

Это не ваша вина, пока вы не измерите производительность. После того, как вы измерили и доказали, что запросы медленные, тогда это ваша вина. Пока вы не измеряете, вы на самом деле ничего не знаете. –

4

попробуйте использовать Client.objects.all(). Select_related()

Это автомагический также кэш связанные модели в одном запросе к базе данных.

Смежные вопросы