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? Хотелось быть...

Комментарии 2

  1. Александр Кошелев написал:

    Бывает;)

    Оставлен 27 Апрель 2008 в 12:36
  2. Александр Кошелев написал:

    Спасибо Иван за замечание. Этот тонкий момент я упустил.

    Оставлен 28 Апрель 2008 в 01:57

Пингбеки 1

  1. От Новости Django 27 Апрель 2008 в 21:54

    [...]Александр Кошелев[...]