Александр Кошелев
Александр Кошелев Python-разработчик

qs-refactor уже в транке!

Наконец-то! Уверен, что я не один воскликнул это слово, когда узнал что долгожданная queryset-refactor ветка влилась в транк и теперь ORM в джанго стал ещё лучше и удобней! Этого момента ждали долга, поскольку это один из самых больших шагов по направлению к 1.0 версии джанги. Итак, что же мы получили.

Начну с основных изменения уже имеющегося функционала:

  • Указание сортировки по при-join-ненным моделям теперь стало более логичным и совпадает с фильтрами lookup’ов. Просто пример:

     Order.objects.all().order_by( "product__price" )
    
  • Обновлена реализация __iter__ метода queryset’a. И не грузит все строки результат в память сразу.

  • Особенно меня радует: сделана нормальная обработка None значения в lookup’ах. Т.е. если раньше приходилось писать писать воркэраунды для случая когда селект осуществлялся по NULL значениям, например так:

     if value is None:
         queryset = queryset.filter( field__isnull = True )
     else:
         queryset = queryset.filter( field = value )
    

То теперь эта проверка лишняя не нужна. Ура!)

  • Есть ещё некоторое количество изменений, но они на низко уровне и на прямую не влияют на использование. Хотя именно они окончательно убивают некоторые баги, которые были в джанговском ORM до сего момента.

Помимо изменений, есть ещё и целый ряд дополнений к возможностям ORM. Вот основные из них:

  • Добавлена возможность производить обновление(UPDATE) нескольких объектов за раз. Это очень удобная возможность улучшить производительность достаточно частых ситуаций. Т.е. раньше для обновления объектов надо было “падать” до самого SQL и писать запросы вручную или делать совсем не оптимальные вещи типа таких:

     for entry in Entry.objects.filter( name = "Not so awesome..." ):
         entry.name = "Awesome!"
         entry.save()
    

То теперь можно этот код сократить до одной элегантной строчки:

    Entry.objects.filter( name = "Not so awesome..." ).update( name = "Awesome!" )

-Появился метод reverse, который разворачивает кверисет в обратном порядке сортировки. Иногда полезно.

  • В select_related можно добавить список relation полей которые должны быть зафетчены. Удобно, когда в модели много внешних связей и все тянут не нужно.

  • Добавлен метод values_list, который похож на старый values за исключение того что возвращает кортеж, а не словарь каждой строки результата. Давно напрашивающаяся функциональность, а то уже надоело из словарей доставать нужные значения.

  • Одним из самых главных нововведений стало появление нормального наследования моделей. Причем в двух вариантах:

    1. Абстрактные базовые классы. Покрывает на мой взгляд 95% случаев когда вообще нужно наследование моделей. Простой злободневный мой пример:

        class BannerBase( models.Model ):
           url  = models.URLField()
           hits = models.PositiveIntegerField( default = 0 )
      
           class Meta:
              abstract = True
              ordering = ( "-hits", )
      
        class TextBanner( BannerBase ):
           text = models.TextField()
      
           class Meta( BannerBase.Meta ):
              verbose_name = "текстовый баннер"
      
        class MediaBanner( BannerBase ):
           file = models.FileField( upload_to = "upload/banners/media" )
      
           class Meta( BannerBase.Meta ):
              verbose_name = "медиа баннер"
      

      Основная суть в том, что для моделей, помеченных как abstract = True, не создаются таблица, а они несут лишь некий общий функционал и описание для дочерних классов моделей. Так же имеется возможно наследовать мета-информацию. Важным замечание является то, что abstract = True не наследуется.

    2. Этот вариант более сложный, но тоже имеет случаи своего применения. Его суть заключается в том, что для каждой модели создается таблица, а дочерние связаны с родительской моделью посредством автоматически добавляемого OneToOneField поля, которое также можно задать и вручную. Мета информацию как в первом варианте наследовать не имеет особого смысла, но такие поля как ordering, если наследник не их перекроет, будут учитываться. Пример:

        class Visitor( models.Model ):
           last_action = models.CharField( max_length = 100 )
           visit_count = models.PositiveIntegerField( default = 0 )
      
        class RegistredVisitor( Visitor ):
           user = models.ForeignKey( User, related_name = "visitors" )
      
        class GuestUser( Visitor ):
           raw_visitor = models.ForeignKey( Visitor, parent_link=True )
           #явно задали связь
      
           name  = models.CharField( max_length = 150 )
           email = models.EmailField()
      

    Так же возможно и множественное наследование.

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

Как вы видите новых возможностей в ORM появилось достаточно, чтобы уже считать его вполне конкурентоспособным по сравнению с самостоятельными монстрами этой сферы. Хотя конечно есть куда идти, например продолжаем все дружно ждать ждать GROUP BY, лучшую поддержку агрегатных функций и композитные первичные ключи.

Что на очереди? newforms-admin? Хотелось быть…

comments powered by Disqus