Тег "python" | Интернет нового века | webnewage.org

Интернет нового века

Я всё продолжаю по рабочим и не только нуждам ковырять наследование моделей, поэтому как и обещал - то ли ещё есть!

  • Очередной сюрприз ждал меня, когда я обновляя свой блог-движок под текущий транк. Я обнаружил сломанным блок "Архив" из-за того, что метод dates() перестал возвращать уникальные значения на месяца, а выдавал мне всё подряд. Я сильно удивился. Но пофиксил всё при помощи, в последнее время моего любимого, set. Да, костыль, но ковырять дальше пока желания нет.

  • Далее уже по рабочим нуждам обновления тест наборы для приложения, проявились некоторые особенности работы fixtures. Для примера, допустим у нас есть такие связанные наследованием модели:

    class Base( models.Model ):
       field1 = models.IntegerField( default = 10 )
       # + неявное поле id
    
    class Derived( Base ):
       field2 = models.IntegerField( default = 1 )
       # + неявное поле base_ptr
    
    1. Сериализатор дампит модель наследника целиком, т.е. со всеми полями в том числе и родителей.

    2. При загрузке модели родителей не могут подцепить свой первичный ключ, а просто делают новый INSERT, получая тем самым новый id. Потом этот ...

Читать далее

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

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

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

    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 модели, что есть правда
    

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

  • Теперь немного ...

Читать далее

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

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

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

Только ...

Читать далее

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

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

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" ) ) )

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

Читать далее

Наконец-то! Уверен, что я не один воскликнул это слово, когда узнал что долгожданная 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 )
    

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

  • Есть ещё некоторое количество изменений, но они на низко уровне и на прямую ...

Читать далее