Тег "snippets" | Интернет нового века | 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 модели, что есть правда
    

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

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

Читать далее

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

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

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

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

Читать далее

А вы задумывались как работает волшебный manage.py? Как он там внутри устроен? А, между прочим, очень интересно.

Вначале чуть-чуть истории. До августа прошлого года вся логика manage.py была в одном большом файле django/core/management.py. Там обработчики всех команд были написаны скопом, от чего файл стал просто монcтруозным и трудно поддавался созерцанию. Да и не расширяем извне.

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

Вся машинерия переехала в django/core/management/commands/. Каждая команда расположена в отдельном файле. Имя файла является именем команды и именно его нужно указывать первым параметром в manage.py.В этом файле должен быть объявлен класс Command в общем случае наследник от BaseCommand. В недрах которых скрывается логика обработки параметров командной строки ОС посредством стандартного модуля optparse.

Основным аспектом создания команды является реализация метода handle, который принимает ...

Читать далее

Часто нужно в шаблоне вывести какой-то конфигурационный параметр. Нет, не тот который в settings.py, а тот который хранится в базе. Ну например префикс заголовка страниц, содержимое мета-тега в head или ещё какую-то информацию. Эти все данные(пары имя-значение) можно либо хранить в простой модели или взять что-то стороннее посерьезней, например dbsettings.

Для примера я возьму простую модель:

class Entry( models.Model ):
   name = models.CharField( max_length = 50, unique = True )
   value = models.CharField( max_length = 150 )

Здесь и далее буду писать упрощенный код

Всё, данные есть где хранить, но их ещё и нужно удобно вывести в шаблон. Первое что приходит в голову для решения - сделать шаблонный тег. Да, и вправду просто и сердито:

@register.simple_tag
def get_conf( name ):
    return Entry.objects.get( name = name )

И использовать легко, примерно так:

<meta name="keywords" content="{% get_conf "meta_keywords" %}"/>

Но у этого способа по сути одно маленькое положительное качество - в этот тег можно передать переменную, а не жестко закодированную строку("hardcoded" - правильно перевел?:)) с названием. Но ...

Читать далее