2016-08-19 1 views
0

Вот мои модели:Джанго QuerySet на несколько внешних ключей с использованием prefetch_related

class Owner(): 
    last_name = models.CharField(max_length=30) 
    ... 

class Species(): 
    species_code = models.CharField(max_length=10) 
    ... 

class Pet(): 
    client = models.ForeignKey(Owner, related_name='pet_fk') 
    species = models.ForeignKey(Species) 
    .... 

Я хочу, чтобы перечислить всех владельцев и их питомцев. У некоторых владельцев нет домашних животных, у других их много.

Если найдено домашнее животное, я хочу аннотировать дополнительное «временное» поле css_species_class объекту этого животного. Это поле будет возвращать «собака», если вид_код для модели для животных «CANINE» или «лошадь», если «EQUINE» и т. Д.

Поле «временное» необходимо из-за многоязычности сайта и значения css_species_class требуется, чтобы вытащить соответствующие значки символов в шаблоне. Я не могу использовать значение, хранящееся напрямую, поэтому мне нужно вставить определенное значение, чтобы оно соответствовало значению, ожидаемому глифами.

Что-то вроде:

Owner: John Smith 
Pet: Saag (css_species_class='dog') 
Pet: Brinjal (css_species_class='cat') 
Pet: Baji (css_species_class='dog') 

Owner: Sue Smith 
Pet: none 

Owner: Clare Smith 
Pet: Aloo (css_species_class='horse') 

Мой шаблон что-то вроде этого:

{% for owner in owners %} 
    <tr> 
     <td>{{ owner.first_name }} {{ owner.last_name }}</td> 
     <td> <!-- loop over pet objects --> 
      {% for pet in owner.pet_fk.all %} 
       <div> 
        .... 
        <span class="glyphicons glyphicons-{{ pet.css_species_class }}"></span> 
        .... 
       </div> 
      {% endfor pet %} 
     </td> 
    </tr> 
{% endfor %} 

Итак, вот моя первая попытка в растворе:

class OwnerListView(ListView): 
    template_name = 'visitors/owner_list.html' 
    context_object_name = 'owners' 
    paginate_by = 50 

    def get_queryset(self): 
     owners_with_pets = Owner.objects.filter(pet_fk__isnull=False).prefetch_related('pet_fk').distinct() 
      # logic goes here to loop over pets 
      # and assign 'css_species_class' temp field 

     owners_without_pets = Owner.objects.filter(pet_fk__isnull=True).prefetch_related('pet_fk').distinct() 

Then 'слияния' два запроса:

 result_list = sorted(
      chain(owners_with_pets, owners_without_pets), 
      key=attrgetter('last_name')) 
      return result_list 

Это «работает» для небольшого числа владельцев, но если я тестирую с реальным числом (около 4000), я получаю «слишком много ошибок в переменной sql».

Первоначально я попытался сделать это в одном запросе (прежде чем решить разбить его на два запроса), но это не так эффектно и для большего числа клиентов.

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

ответ

0

Пробуйте этот код, не проверенный, но я полагаю, что он работает.

class Owner(): 
    last_name = models.CharField(max_length=30) 
    ... 

class Species(): 
    species_code = models.CharField(max_length=10) 
    ... 

class Pet(): 
    # related_name is used for backward relation 
    # this will end up as owner."related_name" --> owner.pets 
    client = models.ForeignKey(Owner, related_name='pets') 
    species = models.ForeignKey(Species) 
    ... 

    @property 
    def css_species_class(self): 
     # this could be anything you want eg: css_scecies_class 
     return self.species.species_code 


class OwnerListView(ListView): 
    template_name = 'visitors/owner_list.html' 
    context_object_name = 'owners' 
    paginate_by = 50 

    def get_queryset(self): 
     # no need to check if onwner instance has pets if you chain them both back 
     return Owner.objects.prefetch_related('pets').all().distinct() 

{% for owner in owners %} 
    <tr> 
     <td>{{ owner.first_name }} {{ owner.last_name }}</td> 
     <td> <!-- loop over pet objects --> 
      {% for pet in owner.pets %} 
       <div> 
        .... 
        <!-- now we can access pet.css_species_class directly because we made it a property of pet class --> 
        <span class="glyphicons glyphicons-{{ pet.css_species_class }}"></span> 
        .... 
       </div> 
      {% endfor pet %} 
     </td> 
    </tr> 
{% endfor %} 
+0

Это сработало красиво, спасибо. –