Логика диспетчеризации запроса для получения свойства с использованием прототипа, примитивна до безобразия:
— Эй, Объект, гони сюда свойство property!
— Хм… а нету… (если есть, был обязан выдать)
— Тогда пусть вернет тот, кто тебя породил, такого урода! (объект бежит просить в словаре у своего конструктора)
Если и у конструктора такого свойства нет, то обычно, на этом все и заканчивается, ибо конструктор конструктора уже не при делах: «вассал моего вассала не мой вассал». Это все, что доступно в JS.
Диспетчеризация запроса по иерархии классов отражает совсем другое отношение: конкретное-общее (класс -> супер-класс) — и логика диспетчеризации запросов тут совершенно иная. Единственное что их роднит с «объект-конструктор», это транзитивное отношение «instanceOf», которое может означать и «объект типа», и «объект класса» (см. диаграмму www.cafepy.com/article/python_types_and_objects/images/relationships_transitivity.png). Но из-за этого «двойного» смысла и возникает путаница, будто одно можно выразить через другое. Это не так (не даром, на диаграмме стрелки выглядят по разному).
Вы пишете obj.property, явно желая получить о объекта «obj», нечто, связанное с именем «property». Но где объект возьмет это «нечто»? В конструкторе? В __dict__? В каком-то из супер-классов? Для этого, в new-классах python используются умопомрачительные правила: www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html#id402018 (следующая статья по вашей ссылке :) ). Другие типы (не-new-классы) имеют другие правила. А благодаря «магическим» методам (__getattr__, __setattr__ и т.п.) и вы можете придумать что-то свое. Но, что бы вы там не напридумывали, «снаружи» это все равно будет выглядить как «obj.property». Вот это и есть абстракция доступа.
В Javascript можно реализовать подобные правила, и описать их внутри методов. Но тогда и «снаружи» они будут выглядить как вызовы методов. Мне попадалось, например, такое: value = obj.findName('property').get(). Внутри findName() реализован механизм ресолвинга свойства по иерархии классов, а при помощи get() абстрагирован доступ к value. Т.е. логически все сделано. Но без абстракции доступа (синтаксического сахара, со стороны языка, если хотите).
Вот за что я не люблю jQuery, так это за то как, он относится к пользовательским событиям. Пример: $().bind('test', function() { window.console.log('test1') })
$().bind('test', function() { window.console.log('test2'); throw 'Error!'; })
$().bind('test', function() { window.console.log('test3') })
$().trigger('test');
...
test1
test2
Error!
'test3' нет. Т.е. если какой-то обработчик обломался и бросил исключение, то обработчики, стоящие в цепочке после него, событие не получат.
C практической точки зрения, события используются в том случае, когда необходимо синхронизировать работу ряда _независимых_ приложений. Поэтому глюки отдельного обработчика не должны влиять на диспетчеризацию события (если только он явно не вызвал event.stopImmediatePropagation()). Это же не поток.
В других фреймворках (том же prototype.js) такого глюка нет.
Приятно, конечно, что прототипы позволяют избавиться от лишнего слоя абстракций в виде классов и, тем более, метаклассов. Более того, эти сущности при желании можно моделировать средствами JS.
Извиняюсь за сумбурность вышесказанного. Ведь хотелось подвести к прямо противоположному выводу. :) Объекты и типы — вот базовая штука в ООП. А классы — это такой жутко полезный тип, — которого в javascript, как раз, серьезно не хватает. Ну и они там не реализуется по-человечески, поскольку язык не позволяет абстрагировать основную оопшную точкозапись «obj.property».
Итак, классический ООП подразумевает наличие только классов и объектов.
В ООП достаточно объектов и типов. Оффтопичный Javascript обходится лишь этим. Я не спорю. В этой области с терминологией творится полный кошмар.
Как показано на картинке по ссылке выше, пунктирчато-стрелочное отношение instanceOf транзитивно. Но, тем не менее, у каждого объекта есть один единственный, самый близкий ему тип. Тот, который его породил.
В Javascript отношение между объектом и «самым близким типом» реализуется через неявную ссылку obj.[[Prototype]], который можно выдернуть, сказав obj.constructor. В python — через obj.__class__ (который, не смотря на название, содержит-таки ссылку на этот «самый близкий тип»). Это я к тому, что термины: constructor и __class__ это синонимы. Поэтому, когда речь заходит об объекте, создающим объекты, то его стоит называть конструктором. Правда это не то же самое, что __init__(self), которая всего лишь процедура инициализации, связанная с объектом (она ни чего не создает, ей объект прилетает уже созданным, в самом первом аргументе self :) ).
В принципе, ссылки от объекта к своему конструктору (который тоже объект и у которого есть свой конструктор, котор…) уже достаточно для того, чтобы порождать магию instanceOf (то бишь typeof), и говорить о наследовании. Т.е. если представить, что у питоньего объекта есть лишь __class__ и __dict__, то станет понятно как живется несчастному жаваскрипту, без классов. :)
Но, в самом деле, как же «класс-ическое» ООП без классов? :) Class это обычный конструктор, характерной чертой которого является поддержка пусто-стрельчатого отношения IsA (т.е. наследования как показано на картинке) между объектами. Для этого в питоньих классах и появляется атрибут __bases__, где хранится кортеж супер-классов (список непосредственных родителей для данного класса, в смысле), суровое шаманство для иерархического поиска атрибутов в этой куче (в дополнение к поиску по иерархии типов), логика instanceOf начинает использовать isA, и определенные правила для манипуляции с object.__dict__. Мрак.
Для того, чтобы создавать классы «на лету» не обязательно придумывать новые конструкторы (мета-классы). Python предоставляет несколько встроенных и шикарную библиотеку (import types) на все оставшиеся 90% случаев жизни. Например: >>> ClassOfPythons = type('ThePython', (object,), {'voice': 'sh-sh-sshhhhhh....'})
>>> squasher = ClassOfPythons()
>>> squasher.voice
sh-sh-sshhhhhh....
ну или так: ClassOfPythons = object.__class__('ThePython', (object,), {'voice': 'sh-sh-sshhhhhh....'})
что абсолютно тоже самое, поскольку id(object.__class__) == id(type)
Мета-класс это узко-специальная магия для того, чтобы класс мог выбрать себе конструктора. Фактически это ситуация равносильна тому, когда хвост станет махать собакой — бессмыслица, но иногда очень нужно. :) В метакласс в django-модели используется сугубо как синтаксический сахар. Пол-статьи ни о чем. А вот пример с абстрактным классом, просто красавчик. За это даже плюсик в карму, если получится. :)
ну вот и напутал. конечно же связывать нужно не сущности с таблицами, а их типы с таблицами. т.е. Entity2Table это бред. читать: EntityType2Table: (entity_type:int, table:int)
, PK(entity_type, table), FK(entity_type -> EntityType.id, ), FK(table -> Table.id)
Table: (int:int, name:string)
, PK(id), FK(id -> Entity.id)
а extends из ООП extends это EntityType2Table.
В этом случае, для в ставки юзера потребуется лишь два запроса: в Entity и в User.
Есть достаточно наивный вариант, чтобы не хранить информацию о типе записи в каждой таблице.
Создадим таблицу Entity (сущности), в которой будем хранить информацию о всех идентификаторах и типах. Дополнительно создадим таблицу EntityType для хранения данных о самом типе (например, имени типа 'name'). Структуру табиц буду записывать в формате: имя_таблицы: (список полей), PK(поле первичного ключа), FK (внешний ключ)
Поле EntityType.id не генерирует значения идентификатора, а ссылается на Entity.id. Т.е. тип это тоже какая-то сущность.
Аналогично — пользователи это тоже сущности. User: (id:int, firstname:string)
, PK(id), FK(id -> Entity.id)
Пользователем, который запостил статью, может быть не любая абстрактная сущность, а лишь некто из таблицы User: Article: (id:int, user:int)
, PK(id), FK(id -> Entity.id), FK(user -> User.id)
В таблице Comment поле item ссылается на абстрактный Entity.id. Поэтому комментировать можно любые сущности: статьи, пользователей, типы,…, какие-то другие сущности, которые появятся в будущем. Аналогично для Vote.
В приципе, на одну и ту же сущность могут ссылаться id из разных таблиц. Для хранения этих данных создадим таблицу Table, где в поле name будем хранить имя табицы. А для связи таблицы таблиц с таблицей сущностей используем отношение многие-к-многим через таблицу Entity2Table: Entity2Table: (entity:int, class:int)
, PK(entity, class), FK(entity -> Entity.id, ), FK(class -> Table.id)
Table: (int:int, name:string)
, PK(id), FK(id -> Entity.id)
Таким образом для записи инфомации о новой сущности (допустим User) придется делать следующие вставки: в таблицу Entity, в таблицу User и в таблицу Entity2Table для связи вновь созданного юзера и таблицы User.
Такая структура отображается на соответствующие понятия ООП: Entity => Object, Table => Class, EntityType => DataType, Entity2Table => extends (classes).
— Огры как лук
— Воняют?
— Да нет.
— От них плачут
— Нет.
— Если их оставить на солнце то они становятся коричневыми
— Нет! У огров есть слои. И у лука есть слои. У тех и у других есть слои.
У джанго-приложений тоже есть слои, которые выражаются структурой каталога. Темпрейт-теги (templatetags/*) это расширения для языка шаблонов. В слое тегов, кодим только то, что нужно для взаимодействия с шаблонизатором. Формированием html занимаются шаблоны (templates/*) или виджеты (см. django/forms/widgets.py). А формированием контекста данных для виждетов — модели представления (admins.py, forms.py, и т.п.).
Из шаблона к блоку обращаемся через тег, из views — через функцию, а из context — по имени value в структуре данных. Т.е. блок у нас, это value внутри Context.
Декомпозиция на функции это все-таки процедурное программирование. Не в смысле плохо, просто — по-другому.
Если рассматривать данный код в разрезе MVC, то функции из view.py, обрабатывающие request, и возвращающие HttpPresponse это экстеншены для Controller. А View размазано между логикой, формирующей dictionary в таком вот обработчике запроса, и рендером шаблона внутри render_to_response('template.html', dictionary).
В качестве примера View в Django можно рассматривать django.forms.*.
Но, по большому счету, Django — не MVC фреймворк.
Посмотри Zope. Там используется подобный подход. В документации описаны случаи когда выгоднее хранить исходники в файлах, а когда в БД.
Невозможность полноценно использовать IDE
Напиши webdav интерфейс, отдающий исходники из БД, подключай этот ресурс как сетевой диск и используй любую IDE, работающую с файлами. Это сильно проще, чем писать текстовый редактор.
Use of ORDER BY for individual SELECT statements implies nothing about the order in which the rows appear in the final result because UNION by default produces an unordered set of rows
а еще лучше, вместе со своим другом, почитайте книжки по реляционной алгебре.
Чтобы получить доступ к информации на сайте нужно *купить* этот журнал.
Данная технология может продлить жизнь многим печатным изданиям, коих сейчас зажимает Интернет. А для Интернет — позволит использовать отлаженные годами способы монетизации печатных изданий.
Валидация входных данных это отдельный слой, который не относится к слою бизнес-логики. Бизнес-логика это не «логика, как обрабатывать данные», а лишь отдельная ее часть (layer) — абстрактная модель предметной области (domain logic).
<a id='target'>click me</a> $('#target').get(0).addEventListener('click', function(e) { window.console.log('test1') }, false) $('#target').get(0).addEventListener('click', function(e) { window.console.log('test2'); throw "Error1"; }, false) $('#target').get(0).addEventListener('click', function(e) { window.console.log('test3') }, false) ... test1 test2 test3 Error!— Эй, Объект, гони сюда свойство property!
— Хм… а нету… (если есть, был обязан выдать)
— Тогда пусть вернет тот, кто тебя породил, такого урода! (объект бежит просить в словаре у своего конструктора)
Если и у конструктора такого свойства нет, то обычно, на этом все и заканчивается, ибо конструктор конструктора уже не при делах: «вассал моего вассала не мой вассал». Это все, что доступно в JS.
Диспетчеризация запроса по иерархии классов отражает совсем другое отношение: конкретное-общее (класс -> супер-класс) — и логика диспетчеризации запросов тут совершенно иная. Единственное что их роднит с «объект-конструктор», это транзитивное отношение «instanceOf», которое может означать и «объект типа», и «объект класса» (см. диаграмму www.cafepy.com/article/python_types_and_objects/images/relationships_transitivity.png). Но из-за этого «двойного» смысла и возникает путаница, будто одно можно выразить через другое. Это не так (не даром, на диаграмме стрелки выглядят по разному).
В Javascript можно реализовать подобные правила, и описать их внутри методов. Но тогда и «снаружи» они будут выглядить как вызовы методов. Мне попадалось, например, такое: value = obj.findName('property').get(). Внутри findName() реализован механизм ресолвинга свойства по иерархии классов, а при помощи get() абстрагирован доступ к value. Т.е. логически все сделано. Но без абстракции доступа (синтаксического сахара, со стороны языка, если хотите).
$().bind('test', function() { window.console.log('test1') })
$().bind('test', function() { window.console.log('test2'); throw 'Error!'; })
$().bind('test', function() { window.console.log('test3') })
$().trigger('test');
...
test1
test2
Error!
'test3' нет. Т.е. если какой-то обработчик обломался и бросил исключение, то обработчики, стоящие в цепочке после него, событие не получат.
C практической точки зрения, события используются в том случае, когда необходимо синхронизировать работу ряда _независимых_ приложений. Поэтому глюки отдельного обработчика не должны влиять на диспетчеризацию события (если только он явно не вызвал event.stopImmediatePropagation()). Это же не поток.
В других фреймворках (том же prototype.js) такого глюка нет.
<a class="link" href="#target">click me</a> <div id="target"></div> ... $('.link').click(function() { $(this).attr('href').trigger('showFloat'); return false; }}Извиняюсь за сумбурность вышесказанного. Ведь хотелось подвести к прямо противоположному выводу. :) Объекты и типы — вот базовая штука в ООП. А классы — это такой жутко полезный тип, — которого в javascript, как раз, серьезно не хватает. Ну и они там не реализуется по-человечески, поскольку язык не позволяет абстрагировать основную оопшную точкозапись «obj.property».
В ООП достаточно объектов и типов. Оффтопичный Javascript обходится лишь этим. Я не спорю. В этой области с терминологией творится полный кошмар.
Как показано на картинке по ссылке выше, пунктирчато-стрелочное отношение instanceOf транзитивно. Но, тем не менее, у каждого объекта есть один единственный, самый близкий ему тип. Тот, который его породил.
В Javascript отношение между объектом и «самым близким типом» реализуется через неявную ссылку obj.[[Prototype]], который можно выдернуть, сказав obj.constructor. В python — через obj.__class__ (который, не смотря на название, содержит-таки ссылку на этот «самый близкий тип»). Это я к тому, что термины: constructor и __class__ это синонимы. Поэтому, когда речь заходит об объекте, создающим объекты, то его стоит называть конструктором. Правда это не то же самое, что __init__(self), которая всего лишь процедура инициализации, связанная с объектом (она ни чего не создает, ей объект прилетает уже созданным, в самом первом аргументе self :) ).
В принципе, ссылки от объекта к своему конструктору (который тоже объект и у которого есть свой конструктор, котор…) уже достаточно для того, чтобы порождать магию instanceOf (то бишь typeof), и говорить о наследовании. Т.е. если представить, что у питоньего объекта есть лишь __class__ и __dict__, то станет понятно как живется несчастному жаваскрипту, без классов. :)
Но, в самом деле, как же «класс-ическое» ООП без классов? :) Class это обычный конструктор, характерной чертой которого является поддержка пусто-стрельчатого отношения IsA (т.е. наследования как показано на картинке) между объектами. Для этого в питоньих классах и появляется атрибут __bases__, где хранится кортеж супер-классов (список непосредственных родителей для данного класса, в смысле), суровое шаманство для иерархического поиска атрибутов в этой куче (в дополнение к поиску по иерархии типов), логика instanceOf начинает использовать isA, и определенные правила для манипуляции с object.__dict__. Мрак.
Для того, чтобы создавать классы «на лету» не обязательно придумывать новые конструкторы (мета-классы). Python предоставляет несколько встроенных и шикарную библиотеку (import types) на все оставшиеся 90% случаев жизни. Например:
>>> ClassOfPythons = type('ThePython', (object,), {'voice': 'sh-sh-sshhhhhh....'})
>>> squasher = ClassOfPythons()
>>> squasher.voice
sh-sh-sshhhhhh....
ну или так:
ClassOfPythons = object.__class__('ThePython', (object,), {'voice': 'sh-sh-sshhhhhh....'})
что абсолютно тоже самое, поскольку id(object.__class__) == id(type)
Мета-класс это узко-специальная магия для того, чтобы класс мог выбрать себе конструктора. Фактически это ситуация равносильна тому, когда хвост станет махать собакой — бессмыслица, но иногда очень нужно. :) В метакласс в django-модели используется сугубо как синтаксический сахар. Пол-статьи ни о чем. А вот пример с абстрактным классом, просто красавчик. За это даже плюсик в карму, если получится. :)
EntityType2Table: (entity_type:int, table:int)
, PK(entity_type, table), FK(entity_type -> EntityType.id, ), FK(table -> Table.id)
Table: (int:int, name:string)
, PK(id), FK(id -> Entity.id)
а extends из ООП extends это EntityType2Table.
В этом случае, для в ставки юзера потребуется лишь два запроса: в Entity и в User.
Создадим таблицу Entity (сущности), в которой будем хранить информацию о всех идентификаторах и типах. Дополнительно создадим таблицу EntityType для хранения данных о самом типе (например, имени типа 'name'). Структуру табиц буду записывать в формате: имя_таблицы: (список полей), PK(поле первичного ключа), FK (внешний ключ)
Entity: (id:autoincrement, type:int) , PK(id), FK(type -> EntityType.id)
EntityType: (id:int, name:string), PK(id), FK(id -> Entity.id)
Поле EntityType.id не генерирует значения идентификатора, а ссылается на Entity.id. Т.е. тип это тоже какая-то сущность.
Аналогично — пользователи это тоже сущности.
User: (id:int, firstname:string)
, PK(id), FK(id -> Entity.id)
Пользователем, который запостил статью, может быть не любая абстрактная сущность, а лишь некто из таблицы User:
Article: (id:int, user:int)
, PK(id), FK(id -> Entity.id), FK(user -> User.id)
Продолжим:
Comment: (id:int, user:int, item:int)
, PK(id), FK(id -> Entity.id), FK(user -> User.id), FK(item -> Entity.id)
Vote: (id:int, user:int, item:int)
, PK(id), FK(id -> Entity.id), FK(user -> User.id), FK(item -> Entity.id)
В таблице Comment поле item ссылается на абстрактный Entity.id. Поэтому комментировать можно любые сущности: статьи, пользователей, типы,…, какие-то другие сущности, которые появятся в будущем. Аналогично для Vote.
В приципе, на одну и ту же сущность могут ссылаться id из разных таблиц. Для хранения этих данных создадим таблицу Table, где в поле name будем хранить имя табицы. А для связи таблицы таблиц с таблицей сущностей используем отношение многие-к-многим через таблицу Entity2Table:
Entity2Table: (entity:int, class:int)
, PK(entity, class), FK(entity -> Entity.id, ), FK(class -> Table.id)
Table: (int:int, name:string)
, PK(id), FK(id -> Entity.id)
Таким образом для записи инфомации о новой сущности (допустим User) придется делать следующие вставки: в таблицу Entity, в таблицу User и в таблицу Entity2Table для связи вновь созданного юзера и таблицы User.
Такая структура отображается на соответствующие понятия ООП: Entity => Object, Table => Class, EntityType => DataType, Entity2Table => extends (classes).
зы: сори за сумбурность. это скорее мемори дамп.
У джанго-приложений тоже есть слои, которые выражаются структурой каталога. Темпрейт-теги (templatetags/*) это расширения для языка шаблонов. В слое тегов, кодим только то, что нужно для взаимодействия с шаблонизатором. Формированием html занимаются шаблоны (templates/*) или виджеты (см. django/forms/widgets.py). А формированием контекста данных для виждетов — модели представления (admins.py, forms.py, и т.п.).
Из шаблона к блоку обращаемся через тег, из views — через функцию, а из context — по имени value в структуре данных. Т.е. блок у нас, это value внутри Context.
Если рассматривать данный код в разрезе MVC, то функции из view.py, обрабатывающие
request, и возвращающиеHttpPresponseэто экстеншены для Controller. А View размазано между логикой, формирующейdictionaryв таком вот обработчике запроса, и рендером шаблона внутриrender_to_response('template.html', dictionary).В качестве примера View в Django можно рассматривать django.forms.*.
Но, по большому счету, Django — не MVC фреймворк.
Напиши webdav интерфейс, отдающий исходники из БД, подключай этот ресурс как сетевой диск и используй любую IDE, работающую с файлами. Это сильно проще, чем писать текстовый редактор.
а еще лучше, вместе со своим другом, почитайте книжки по реляционной алгебре.
Данная технология может продлить жизнь многим печатным изданиям, коих сейчас зажимает Интернет. А для Интернет — позволит использовать отлаженные годами способы монетизации печатных изданий.