Рад сообщить, что блог переехал на новый домен.
Новый адрес: http://cyxapeff.org
этот блог больше обновляться не будет.
четверг, 2 августа 2007 г.
воскресенье, 15 июля 2007 г.
суббота, 14 июля 2007 г.
воскресенье, 17 июня 2007 г.
Пишем Tv программу на Django. Часть 3
как обычно код этого поста тут: http://tvwatcher.googlecode.com/svn/tags/post3
Наконец пришло время создать view. Вид. То, что будет видеть обычный пользователь когда зайдёт к нам на страничку.
В самом начале я задумывался о xml+xslt, но немножко подумав как это реализовывать пришёл к выводу, что в таком случае я потеряю очень мощный инструмент django. Его шаблоны. Поэтому будем делать на обыкновенном html4.
Первым делом я открываю Gimp и рисую макет будущей страницы... (чёрт, похоже я его уже потерял, вобщем поверьте на слово я его нарисовал)
Затем режу эту картинку и верстаю тестовую страничку. Привожу к виду как на картинке.
В результате оно стало выглядеть примерно так:
Да, не очень красиво. Особенно цвета. Ну да ладно...
Теперь нужно выделить основной код который будет повторяться на всех страницах. Это будет наш base.html, который мы закинем в ownproject/templates/tv/.
Если вы уже ознакомились с официальным туториалом, то знаете, что в шаблонах у джанго свой мини язык. Вот пользуясь им и создаём наш base.html
У меня получилась вот такая штука:
Как видно в теге title написано {% block title %}Tv программа{% endblock %}. Это значит, что по умолчанию название страницы будет Tv программа, но мы можем его изменять в любом наследующем шаблоне, переопределив block title. То же самое происходит с upbar, week и content.
Так же на каждой странице будет отображаться верхний блок с днями недели и тп. Чтобы не таскать одно и то же из функции в функцию в view, я вынес их в отдельный шаблонный тег.
Для этого создаём папочку ownproject/tv/templatetags. В ней пустой файл __init__.py и menu_ex.py который собственно и является нашим тегом. Ничего особо интересного там нет. Так что кому интересно смотрим в svn. (вообще туда заглядывать просто обязательно! в самих постах очень многое пропущенно) Единственно что стоит отметить, это то как сказать django что это именно тег. Делается это примерно так:
Таблица стилей и яваскрипт не случайно положены в папку media. Дело в том, что встроенный веб сервер django не умеет просто так отдавать статику. Поэтому все картинки, js, css и тп обычно скидывается в какую-нибудь папку, например ownproject/media. Затем открывается ownproject/urls.py и дописывается такая строчка:
В ownproject/settings.py определяем переменную MEDIA_ROOT. В моём случае:
И если директория была названа media, то необходимо изменить ADMIN_MEDIA_PREFIX, например на amedia. (всё это в том же файле settings)
Ну вот. Теперь можно попробовать начать создавать нашу главную страницу.
Создадим файл /ownproject/tv/urls.py и пропишем там:
Отлично. Теперь при попытке зайти на http://127.0.0.1:8000/tv будет вызываться функция index из ownproject/tv/views.py. Правда её ещё нет. :)
Займёмся её написанием... Хотя нет. Стоп! Что такое главная страница? Это телепрограмма на сегодня. То есть это то же самое, что и по ссылкам Пн, Вт, Ср, Чт и тд. Только дата устанавливается автоматически. Логично было бы написать функцию которая будет работать с любой датой, а в index просто вызывать её с сегодняшней датой. Этим и займёмся.
Нарисуем шаблон list.html, который будет выводить каналы.
Ну и посмотрим на ownproject/templates/tv/list.html. Там тоже ничего сложного:
Говорим, что мы потомок base.html и переопределяем block content.
Скрипт в конце необходим чтобы правельно выводить блоки с каналами.
Здесь вообще довольно интересная история произошла. Изначально я нарисовал 2 колонки с каналами и каналы в своих колонках расположены один под другим. Вообщем получается как в той же бумажной телепрограмки. И вроде всё было хорошо, пока я не растянул свой браузер на всю ширину монитора (1280px). появилось просто ужасающее колличество свободного места. Тогда я все каналы сделал float:left, и стал выводить... Но конечно же получилось очень страшно, т.к у них у всех различная высота. Думал, думал, думал чего делать. Но так ничего лучше яваскрипта не придумал. Он смотрит на ширину окна браузера и в зависимости от него каждому Nому каналу ставит свойство clear: left. В результате получилось не совсем так как я хотел, но вроде более или менее нормально.
И так вернёмся к нашим баранам. Выделив функцию day, мы автоматически создали обработчик других дней. Поэтому дописываем в urls
Теперь можно перейти на сайт и потыкать по ссылкам. Оно должно работать. :)
Создание страничек с каждым каналом в отдельности и описанием тоже не представляют ничего интересного, поэтому на них можно посмотреть в svn. С добавлением\удалением канала у юзера то же самое. Поэтому и смотрим там же.
Теперь нужно к этому всему подключить пользователей. Как видно я уже учёл различное поведение для анонимного пользователя и авторизированного в виде и шаблонах. Поэтому осталось только сделать механизм входа\выхода\регистрации. Так как почти всё это в будующем будет заменено ajax'ом, то по возможности используем встроенные в django функции или элементарные формочки.
Для входа и выхода уже написаны полностью рабочие функции, осталось лишь прописать их в urls.
А вот регистрации готовой нет, поэтому набрасаем простенькую формочку в шаблоне.
Django освобождает нас от необходимости прописывать бесмысленные функции в view, которые занимаются только тем, что рендерят шаблон. Вместо этого используем django.views.generic.simple.direct_to_template.
В urls пропишем:
Форма после заполнения будет вызывать функцию createuser: (r'^createuser/$', views.createuser),
Она опять же очень эллементарна. Почти всё за нас делает django. Импортируем User из django.contrib.auth.models и authenticate, login из django.contrib.auth.
Ну вот наверное и всё. В результате мы получили почти рабочий сайт с телепрограммой.
Наконец пришло время создать view. Вид. То, что будет видеть обычный пользователь когда зайдёт к нам на страничку.
В самом начале я задумывался о xml+xslt, но немножко подумав как это реализовывать пришёл к выводу, что в таком случае я потеряю очень мощный инструмент django. Его шаблоны. Поэтому будем делать на обыкновенном html4.
Первым делом я открываю Gimp и рисую макет будущей страницы... (чёрт, похоже я его уже потерял, вобщем поверьте на слово я его нарисовал)
Затем режу эту картинку и верстаю тестовую страничку. Привожу к виду как на картинке.
В результате оно стало выглядеть примерно так:
Да, не очень красиво. Особенно цвета. Ну да ладно...
Теперь нужно выделить основной код который будет повторяться на всех страницах. Это будет наш base.html, который мы закинем в ownproject/templates/tv/.
Если вы уже ознакомились с официальным туториалом, то знаете, что в шаблонах у джанго свой мини язык. Вот пользуясь им и создаём наш base.html
У меня получилась вот такая штука:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>{% block title %}Tv программа{% endblock %}</title>
<script type="text/javascript" src="/media/tv/css.js"></script>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link href='/media/tv.css' type='text/css' rel='stylesheet'>
</head>
<body>
<div id="upperbar">
{% block upbar %}
<div id="menu">{% if user %}
<a href="">Настройки</a> | <a href="/tv/logout/">Выход</a>
{% else %}
<a href="/tv/login/?next=/tv">Войти</a> |
<a href="/tv/registration">Регистрация</a>
{% endif %}</div>
<div id="hello">Здравствуйте, {% if user %}
{{ user.username }}!
{% else %}
Гость!
{% endif %} | {% now "d F Y H:i" %}</div>
{% endblock %}
</div>
<div id="upmain">
<h1 id="logo">Tv программа</h1>
</div>
{% block week %}
{% load menu_ex %}
{% menu display date chanal %}
{% endblock %}
{% block content %}{% endblock %}
</div>
</body>
</html>>
Как видно в теге title написано {% block title %}Tv программа{% endblock %}. Это значит, что по умолчанию название страницы будет Tv программа, но мы можем его изменять в любом наследующем шаблоне, переопределив block title. То же самое происходит с upbar, week и content.
Так же на каждой странице будет отображаться верхний блок с днями недели и тп. Чтобы не таскать одно и то же из функции в функцию в view, я вынес их в отдельный шаблонный тег.
Для этого создаём папочку ownproject/tv/templatetags. В ней пустой файл __init__.py и menu_ex.py который собственно и является нашим тегом. Ничего особо интересного там нет. Так что кому интересно смотрим в svn. (вообще туда заглядывать просто обязательно! в самих постах очень многое пропущенно) Единственно что стоит отметить, это то как сказать django что это именно тег. Делается это примерно так:
from django import template
register = template.Library()
def menu(display=0, date=None, chanal=None):
...
register.inclusion_tag('tv/menu.html')(menu)
Таблица стилей и яваскрипт не случайно положены в папку media. Дело в том, что встроенный веб сервер django не умеет просто так отдавать статику. Поэтому все картинки, js, css и тп обычно скидывается в какую-нибудь папку, например ownproject/media. Затем открывается ownproject/urls.py и дописывается такая строчка:
(r'^media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
В ownproject/settings.py определяем переменную MEDIA_ROOT. В моём случае:
MEDIA_ROOT = '/mnt/H/My Developement/django/mysite/media/'
И если директория была названа media, то необходимо изменить ADMIN_MEDIA_PREFIX, например на amedia. (всё это в том же файле settings)
Ну вот. Теперь можно попробовать начать создавать нашу главную страницу.
Создадим файл /ownproject/tv/urls.py и пропишем там:
from django.conf.urls.defaults import *
from django.conf import settings
from tv import views
urlpatterns = patterns('',
(r'^$', views.index),
}
Отлично. Теперь при попытке зайти на http://127.0.0.1:8000/tv будет вызываться функция index из ownproject/tv/views.py. Правда её ещё нет. :)
Займёмся её написанием... Хотя нет. Стоп! Что такое главная страница? Это телепрограмма на сегодня. То есть это то же самое, что и по ссылкам Пн, Вт, Ср, Чт и тд. Только дата устанавливается автоматически. Логично было бы написать функцию которая будет работать с любой датой, а в index просто вызывать её с сегодняшней датой. Этим и займёмся.
def index(request):
return day(request, time.strftime("%Y-%m-%d"))
Нарисуем шаблон list.html, который будет выводить каналы.
def day(request, date):
if request.user.is_authenticated(): # Проверяем авторизирован ли юзер
user=request.user
setting = user.tv_Setting
display = tv_Setting.Mode # режим отображения берём из настроек
chanals = []
for place in setting.Chanals.order_by('N'): # Извлекаем из базы "места" сортираю по его номеру
chanals.append(place.Chanal) # и добавляем в массив связанные с ними каналы
else: # иначе юзер у нас None, а каналы просто первые четыре штуки
user = None
chanals = Chanal.objects.all()[:4]
display = request.GET.has_key("allday") # режим отображание смотрим по тому есть ли в адресе переменная allday
for chanal in chanals: # для каждого канала необходимо вызвать функцию get_chanal, чтобы добавить в них информацию из других таблиц
chanal = get_chanal(display, date, chanal)
c = { # именнованный массив который мы отдаём в шаблон
"chanals": chanals,
"date": date,
"display": display,
"user": user,
"chanal": None,
}
return render_to_response('tv/list.html', c)
Ну и посмотрим на ownproject/templates/tv/list.html. Там тоже ничего сложного:
{% extends "tv/base.html" %}
{% block content %}
{% if user %}
<div id="addchanal"><a href="/tv/addchanals">Добавить канал</a></div>
{% endif %}
{% if chanals %}
{% for chanal in chanals %}
<div class="chanal">
{% if user %}
<div class="actions"><a href="/tv/delete/{{ chanal.id }}"><img src="/media/tv/icon_deletelink.gif" alt="удалить"></a></div>
{% endif %}
{% if chanal.Icon %}
<img src="{{ chanal.Icon }}">
{% endif %}
<h2><a href="/tv/{{ chanal.Slug }}">{{ chanal.Title }}</a></h2>
<ul>
{% if chanal.programs %}
{% for program in chanal.programs %}
<li>{{ program.Time }}
{% if program.description %}
<a href="/tv/description/{{ program.description.id }}">{{ program.Title }}</a>
{% else %}
{{ program.Title }}
{% endif %}
</li>
{% endfor %}
{% else %}
Нет телепрограмм
{% endif %}
</ul>
</div>
{% endfor %}
{% else %}
Нет каналов.
{% endif %}
<script>
checkBrowserWidth();
</script>
{% endblock %}
Говорим, что мы потомок base.html и переопределяем block content.
Скрипт в конце необходим чтобы правельно выводить блоки с каналами.
Здесь вообще довольно интересная история произошла. Изначально я нарисовал 2 колонки с каналами и каналы в своих колонках расположены один под другим. Вообщем получается как в той же бумажной телепрограмки. И вроде всё было хорошо, пока я не растянул свой браузер на всю ширину монитора (1280px). появилось просто ужасающее колличество свободного места. Тогда я все каналы сделал float:left, и стал выводить... Но конечно же получилось очень страшно, т.к у них у всех различная высота. Думал, думал, думал чего делать. Но так ничего лучше яваскрипта не придумал. Он смотрит на ширину окна браузера и в зависимости от него каждому Nому каналу ставит свойство clear: left. В результате получилось не совсем так как я хотел, но вроде более или менее нормально.
И так вернёмся к нашим баранам. Выделив функцию day, мы автоматически создали обработчик других дней. Поэтому дописываем в urls
(r'^(?P\d{4}-\d{2}-\d{2})/$', views.day),
Теперь можно перейти на сайт и потыкать по ссылкам. Оно должно работать. :)
Создание страничек с каждым каналом в отдельности и описанием тоже не представляют ничего интересного, поэтому на них можно посмотреть в svn. С добавлением\удалением канала у юзера то же самое. Поэтому и смотрим там же.
Теперь нужно к этому всему подключить пользователей. Как видно я уже учёл различное поведение для анонимного пользователя и авторизированного в виде и шаблонах. Поэтому осталось только сделать механизм входа\выхода\регистрации. Так как почти всё это в будующем будет заменено ajax'ом, то по возможности используем встроенные в django функции или элементарные формочки.
Для входа и выхода уже написаны полностью рабочие функции, осталось лишь прописать их в urls.
(r'^login/$', 'django.contrib.auth.views.login'),
(r'^logout/$', 'django.contrib.auth.views.logout'),
А вот регистрации готовой нет, поэтому набрасаем простенькую формочку в шаблоне.
Django освобождает нас от необходимости прописывать бесмысленные функции в view, которые занимаются только тем, что рендерят шаблон. Вместо этого используем django.views.generic.simple.direct_to_template.
В urls пропишем:
(r'^registration/$', 'django.views.generic.simple.direct_to_template', {'template': 'tv/registration.html'}),
Форма после заполнения будет вызывать функцию createuser: (r'^createuser/$', views.createuser),
Она опять же очень эллементарна. Почти всё за нас делает django. Импортируем User из django.contrib.auth.models и authenticate, login из django.contrib.auth.
user = User.objects.create_user(request.POST['username'], '', request.POST['password']) # создаём пользователя
user.save() # сохраняем его
user = authenticate(username=request.POST['username'], password=request.POST['password'])
login(request, user) # сразу же авторизируем
return HttpResponseRedirect('/tv/') # и кидаем на главную
Ну вот наверное и всё. В результате мы получили почти рабочий сайт с телепрограммой.
суббота, 26 мая 2007 г.
Прощай школа
Ну вот, вчера отзвенел последний звонок. Всё пиво, водка и другие напитки выпиты. Недельный запас еды прикончен. В квартире писец. Но школа закончена, жалко конечно, но жалеть нет времени. Нужно поступать в институт. ><
воскресенье, 20 мая 2007 г.
SMF. Злость!
Спросите что такое SMF? Это Садо-Мазо Форум!
Руки бы поотрывал его разработчикам. Ну как можно ТАК сделать систему шаблонов?! Код и хтмл вперемежку это просто какой-то кошмар. Но приходится продолжать мучить себя им, т.к нет ни одного вменяемого бесплатного форума с нормальной интеграцией в drupal... Хотя эту реализацию тоже довольно сложно назвать нормальной...
Вот после знакомства с такими приложениями я понимаю, что просто обожаю django. Джанго как и весь питон заставляет писать правильно (чего стоят отступы питона или в джанго хочешь не хочешь, а приходится разделять модель, вид и шаблон).
Руки бы поотрывал его разработчикам. Ну как можно ТАК сделать систему шаблонов?! Код и хтмл вперемежку это просто какой-то кошмар. Но приходится продолжать мучить себя им, т.к нет ни одного вменяемого бесплатного форума с нормальной интеграцией в drupal... Хотя эту реализацию тоже довольно сложно назвать нормальной...
Вот после знакомства с такими приложениями я понимаю, что просто обожаю django. Джанго как и весь питон заставляет писать правильно (чего стоят отступы питона или в джанго хочешь не хочешь, а приходится разделять модель, вид и шаблон).
суббота, 19 мая 2007 г.
Пишем Tv программу на Django. Часть 2
Ну вот, модель мы создали. Таблицы создали. Теперь было бы их не плохо заполнить.
Заполнять будем парсером афиши@mail.ru Поэтому теперь мы будем злобными пиратами, ворующими чужой контент. Кому это не интересно можете пропускать эту запись сразу, а наполнять базу можно и какими-то легальными способами.
Мой способ - парсить майл.ру каждое воскресенье, доставая оттуда телепрограмму на следующую неделю. То есть сейчас необходимо написать какой-то скриптик, который потом нужно будет прописать в cron. И после этого мы всегда будем иметь актуальную информацию в нашей бд.
И так зайдём на http://pda.tv.mail.ru и выберем какой-нибудь канал. Смотрим урл, например: http://pda.tv.mail.ru/?gosetup=1&channel=365. 365 это тот Num который мы создавали в модели. Но как видно отображаются лишь актуальные телепередачи, поэтому переключимся в вид "весь день". Но теперь отображается программа на сегодня, а нам бы нужно бы иметь возможность получать программу на разные дни. Выберем какой-нибудь другой день... Замечательно! Спасибо мэйлу, нам необходимо только циклом ходить по страничкам вида http://pda.tv.mail.ru/?date=[date]&gosetup=1&period=3&channel=[Num] и парсить их.
Посмотрим исходный код...
Шикарно, ещё один респект мэйлу от меня.
Нужная нам информация находимтся в блоке между <!-- START: Programm Content --> и <!-- END: Programm Content -->
Там простая табличка, которую не составит большого труда отпарсить, тем более программы с описанием выделены тегом "b". Описание фильмов находится в теге "p" с классом t75. Ну вроде бы в устройстве сайта разобрались, Парсим!
Создаём новый файлики, я его назвал core.py Подключаем стандартные питоновские модули HTMLParser и urllib, ну и дальше по мануалу (хотя наверняка можно написать гораздо красивее):
и
Теперь скормив этим классам странички с программой или описанием будем получать только нужную информацию.
делается это вот так:
Для описаний аналогично.
Это всё конечно замечательно, но надо это всё ещё и в бд запихнуть.
Для этого нужно подключить к нашему скрипту django'вский ORM. Делается это довольно просто. А именно:
Ну вот, теперь мы можем использовать всю мощь Django в своём скрипте.
Подключаем из нашей недавно созданный модели классы каналы, программы и описания:
для работы с бд я написал отдельный класс, хотя можно было обойтись и без этого:
Вобщем-то всё. Осталось написать простой цикл обхода всех страниц и скрипт готов.
Взять его как всегда можно из моего svn:
svn checkout http://tvwatcher.googlecode.com/svn/tags/post2/ tvwatcher
Запускаем django сервер, заходим в админку, добавляем какой-нибудь канал, указывая в Num номер канала на мэйле. Запускаем core.py с ключом w, снова идём в админку и видим, что добавились программы и описания этого канала на неделю.
Заполнять будем парсером афиши@mail.ru Поэтому теперь мы будем злобными пиратами, ворующими чужой контент. Кому это не интересно можете пропускать эту запись сразу, а наполнять базу можно и какими-то легальными способами.
Мой способ - парсить майл.ру каждое воскресенье, доставая оттуда телепрограмму на следующую неделю. То есть сейчас необходимо написать какой-то скриптик, который потом нужно будет прописать в cron. И после этого мы всегда будем иметь актуальную информацию в нашей бд.
И так зайдём на http://pda.tv.mail.ru и выберем какой-нибудь канал. Смотрим урл, например: http://pda.tv.mail.ru/?gosetup=1&channel=365. 365 это тот Num который мы создавали в модели. Но как видно отображаются лишь актуальные телепередачи, поэтому переключимся в вид "весь день". Но теперь отображается программа на сегодня, а нам бы нужно бы иметь возможность получать программу на разные дни. Выберем какой-нибудь другой день... Замечательно! Спасибо мэйлу, нам необходимо только циклом ходить по страничкам вида http://pda.tv.mail.ru/?date=[date]&gosetup=1&period=3&channel=[Num] и парсить их.
Посмотрим исходный код...
Шикарно, ещё один респект мэйлу от меня.
Нужная нам информация находимтся в блоке между <!-- START: Programm Content --> и <!-- END: Programm Content -->
Там простая табличка, которую не составит большого труда отпарсить, тем более программы с описанием выделены тегом "b". Описание фильмов находится в теге "p" с классом t75. Ну вроде бы в устройстве сайта разобрались, Парсим!
Создаём новый файлики, я его назвал core.py Подключаем стандартные питоновские модули HTMLParser и urllib, ну и дальше по мануалу (хотя наверняка можно написать гораздо красивее):
class ChanalHTMLParser(HTMLParser):
""" Класс парсера для канала, создаём на основе HTMLParser """
def __init__(self):
HTMLParser.__init__(self)
self.content=0 # несколько служебных переменных - переключателей
self.flag=0
self.result=[]
self.buffer=[]
self.flag2=0
def handle_comment(self, data):
""" Ищет в комментах необходимые строки """
if "START: Programm Content" in data:
self.content=1 # говорим что начались полезные данные
elif "END: Programm Content" in data:
self.content=0 # а теперь закончились
def handle_starttag(self, tag, attrs):
""" Функция вызывается при нахождении открывающего тега """
if self.content==1: # Если мы внутри интересующий нас информации, то ищим в тегах
if tag=="td":
if len(attrs)>0:
if attrs[1][0]=="class" and attrs[1][1]=="time":
self.flag=1 # последующие данные, до закрытия тега будут временем
else: # иначе ссылкой и названием программы
if self.flag==2: # конечно если тег со временем уже закрыт
self.flag=3 # говорим, что нашли строку с названием
elif tag=="b" and self.flag==3: # встретили b - верный признак того, чтоо есть описание
self.flag2=1 # сообщаем об этом
elif tag=="a" and self.flag2==1 and self.flag==3: # а вот если встречаем a, то внутренность его нужна только если есть описание
self.buffer.append(attrs[0][1]) # если всё ok, то добавляем ссылку в буффер
def handle_data(self, data):
""" Выполняется каждый раз когда оказывается 'внутри' тегов """
if self.flag==1 or self.flag==3: # если там время или название
self.buffer.append(data) # то добавляем их в буффер
def handle_endtag(self, tag):
""" Ну а эта функция соответсвенно проверяет закрывающие теги """
if self.content==1: # снова делаем проверку
if tag=="b":
if self.flag2==1 and self.flag==3:
self.flag2=0 # обнуляем сообщение о описании
if tag=="td":
if self.flag==1: # если сейчас было 'время'
self.flag=2 # то сообщаем о том, что оно кончилось
elif self.flag==3: # если было название
self.result.append(self.buffer) # до добавляем буфер к результату
self.buffer=[] # очищаем его
self.flag=0 # и обнуляем флаг
и
class FilmHTMLParser(HTMLParser):
""" Класс парсера для описаний, создаём на основе HTMLParser """
content=0
flag=0
result=""
def handle_comment(self, data):
""" Ищем комменты """
if "BEGIN: Main table" in data:
self.content=1
elif "END: Main table" in data:
self.content=0
def handle_starttag(self, tag, attrs):
""" нужный тег с нужным классом """
if self.content==1:
if tag=="p":
if len(attrs)==1:
if attrs[0][1]=="t75":
self.flag=1
def handle_data(self, data):
""" добавляем содержимое """
if self.flag==1:
self.result+=data
def handle_endtag(self, tag):
if self.content==1:
if tag=="p":
self.flag=0
Теперь скормив этим классам странички с программой или описанием будем получать только нужную информацию.
делается это вот так:
p = ChanalHTMLParser()
sock = urllib.urlopen("http://pda.tv.mail.ru/?date="+date+"&channel="+str(chanal.Num)+"&period=3")
htmlSource = sock.read()
htmlSource = unicode(htmlSource, "cp1251") # переводим страничку в юникод. Советую все данный поступающие от пользователя или ещё откуда-нибудь сразу же переводить в unicode. Меньше проблем будет.
sock.close()
p.feed(htmlSource)
print p.result
Для описаний аналогично.
Это всё конечно замечательно, но надо это всё ещё и в бд запихнуть.
Для этого нужно подключить к нашему скрипту django'вский ORM. Делается это довольно просто. А именно:
from django.core.management import setup_environ
sys.path.append("Путь до проекта") # т.к core.py у меня лежит в папке с приложением, которое обычно находится внутри папки проекта, то в место прямого пути я написал вот такую штукенцию os.path.split(os.path.dirname(os.path.abspath(__file__)))[0]
import settings
setup_environ(settings)
Ну вот, теперь мы можем использовать всю мощь Django в своём скрипте.
Подключаем из нашей недавно созданный модели классы каналы, программы и описания:
from mysite.tv.models import Chanal, Program, Description
для работы с бд я написал отдельный класс, хотя можно было обойтись и без этого:
class DataBase2:
def get_chanals(self):
""" Функция просто получает все каналы """
chanals = Chanal.objects.all()
return chanals
def add_programm(self, chanal, ptime, title):
""" Функция добавляет программу """
if len(Program.objects.filter(Title=title, Time=ptime, Chanal=chanal))==0: # проверяем нет ли уже такой в бд
p = Program(Title=title, Time=ptime, Chanal=chanal)
p.save()
return p
else:
return None
def add_description(self, programm, content):
""" Функция добавляет описание """
if len(Description.objects.filter(Text=content, Program=programm))==0: # опять проверка на клонов
Description(Text=content, Program=programm).save()
Вобщем-то всё. Осталось написать простой цикл обхода всех страниц и скрипт готов.
Взять его как всегда можно из моего svn:
svn checkout http://tvwatcher.googlecode.com/svn/tags/post2/ tvwatcher
Запускаем django сервер, заходим в админку, добавляем какой-нибудь канал, указывая в Num номер канала на мэйле. Запускаем core.py с ключом w, снова идём в админку и видим, что добавились программы и описания этого канала на неделю.
TV программа и svn
Нужно как-то упорядочить код, я думаю.
Поэтому последняя версия всегда будет лежать в http://tvwatcher.googlecode.com/svn/trunk/
А исходный код который написан на момент написания каждого поста будет лежать в http://tvwatcher.googlecode.com/svn/tags/
Например исходный код первой части всегда будет доступен отсюда
Поэтому последняя версия всегда будет лежать в http://tvwatcher.googlecode.com/svn/trunk/
А исходный код который написан на момент написания каждого поста будет лежать в http://tvwatcher.googlecode.com/svn/tags/
Например исходный код первой части всегда будет доступен отсюда
пятница, 18 мая 2007 г.
Пишем Tv программу на Django. Часть 1
Ну вот блог создал. Чуть-чуть его покрутил, теперь и наполнять надо...
А наполнять я его по крайней мере по началу буду рассказом о том как я создавал простенькое web приложение на django. В процессе я учусь сам и возможно, кому-то это тоже поможет.
Моим приложением будет Tv программа. Точнее не так, а web2.0Tv программа. :)
Делаю я её в общем-то для себя и для моей не большой сети. Потому что на "внешке" всяких программ и так дополна. Тем не менее я попытаюсь написать тру приложение на django, xml+xlst (хотя в этом пока не уверн, может быть и обычным html'ем обойдусь), с ajax'ом, экспортом в разные форматы и тому подобной мишурой.
И так наверно приступим.
Для начала создаём проект:
создаём приложение:
сразу же подключаем его к проекту, для этого в settings.py в INSTALLED_APPS дописываем:
и настраиваем всё остальное, как Вам надо. Да не забудьте раскомментировать django.contrib.admin.
Отлично, теперь необходимо продумать и создать модель нашего приложения.
Что необходимо Tv программе передач:
Для каналов у меня получилась такая моделька:
Для программ:
Ну и наконец описание:
Теперь самое сложное. Нужно придумать как привязать это всю к пользователю. Для этого я создал класс setting. Он служит "мостиком" между пользователем и данными приложения.
Для связи этого класса с пользователем было бы логично написать что-то вроде:
Owner = models.ForeignKey(User)
Но тут появляется куча проблем. Где создавать этот класс? При регистрации пользователя? А если они уже есть, то придётся писать какой-то дополнительный скрипт. Короче мороки - куча. Очень красивое решение предложил Иван Сагалаев. У него даже целая статья этому посвящена, советую всем ознакомиться. Не долго думая, тупо копи-пастем его код. :) (как говориться сила OpenSource) После этого у нас появляется замечательное поле AutoOneToOneField. Теперь можно думать дальше. Сразу при планировании я решил, что каналы будут выводится блоками и соответственно было бы неплохо иметь возможность менять их местами. Думал как это сделать попроще довольно долго. Но ничего кроме как создать ещё один класс:
ничего придумать не смог. :( Если у кого есть идеи - поделитесь пожалуйста.
В результате для setting у меня получилось вот такая штука:
Всё что к этому времени написанно можно взять тут:
svn checkout http://tvwatcher.googlecode.com/svn/tags/post1/ tvwatcher
А последняя версия всегда будет доступна вот тут:
svn checkout http://tvwatcher.googlecode.com/svn/trunk tvwatcher
(если хочется просто посмотреть на код, то можно прямо браузером перейти по этим ссылкам и посмотреть)
Теперь в urls.py раскомментируем строчку
Выполняем в консоли:
Теперь по адресу http://127.0.0.1:8000/admin/ у вас лежит рабочая админка. Туда можно зайти и убедиться, что все наши классы правильно создались.
На этом пока всё. В следующей части займёмся наполнением базы данных.
P.S чуть не забыл. Я использую сторонний модуль pytils от Pythy. Просто закиньте модуль в директорию с проектом. После этого будет работать import.
А наполнять я его по крайней мере по началу буду рассказом о том как я создавал простенькое web приложение на django. В процессе я учусь сам и возможно, кому-то это тоже поможет.
Моим приложением будет Tv программа. Точнее не так, а web2.0Tv программа. :)
Делаю я её в общем-то для себя и для моей не большой сети. Потому что на "внешке" всяких программ и так дополна. Тем не менее я попытаюсь написать тру приложение на django, xml+xlst (хотя в этом пока не уверн, может быть и обычным html'ем обойдусь), с ajax'ом, экспортом в разные форматы и тому подобной мишурой.
И так наверно приступим.
Для начала создаём проект:
django-admin.py startproject mysite
создаём приложение:
cd mysite
python manage.py startapp tv
сразу же подключаем его к проекту, для этого в settings.py в INSTALLED_APPS дописываем:
'mysite.tv',
и настраиваем всё остальное, как Вам надо. Да не забудьте раскомментировать django.contrib.admin.
Отлично, теперь необходимо продумать и создать модель нашего приложения.
Что необходимо Tv программе передач:
- Каналы
- Программы
- Описание некоторых программ
- И каким-то образом привязать это всё к пользователю.
Для каналов у меня получилась такая моделька:
class Chanal(models.Model):
""" Модель канала """
class Admin: # Сразу же делаем их видимыми в админке
list_display = ('Title',) # отображаем только названия
def __str__(self): # вместа номера объекта будет возвращаться его название
return self.Title
Title = models.CharField(maxlength=50)
Icon = models.ImageField(blank=True, upload_to="tv/images")
Num = models.IntegerField(blank=True, null=True) # номер канала на сервере откуда его берём
Slug = models.SlugField(maxlength=255, editable=False) # название канала в транслите, нужно для нормальных урлов
def save(self):
""" Функция, которая изменяет стандартный метод сохранения для нормального сохранения поля slug """
self.Slug = slugify(self.Title.decode(settings.DEFAULT_CHARSET))
super(Chanal, self).save()
Для программ:
class Program(models.Model):
class Admin: # аналогично делаем видимым в админке
list_display = ('Title', 'Time', 'Chanal') # отображаем название, время и канал в 3 колонки
def __str__(self):
return self.Title
Title = models.CharField(maxlength=150)
Time = models.DateTimeField(blank=True)
Chanal = models.ForeignKey(Chanal) # привязывает программу к каналу
Ну и наконец описание:
class Description(models.Model):
class Admin:
list_display = ('Program', 'Text',)
def __str__(self):
return self.Text[:50]
Text = models.TextField(blank=True)
Program = models.ForeignKey(Program)
Теперь самое сложное. Нужно придумать как привязать это всю к пользователю. Для этого я создал класс setting. Он служит "мостиком" между пользователем и данными приложения.
Для связи этого класса с пользователем было бы логично написать что-то вроде:
Owner = models.ForeignKey(User)
Но тут появляется куча проблем. Где создавать этот класс? При регистрации пользователя? А если они уже есть, то придётся писать какой-то дополнительный скрипт. Короче мороки - куча. Очень красивое решение предложил Иван Сагалаев. У него даже целая статья этому посвящена, советую всем ознакомиться. Не долго думая, тупо копи-пастем его код. :) (как говориться сила OpenSource) После этого у нас появляется замечательное поле AutoOneToOneField. Теперь можно думать дальше. Сразу при планировании я решил, что каналы будут выводится блоками и соответственно было бы неплохо иметь возможность менять их местами. Думал как это сделать попроще довольно долго. Но ничего кроме как создать ещё один класс:
class Place(models.Model):
Chanal = models.ForeignKey(Chanal)
N = models.IntegerField()
ничего придумать не смог. :( Если у кого есть идеи - поделитесь пожалуйста.
В результате для setting у меня получилось вот такая штука:
class Setting(models.Model):
# привязываем класс к пользователю... или наоборот... Не важно.
Owner = AutoOneToOneField(User, related_name='tv_Setting')
# Режим выводить все программы за день или только актуальные. То есть те, которые идут сейчас или начинаются позже
Mode = models.CharField(maxlength=50)
# Список каналов которые пользователь хочет видеть на главной странице c их номерами
Chanals = models.ManyToManyField(Place)
class Admin:
pass # если в админке видить класс хотим, но ничего специфичного нет, можно как в туториале просто написать pass. Тогда будет выводится название объекта, который у нас подменяется именем пользователя.
def __init__(self, *args, **kwargs):
""" Функция вызывается при создании объекта. То есть когда пользователь первый раз обращается к нашему приложению. """
super(Setting, self).__init__(*args, **kwargs)
self.Mode = 'new' # Устанавливаем режим в "только актуальные"
def __str__(self):
return str(self.Owner)
Всё что к этому времени написанно можно взять тут:
svn checkout http://tvwatcher.googlecode.com/svn/tags/post1/ tvwatcher
А последняя версия всегда будет доступна вот тут:
svn checkout http://tvwatcher.googlecode.com/svn/trunk tvwatcher
(если хочется просто посмотреть на код, то можно прямо браузером перейти по этим ссылкам и посмотреть)
Теперь в urls.py раскомментируем строчку
(r'^admin/', include('django.contrib.admin.urls')),
Выполняем в консоли:
python manage.py syncdb
python manage.py runserver
Теперь по адресу http://127.0.0.1:8000/admin/ у вас лежит рабочая админка. Туда можно зайти и убедиться, что все наши классы правильно создались.
На этом пока всё. В следующей части займёмся наполнением базы данных.
P.S чуть не забыл. Я использую сторонний модуль pytils от Pythy. Просто закиньте модуль в директорию с проектом. После этого будет работать import.
Подписаться на:
Сообщения (Atom)