Архив за [undefined]

Наследство с особенностями

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

Начнем с особенностей:

  • Кастомные менеджеры наследуются. Что вполне логично, если учесть, что менеджер это всего-лишь обычное поле класса, которое при наследовании переходит к наследнику. Но, что не очень очевидно на первый взгляд, так то, что он остается "прикрепленным" к модели предка и его использование с моделью наследника будет давать не совсем ожидаемы результат. Вот пример:

    class Base( models.Model ):
       field1 = models.IntegerField()
    
       objects = models.Manager()
       my_custom_manager = MyManager()
    
    class Derived( Base ):
       field2 = models.IntegerField()
    
    #...
    
    Derived.my_custom_manager.filter( field2 = 777 )# Вот это не сработает
    # и ругнется на отсутсвие данного поля у Base модели, что есть правда
    

    Отсюда вывод - переопределяйте свои менеджеры в моделях наследниках. Вот соответствующее обсуждение в джанго-девелоперс

  • Теперь немного копнем внутрь. Как вам известно список fields у мета объекта модели содержит в себе поля данной модели. Так вот, у класса наследника DerivedModel._meta.fields он содержит все поля, в том числе и поля всех родителей. Вот с этой конкретными ...

У джанго воруют sql! Целыми запросами

В очередной раз разгребая от непомерного груза непрочитанных RSS свой ридер, наткнулся на примечательный пост некого Энди Маккея (кстати блог у него на джанге. Узнал случайно - получив от него как-то стандартный джанговский 404:)).

Суть в том, что он применяет джангу в качестве базы для прототипирования Zope приложений. Поскольку как вы сами прекрасно понимаете, на джанго быстрее и проще сделать что-работающее. Использует все вкусности ORM и CRUD'а для добавления тестовых данных. Потом набивает тесты с необходимым функционалом и "ворует" у ORM сгенерированные SQL запросы, портируя их в ZSQLMethods(это такая зопавская(вау!:)) обертка над SQL запросами). Да, зачем мучит себя ручным написанием мудреных запросов, когда можно поручить это машине, а самому кайфовать от высокоуровневых абстракций. Не правда ли элегантное и остороумное решение?:)

И действительно. Ведь так удобно: быстренька набросать модельки, попутно ещё и DDL получить бесплатно, потом набить всё это необходимой логикой и получит у любезного ORM SQL запросы. Которые после недавних событий стали гораздо валиднее и краше. Класс!

Только вот, положа руку на сердце, джанговский ORM пока далек от идеального и всеобъемлющего, но ему видимо хватает:)

А вы знаете ещё способы не совсем стандартного использования джанги?:)

Перечисления на службе добра

Давно уже сталкиваюсь с одним неудобством в повседневной работе с джангой.

Например у нас есть моделька:

class Entry( models.Model ):
    title = models.CharField( max_length = 150 )
    type = <...>

И каким полем выразить тип (да, знаю, что имя конфликтует со встроенным, но тут это не принципиально)? "Ха!" - скажут некоторые. Да просто взять IntegerField и сделать типы целыми числами от 0 до сколько надо. Легко!

TYPES = ( ( 0, _( "inactive" ) ),
          ( 1, _( "active" ) ) )

class Entry( models.Model ):
    title = models.CharField( max_length = 150 )
    type = models.PositiveIntegerField( choices = TYPES )

Казалось бы, проблема решена. И этим можно пользоваться. Но, тут сразу начинаются неудобства.

Для того чтобы код не превращался в склад "магических чисел" надо добавить соответствующие константы, которые описывают эти два типа.

INACTIVE = 0
ACTIVE   = 1

# или даже
INACTIVE, ACTIVE = range( 0, 2 )

потом можно пойти ещё дальше и выразить choices через них, чтобы было меньше дублирования

TYPES = ( ( INACTIVE, _( "inactive" ) ),
          ( ACTIVE, _( "active" ) ) )

Теперь можно вполне себе удобно использовать это хозяйство. И выборки делать и прочие проверки с присваиваниями

e = Entry.objects.get( name = "Спартак чемпион!", type = INACTIVE ) 
e.type = ACTIVE
e.save()

Но, это не удобно! Всё равно от повторений избавиться не удалось. Да и красоты мало. Конечно константы как и choices можно объявить в scope ...