вторник, 28 июня 2011 г.

Writing Django widgets, или "Никаких учебников, только исходники"


Единственный учебник по программированию который я пока-что прочитал от корки до корки  это "Язык программирования Python" хороший учебник. Многое мне дал. Остальные я использовал как справочники, и читал парочку первых страниц с описанием синтаксиса и основных особенностей.Да-да, это плохое качество, но когда времени нет на 1000 страниц теории, а надо сделать что-то ручками, и работающее, то Документация + http://stackoverflow.com/+ Блоги, и в атаку.
Так было и с Django.
И так, Writing Django widgets.
Информации по этой теме мало, документация хреновая.Поделюсь 2-х дневным опытом.
Widget в переводе с забугорного знать как изобразить.
Что мы будем писать? Виджет календарика.
Я погорячился со словом писать. Ну зачем писать то, что уже давно написано, причём сотни раз,умными трудолюбивыми людьми?
Пусть на другом языке, но написано.
Мы просто прикрутим Скриптик к нашей формочке.
Шаг 1
И так у нас есть простенькая формочка

class ContactForm(forms.Form):
    subject = forms.CharField()
    email = forms.EmailField()
    Date = forms.DateField()
Дабы раскрыть архитектуру действия Вижетов, от календарика мы пока удалимся.

class My_widjet(forms.widgets.TextInput):
    def render(self, name, value, attrs=None):
        return """Простите Блог спот и highlight пытаются интерпритировать HTML Теги""" 
Простите, Блог спот и highlight пытаются интерпритировать HTML Теги.
Поэтому буду давать ссылки: Преведущий код. Да покажите мне такой пример 2 дня назад, я бы не заморачивался столько времени. И нервов бы сохранил. Это, только что, был наш с вами первый виджет. Теперь применим его (когда это возможно я всё-же код буду Пастить сюда)

 class ContactForm(forms.Form):
    subject = forms.CharField()
    email = forms.EmailField()
    Date = forms.DateField(widget = My_widjet)

Ну что, загружайте страничку с вашей формочкой, и "О чудо!" Наш виджет сработал Теперь это вовсе не поле для ввода даты, это ссылка на гугл.
Что от сюда надо вынести?
  •  Виджет целиком и полностью заменяет то к чему его применили. Заменяет он это методом render(он вызывается автоматически), точнее не самим методом а тем что он возвращает. 
Шаг 2
Но согласитесь, целый календарик в строку пихать это совсем по быдлокодерски. Поэтому любимый Django позаботился и дал нам класс Media в Виджете, с помощью него можно по человечески работать с таблицами стилей и яваскриптами . Слава богу, включение скриптов и стилей в Media хорошо продокументировано на официальном сайте. Но есть 1 костыль. Если вы прочти документацию, то увидели, что в последствии в виджете self.media(не люблю когда Питонеры попросту играются с регистром, вместо инициализации интуитивно понятных имён)
будут строки подключения файлов скриптов. Но работать эти строки будут уже в браузере. Я уже был в отчайнии, пока не догадался, что нужно сделать чтоб скрипты подключились. И так в медиа у нас лежит что-то типа 

class Media:
  js = ( 
     "/src/js/unicode-letter.js",
  )
а в render мы прописали что то типа

return "%s some code"%self.media
Таким фигом в браузере у нас
<script type="text/javascript" src="/src/js/unicode-letter.js"></script>
А что по Адресу скриптика? А 404 по адресу скриптика.
И отрубите мне копыта если это есть в официальной документации.
И почему там 404, и что делать? Всё просто. Ваш сайт это не html страница, это Джанго проект, и когда вы запрашиваете его о какой-то  ссылке,  он ищет её в файлике urls.py, и вызывает асоциирующуюся с ней функцию, которая в свою очередь возвращает, запросившему её, данные. Вот такой вот каламбур. Теперь когда нам известна суть проблемы мы может её решить.
Надо в наш фаил привязок добывить такую строку

('^src/([\x20-\x7E]*)/?$',views._catalog),

Для тех кому не понятно советую почитать про регулярные выражения, это очень полезно. Я в кратце поясню при вызове чего- начинающегося на src/... вызывается функция views._catalog с 2 аргументами, 1 - request. А второй это всё что после src/ в виде строки.

Далее создадим функцию _catalog в views


def _catalog(request,path):
 path= ""
 try:
  f = open( os.path.join(os.path.dirname(__file__), 'src/').replace('\\','/')+path,'r')
  path = f.read()
 finally:
  f.close()
 return HttpResponse(path)
Да правильно, она считывает фаил по указаному пути( мы ведь уже не в браузере, и файловая система нам доступна)
И ява скрипт будет работать.
 Что от сюда надо вынести?
  • Привязку пути для ваших скриптов к функции которая их возврашает
Шаг 3  Собственно календарик. Я использовал вот этот чудный календарик JSCal2. Для того чтобы он всплывал и по кнопочке и заполнял поле датой и годом, и временем достаточно сделать так.
Тоесть надо подключить css и Js файлы, и выдать пару строк в ответе. Там около 15 файлов css.Можно было разоборатся, что надо, что не надо, и по  подключать это как должно быть, но я устал, поэтому сделал так. Вот Класс Media для виджета календаря.  Как видете, в нём я создал свою переменную my_css, благодоря этому мне не пришлось прописывать в словарик каждый css файлик.
Ну а теперь целиком весь виджет .
Меня не мало бесит, что приходится подставлять в строку (name,name,name,name) но я не придумал пока как это сделать по дугому. Думал сделать функцию типо этой

def N_times(times,what):
    I=0;
    T=tuple()
    while I<=times:
        I=I+1
        T=T+(what,)
    return T
Но это тоже не очень красиво. Придумаю отпишусь. Ну вот. Календарик есть. Ура. Мы разобрались как работают виджеты и сделали кадендарик который можно подключить в 1 строку к любому field-у.

2 комментария:

  1. Фак мой мозг!!! за что такие изврашения. Поставленная задача реализуется горазро проще.

    После подключения скрпита в медиа
    class Media:
    js = (
    "/src/js/unicode-letter.js",
    )

    Нужно в настройках хоста в конфиге apache или nginx задать алиас для нужной папки и скрпит будет доступен из без всяких 404

    К тому же скрипты и стили важих приложений принято хранить в папке static. Если они хранятся там то при выполнении manage.py collectstatic они автоматом скопируются в папку static проекта.

    ОтветитьУдалить
  2. manage.py collectstatic далеко не во всех версиях Django

    ОтветитьУдалить