Веб-стандарты и компромиссы
Всю жизнь человека преследуют компромиссы. Даже самые принципиальные люди, желающие жить в обществе, понимают, что есть моменты в которых компромисс просто необходим.
В мире веб-разработки да и вообще программирования компромиссы встречаются не менее часто. Хоть это и виртуальный мир окон, диалогов и кнопок, стоят за ним вполне реальные люди.
Об одном из таких компромиссов я хотел бы сегодня рассказать. Любой веб-разработчик знает про существование веб-стандартов, придуманных, как и любой стандарт, для облегчения работы и координации множества людей при выполнении схожих или одинаковых задач. Весь веб построен на стандарте HTML, и я думаю, что 99% веба работает с использованием CSS. HTML и CSS — две главные технические составляющие дизайна любого веб-сайта. Но как известно, мало кто из дизайнеров пользуется веб-стандартами (HTML/CSS) для создания дизайна, у них есть свои инструменты (графические редакторы) для решения этой задачи.
Процессом превращения картинок дизайна в HTML/CSS представление занимаются как раз выше упомянутые веб-разработчики, процесс этот называется версткой.
Вот в момент этого магического «превращения» картинок в сайт возникает проблема или правильнее сказать дилема.
HTML сам по себе не служит для создания стиля или дизайна, это в первую очередь документ,
его цель донести до пользователя содержимое (контент). Я уже писал про семантику, не буду повторяться в рамках этой статьи.
Перехожу к сути. Для настройки стиля (читай дизайна) HTML-документа в веб-стандартах предусмотрено использование CSS. Часто возникает ситуация, при которой в HTML документ попадает избыточное кол-во элементов, которые не несут никакого содержимого для пользователя, а используются только в CSS для придания стиля/дизайна документу. Это некорректно с точки зрения семантики HTML. Но это не так страшно. Более серьезная проблема в том, что частенько приходится нарушать сам стандарт разметки документа (HTML) ради придания ему определенных стилей (через CSS). Профессионалы называют это на сленге «невалидный код», от английского «valid — верный, корректный».
Нельзя сказать что валидный HTML очень важен для работы пользователя. На самом деле браузеры вполне хорошо обрабатывают документы с ошибками. На сегодня это вопрос скорее всего принципиальный и технологический. Является ли корректность кода обязательной — тема для отдельной статьи. Я просто кратко выражу личное мнение. Для больших, посещаемых сайтов корректность HTML-кода важна, т.к. ими пользуются не только люди, но и роботы, т.е. компьютерные программы, которые автоматизируют какие-то действия. Например, делают экспорт новостей с крупного новостного сайта для автоматической рассылки по смс. В этом случае ваш HTML код будет выступать в роли своеобразного API. Но это лишь один из примеров важности корректного HTML-кода.
Так вот теперь о компромиссах. Есть еще не упомянутая сегодня веб-технология JavaScript. С помощью JavaScript и DOM можно в момент загрузки страницы браузером корректировать ваш код. В нашем случае интересует именно HTML. Так вот это и есть простой и эффективный компромисс, модифицировать готовый корректный HTML с целью добавления элементов, которые используются чисто для дизайна. При относительно небольшом кол-ве таких элементов, скорость загрузки/отображения страницы для пользователя останется незаметной.
В ближайшем будущем попробую написать заметку почему важна валидность кода. В ней попробую привести примеры «компромиссного» кода и замерить скорость его работы.
PS: статья получилась довольно общая и возможно не совсем интересная для «бывалых», но это только первая статья из серии посвященной веб-стандартам. To be continued...
Загрузка файлов в Django. FileField & upload_to. 3
На момент выхода версии 1.0 Django, одним из самых значительных изменений стал механизм загрузки (upload) и хранения (storage) файлов. Загрузка файлов без FileField в модели базы данных конечно очень редкий случай, поэтому начнем именно с одного из обязательных параметров FileField — upload_to.
Главная инновация — это гибкая возможность настройки этого самого upload_to. Раньше можно было только передавать строку
с кодированными strftime параметрами. Тем самым можно было разбивать
загруженные файлы по директориям, создаваемыми по дате, типа uploads/mp3/2008-12/uploaded.mp3. Сейчас параметром
upload_to может быть и callable, т.е. к примеру функция, которая должна возвращать полный путь с именем файла, куда
будет сохраняться загруженный файл. Функция должна принимать два параметра: instance и filename.
- instance — это экземпляр объекта модели, которой и принадлежит поле с типом FileField.
- filename — юникодное имя файла.
Эта функция будет запускаться перед сохранением в базу, поэтому если объект новый (вы не редактируете существующий объект, а создаете новый), то поле pk/id будет пустым и соотвественные Django сигналы еще не выполнены.
Думаю, что с примером кода все будет наиболее хорошо понятно.
Итак функция-callback, которую мы будем использовать в upload_path:
def make_upload_path(instance, filename):
"""Generates upload path for FileField"""
return u"uploads/%s/%s" % (instance.category.slug, filename)
Теперь пример модели с FileField:
class Upload(models.Model):
user = models.ForeignKey(User)
file = models.FileField(upload_to=make_upload_path)
category = models.ForeignKey(Category)
uploaded_date = models.DateTimeField(auto_now_add=True)
Все просто. Можно использовать множество параметров вашей модели для создания директорий
и упорядочивания файлов в них. И самое главное upload_to — самый простой способ
закачивания файлов с русскими (и другими юникодными) именами. Т.к. по-умолчанию
Django заменяет их на символ подчеркивания, что приводит к появлению файлов типа __________.jpg.
Если вы сделаете самую простую функцию с вовращением вида return u"uploads/mypath/%s" % (filename),
то получите русские имена файлов после закачки.
Официальная документация Django по FileField.
Django signals по-новому 1
На пути к 1.0 релизу Django претерпевал немало радикальных изменений. Одно из них рефакторинг системы сигналов.

Если вы первый раз читаете и не в курсе «что это такое и с чем его едят», то скажу в двух словах. Это система реагирования на события приложения. Любой JavaScript или прикладной UI программист хорошо знаком с системой событий (event) — клик мышкой, нажатие горячей клавиши и т.п. Для программистов серверной части веба все выглядит немного по другому. Есть HTTP-запрос и есть его обработчик, анализируется как правило URL на предмет «кому отправлять запрос». Но на самом деле это та же система сигналов-событий, только узкопрофилированная под обработку HTTP-запросов.
Оказывается серверная часть веб-приложения тоже может, и я уверен, просто должна генерировать намного больший спектр сигналов, чем просто обработку URL и данных запроса. С чем успешно и справляется Django. Теперь немного прозы. Какие же события может ловить Django «из-коробки».
pre_init— перед запуском метода-конструктора__init__()модели;post_init— после выполнения метода__init__()модели;pre_save— перед сохранением экземпляра модели в базу;post_save— после успешного сохранения экземпляра модели в базу;pre_delete,post_delete— перед и после удаления экземпляра модели из базы;post_syncdb— генерируется админкой Django после установки нового приложения (INSTALLED_APPS);request_started,request_finished,got_request_exception— генерируются при обработке HTTP-запросов;template_rendered— генерируется только в режиме тестирования приложения.
Итак сигналы *_init, _*save, *_delete и post_syncdb генерируются системой моделей Django и их можно импортировать из django.db.models.signals.
Сигналы обработки HTTP-запросов из django.core.signals. И тестовый template_rendered из django.test.signals.
А теперь реальный пример, как повесить обработчик на событие-сигнал сохранения модели. В нашем случае будет требоваться определить тип mime закачанного файла перед его сохранением в базу.
Код модели:
class MimeType(models.Model):
"""
Mime Types table
"""
name = models.CharField(max_length=200)
slug = models.SlugField()
def __unicode__(self):
return self.name
class Item(models.Model):
"""
Main file
"""
name = models.CharField(max_length=200)
slug = models.SlugField()
file = models.FileField(upload_to='files', blank=True)
mime = models.ForeignKey(MimeType, blank=True, null=True)
upload_date = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.name
def set_mime(self, mime):
obj, created = MimeType.objects.get_or_create(name=mime, slug=slugify(mime))
self.mime = obj
Посмотрите на метод set_mime модели Item. Он создает новый объект MimeType или использует если он уже создан.
При этом не сохраняя модель Item. Теперь я пишу функцию-callback, которая будет вызываться по событию сохранения модели Item.
import mimetypes
import os.path
# init mime types dict
mimetypes.init()
def add_mime_type(instance, **kwargs):
"""
Adding mime-type to uploaded file (for future use).
Would be called on post-save.
"""
if not hasattr(instance, 'mime') or not hasattr(instance, 'file'):
raise Exception("Object %s does not have 'mime' attribute! Can't set mime-type!" % instance)
if instance.file:
extension = os.path.splitext(instance.file.path)[1].lower()
instance.set_mime(mimetypes.types_map[extension])
Итак, функция-callback add_mime_type принимает параметр instance, который является экземпляром модели, сгенерировавшим сигнал (в нашем случае модели Item). Вторым параметром принимает словарь (dictionary). Кстати, это и есть один из моментов обратной несовместимости. После рефакторинга каждая функция-callback должна принимать параметр **kwargs.
Теперь третий самый простой шаг — связывание модели с сигналом. В самом конце файла моделей добавилась строчка:
models.signals.pre_save.connect(add_mime_type, sender=Item)
Теперь перед каждым сохранением объектов Item, будет выполняться проверка и установка Mime-типа.
Кстати, я советую если у вас больше одного сигнала, выносите их в отдельный файл signals.py или можно по-старинке писать в файле моделей (что ИМХО иногда мешает чтению кода).
Почитать про сигналы можно еще в официальной доке: Django Signals и Django Built-in signal reference. Почитать про рефаторинг можно на странице Backwards Incompatible Changes и в соответствующем коммите 8223.
Александр Кошелев тоже немного раньше написал по теме, статья «А вы поймали новые сигналы?» с хорошим примером создания абстрактного сигнала.
«На сегодня все. Вопросы в студию» :-)
Две модели в newforms 10
На днях нужно было за короткое время решить одну несложную и довольно типичную задачу на django — построение формы профайла пользователя. Я не стал искать в закладках ссылки на старые известные how-to от мэтров-джангистов :-), а попробовал посмотреть что же нового и интересного у нас имеется в последней версии django (trunk).
Итак, модель была приготовлена заранее, вопрос больше касался построения формы (через newforms) пользовательского профайла. Итак, модель.
class UserProfile(models.Model):
user = models.ForeignKey(User, verbose_name=_('user'), unique=True, related_name="profile")
country = models.ForeignKey(Country, verbose_name=_('country'), blank=True, null=True)
city = models.CharField(verbose_name=_('city'), max_length=30, blank=True, null=True)
post_zip = models.CharField(verbose_name=_('post zip'), max_length=10, blank=True)
address = models.CharField(verbose_name=_('address'), max_length=250, blank=True)
Обратите внимание, что профайл ссылается на модель User с unique=True. Это в принципе самый простой и работающий вариант расширения модели пользователя в django. Можно смело использовать для объекта User метод profile.get(). Например:
from django.contrib.auth.models import User
u = User.objects.get(username='test')
profile = u.profile.get()
Теперь для моделей User и UserProfile необходимо построить единую форму. Для каждой отдельной модели сделать это не проблема. А вот сделать «два в одном» не так просто. Итак, по порядку:
Новенькое — широко используемые функции form_for_model и form_for_instance оказывается уже не актуальны. Они работают, но есть более новый и интересный метод получения формы для модели — ModelForm.
В отличии от форм полученных через form_for_model и form_for_instance, формы от ModelForm можно наследовать (а вот это очень интересно в нашем случае!!!).
Поэтому, как только эти два пункта появились в моей голове, я сразу же открыл Python-консоль и начал экспериментировать с кодом. Не уверен на 100%, что мое решение максимально красивое, но тем не менее оно вполне простое и аккуратное (особенно если учесть, что времени на «подумать» особо не было)...
Итак кусочек моего forms.py ниже.
from django.newforms.models import ModelForm
from django.contrib.auth.models import User
from django import newforms as forms
from profile.models import UserProfile
class ProfileForm(ModelForm):
class Meta:
model = UserProfile
fields=('country', 'city', 'post_zip', 'address')
class ProfileFormFull(ProfileForm):
def __init__(self, *args, **kwargs):
super(ProfileFormFull, self).__init__(*args, **kwargs)
self.fields['first_name'] = User._meta.get_field('first_name').formfield()
self.fields['last_name'] = User._meta.get_field('last_name').formfield()
def save_user_data(self):
if self.instance:
user = self.instance.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
return user.save()
else:
raise instance.DoesNotExist
Теперь мои комментарии к коду (что я делаю):
- Создаю форму ProfileForm из моей модели UserProfile.
- Наследую от ProfileForm новую форму ProfileFormFull, которую буду расширять для возможности редактирования двух моделей (User и UserProfile) в одной форме.
- Суть расширения в добавлении двух полей из модели User (first_name и last_name) и метода save_user_data который эти поля сохраняет в модель User.
- Остальные поля из модели UserProfile обрабатываются автоматически созданным методом save.
- При сохранении формы нужно вызывать два метода — save и save_user_data. Я специально оставил эту возможность для моего случая. Но вы можете сделать сохранение save_user_data внутри save, расширив его.
Как выглядит код, который работает с этой формой:
@login_required
@render_to('profile/main.html')
def profile_save(request):
if request.method == 'GET':
return HttpResponseRedirect(reverse('profile.views.profile_page'))
profile, created = UserProfile.objects.get_or_create(user=request.user)
form = ProfileFormFull(data=request.POST, instance=profile)
if form.is_valid():
form.save()
form.save_user_data()
request.user.message_set.create(message="Your profile details saved.")
return HttpResponseRedirect(reverse('profile.views.profile_page'))
else:
return {'profile': profile, 'profile_form': form, 'user': request.user}
На этом пожалуй откланиваюсь. Был бы очень рад любым конструктивным замечаниям и вопросам в комментариях. Хотелось бы унифицировать кусок кода с формой (forms.py) и может вынести в отдельный сниппет.
PS: обязательно почитайте документацию по ModelForm, интересно :-)
Пользовательский интерфейс в Web 3
Последний год я стал замечать что слово «юзабилити» стало проскакивать все чаще во всякого рода компьютерных публикациях рунета. Сейчас особо часто это слово замечается рядом с более нашумевшим «Веб 2.0». Все чаще слышатся оптимистичные заявления о переходе на приложения с веб-интерфейсом и в большинстве случаев имеются ввиду приложения Гугл.
И, как разработчик, я хотел бы представить читателю (который скорее всего является подобным мне разработчиком) свой взгляд на проблемы и решения создания веб-приложений и их интерфейсов.
Веб живет своей жизнью и жизнь эту можно отметить достаточно бурной эволюцией и развитием. Особенно последнии годы в период раздувания «Веб 2.0 пузыря». И самое интересное в этом развитии, то, что «атовизмы» отсыхают и отпадают не так быстро, как хотелось бы. А новые способности и возможности адаптируются и применяются наоборот достаточно медленно. Именно с этого я хочу начать свое рассуждение, т.к. считаю вопрос совместимости основным для успешного развития веб-интерфейсов. Я не буду приводить множество примеров о глючном IE или отличным рендерингом css-свойств в разных браузерах. Или взять тот-же флеш от Адоби, который, несмотря на долгую историю развития и мажорную версию 9, все еще преподносит немало сюрпризов.
Цель этой заметки — небольшая аналитка, собранная из личного опыта. Как построить удобный пользовтельский интерфейс для веба. Итак, мои советы.
Кроссбраузерная верстка. Не всегда дается легко, особенно при использовании сложного размещения блоков в дизайне. Но это первое и главное правило, если вы делаете проект в рамках обычного html интерфейса.
Не пугайтесь невалидного кода. Да, если можно делайте код красивым, но помните что код читают роботы, а сайтом пользуются люди. Поэтому чаще полезнее провести время в проектировании, тестировнии интерфейса или еще что вы посчитаете более важным.
Меньше используйте мешанины flash-html. Если вы хотите использовать флеш, сделайте на нем отдельную часть сайта, чтобы пользователь варился в ней как в отдельном приложении. Все дело в том, что флеш не учитывает особенностей исполнения интерфейса платформы на которой работает конечный пользователь. Поэтому контролы будут сильно отличаться внешне и по принципу работы, что будет раздражать пользователя. Пользователю легче привыкнуть к одному месту на сайте где все по другому, чем постоянно переключать внимание.
При использовании Ajax старайтесь всегда делать версию сайта, работающую в стиле old-school. Формы, поля, ссылки. В случае глюков или тормозов JS опытные пользователи всегда смогут перейти к обыкновенному режиму работы.
Используйте готовые JS/Ajax библиотеки. В них учтены уже кросс-браузерность и неровности работы JS. Писать «с нуля» свое слишком дорого и не оправдано.
Делайте сайты похожими на другие. Не надо клонировать дизайн или идеи. Я имею ввиду функциональные решения. Если есть тенденции их надо учитывать. Пользователю будет проще разобраться со всеми активными элементами сайта, если он их уже несколько раз встречал.
Пробуйте самые последнии и современные технологии веб, в которых уже учтены многие недостатки прошлых лет. На данный момент я имею ввиду декстопно ориентированные фреймвоки MS Silverlight и Adobe Air. Не обязательно вы их будете использовать в своих решениях, но быть «на коне» очень важно.

Семантическая верстка или тексты для роботов
После недавнего общения с Иваном, систематизировал в голове свои идеи насчет семантической верстки и связанных с ней нюансов.
Главный вопрос: а зачем нужна семантика в верстке? Может это просто очередной понт, который не имеет реально важного значения?
Хорошо, перейду сразу к делу без всякой теории.

Итак случай первый, когда семантика мне оказалась необходима. Я думаю все, кто разрабатывали средние и мелкие веб-сайты, имели дело с текстовыми материалами заказчика (сделанные обычно в M$ Word). Или возможно Вам просто приходилось переделывать чьи-то текстовые документы (например при подготовке к диплому или редактировании статьи). Я уверен, что любой человек, читающий эту заметку, испытывал трудности при редактировании чужого текста. Типичные примеры это сбивка и прыгание абзацев, заголовков и особенно списков. Так вот любой среднестатистический пользователь ПК, скорее всего, при создании списков сам проставляет номера элементов, забивает пробелами отступы в абзацах, вместо того чтобы воспользоваться соответствующими пунктами меню. В принципе это вполне нормально для круга задач среднестатистического пользователя ПК. Но если пойти чуть дальше в использовании текстового материала, например экспорта в другой формат, то глюки в форматировании текста не заставят себя долго ждать. И даже хорошо поддерживаемый вордом RTF покажет пользователю все прелести несемантической верстки. А если Вам нужно конвертировать на другой носитель информации (например с экрана на бумагу, я не беру в пример стандартное распечатывание страниц в M$ Word), то тут скорее всего пользователя ждет еще больше сюпризов (наиболее часто замечаемый – это переносы строк). А при импорте неправильно сверстанных (читай несемантично сверстанных) документов в среду веб, необходимо перелопачивать весь текст и исправлять (обычно в ручную) неправильные элементы верстки. Далее, если даже осмотреться в самой среде веб, уже можно заметить все больше разноплановых устройств, с помощью которых можно читать веб-страницы. Особенности семантической верстки очень актуальны для мобильных устройств, т.к. они имеют нестандартные размеры экрана и сами браузеры имеют разный уровень качества по парсингу (x)HTML. Поэтому, чтобы оставить на совесть разработчиков мобильного устройства неправильное отображение сверстанных Вами страниц, нужно делать все по стандартам (в которые потом можно будет тыкнуть пальцем ;-)). Возвращаясь к началу моего первого пункта, замечу, что почти всех своих клиентов по разработке веб-сайтов я сначала консультировал, как правильно готовить контент к импорту и публикации на сайт.

Случай второй, когда семантика очень полезна. Семантика в верстке всегда несет смысловую нагрузку. На любом этапе работы с контентом можно понять что хотел сказать контент-менеджер или просто автор текста. Если текст не единичный, а имеет какую-то периодичность, то четкое типовое форматирование намного снижает рутинную работу кодера или верстальщика. Также это очень помогает при создании стилей и оформления документов дизайнерами. Род работ зависит от типа документа (журнал, газета или веб-публикация), но идея семантически верного текста везде одинакова. Для меня это всегда было важно при написании стилей (CSS) для веб-страниц. Т.е. чем больше типовых элементов текста, тем более легко сверстать их внешний вид корректно и тем понятнее, что хотел сказать автор текста на всех промежутках работы над ним. И не забывайте про друга верстальщика – copy n paste ;-), тут семантика будет на Вашей стороне.

Случай третий, самый важный. Автоматизация обработки текста. Часто один и тот же текстовый контент приходится держать в нескольки форматах. Типичный пример – страница веб-сайта доступная как pdf-документ и имеющая версию для печати. Все три варианта текста будут иметь разный внешний вид. Если просто веб-страницу и версию для печати можно просто настроить с помощью стилей (CSS), то с pdf такой вариант не пройдет. Тут нужна конвертация. Как раз на этом этапе будет видно качество верстки текста. Но это не совсем полный пример автоматизации. Некоторое время назад (достаточно давно) я писал на PHP CMS движок для веб-сайта. Одним из требований была синхронизация сайта по нескольким зеркалам. Синхронизация БД не была возможна по некоторым техническим причинам, поэтому был выбран вариант хранения всей информации в XML-файлах. Но немного поразмыслив я решил использовать XHTML формат для контента, в итоге в чистом XMLе хранилась только карта сайта, из которой генерировались URL. Почему же я выбрал XHTML? Дело в том, что XHTML может рендерить сам браузер (мне его остается только стилизовать, например с помощью CSS), поэтому отпадает нобходимость в конвертации текста в HTML формат. С другой стороны XHTML дает возможность работать с текстом средствами XML-утилит. В итоге получилась очень удобая смесь. При сохранении контента он строго семантически форматировался в нужные элементы с соответствующими аттрибутами, что давало возможность мне использовать их для стилизации на стороне клиента (через CSS) и обработки (фильтрации, выборки и поиска) на стороне сервера. Давайте рассмотрим пример.
Отличная погода сегодня в городе
2007/09/22Весь город сегодня вышел на улицы, чтобы ощутить последнее осеннее тепло...
Подробнее ...some other text
В данном примере я могу использовать названия элементов и аттрибутов в CSS файле для стилизации текста и в тоже время на стороне сервера, например с помощью xpath я могу выбрать все заголовки новостей. Вот пример xpath-выражения: /body[@id='root-node']/div[@class='news-node']/h1[@class='news-head']. Самое главное что такой четко структуированный документ всегда будет легко экспортировать в любой другой формат. В данном случае, спустя год я переделывал сайт на django и буквально за несколько часов написал скрипт, который перевел весь контент (без единой потери) в базу джанги.
На Python такой запрос будет выгялдеть так:
import libxml2
xml = """ .... """ # тут кусок xml приведенный выше
doc = libxml2.parseDoc(xml)
head_node = doc.xpathEval("/body[@id='root-node']/div[@class='news-node']/h1[@class='news-head']")[0]
print head_node.contentКонечно я привел не все случаи и варианты применения семантической верстки, но это то, с чем я сталкивался сам. Интересно узнать мнения других. Так что пишите в комментариях, кто как использует семантику в реальной жизни.
XML-сервисы на Python. Часть первая. Создание и парсинг XML. 2
Недавно второй раз на практике столкнулся с серьезной задачей по работе с XML на Python. И второй раз был расстроен. К сожалению не все просто в Python настолько, насколько хотелось бы.
Говоря в общем, встроенная в последний Python (2.5) ElementTree не совсем хороший выбор, как по мне, для полноценной работы с XML. С помощью ElementTree удобно создавать XML-документы, но никак не парсить. Я был удивлен, что такая простая задача как принять XML-документ из переменной—окажется такой замороченной… ElementTree заточен для парсинга файлов (т.е. ему нужно передавать при открытии или путь или уже открытый файл). В моем случае я уже имел переменную из HTTP-запроса, обработанного Django. Несколько часов я плясал с бубном, сначала чтобы заставить принять XML-документ из переменной, потом уже с самим парсингом и получением аттрибутов тегов. В общем убил много времени впустую, что очень неприятно. В довесок ко всему общеизвестный факт что ElementTree имеет очень ограниченную документацию, поэтому время было потрачено дополнительно на гугление и ковыряние в его сырцах.
Хочу отметить что создание XML-документа с помощью ElementTree оказалось достаточно простым и удобным. А начинали мы именно с создания, поэтому и парсинг позже пытались делать тем же ElementTree.
В итоге, после всей возни, я решил для парсинга использовать отдельную библиотеку—Beautilful Soup. И работа сразу пошла очень быстро. Главное достоинство Beautilful Soup—это практически полная сериализация XML-документа в объекты Python. Что очень хорошо отражается на читаемости кода и очень удобно при отладке. Насчет создания документов с помощью Beautilful Soup, ничего сказать не могу, т.к. небыло времени для себя потестировать.
Из прошлого своего опыта также добавлю, что есть еще одна библиотека для работы с XML на Python—lxml. Она базируется на Cи библиотеках libxml и libxslt. Которые в свою очередь используются в большом количестве unix-приложений и других скриптовых языках. Прошлая моя задача была свзязана с выборкой данных из XML-документов с помощью Xpath. Так вот lxml и в частности libxml имеет хорошую и удобную реализацию Xpath. Так что рекомендую.
PS: Все написанное выше—сугубо личное мнение, на которое в принципе имею право :)
Ниже примеры кода.
# пример парсинга XML через Soup
from BeautifulSoup import BeautifulStoneSoup as Soup
def some_view(request):
message = Soup(request.raw_post_data)
some_obj = Obj(content = message.body.string, phone = message.sin.string)
# message.body - body это xml тег# пример создания XML-документа
import elementtree.ElementTree as ET
# создаем документ
root = ET.Element("message") # рутовый элемент
root.set("rid", "7idfndsi9s") # устанавливаем ему аттрибут
sn = ET.SubElement(root, "sn")
sn.text = "1039303"
body = ET.SubElement(root, "body")
body.set("content-type","text/plain")
body.text = "somte text content"
message = ET.tostring(root) 1039303 somte text content
Проверка HTTP-запроса на аяксовость (ajax) 4
При написании приложения по принципам MVC с использованием Ajax, часто для одного и того же запроса нужно получить данные в двух отличных друг от друга вариантах—в чистом HTML и в XML/json для Ajax запроса. А т.к. один и тот же контроллер в MVC обрабатывает оба типа запроса и скорее всего было бы удобно для его обработки использовать один и тот же URL, надо как-то отличать их, чтоб знать в каком формате отдать данные. Если это POST-запрос то можно просто создать дополнительное поле/переменную в запросе, а если GET, то нужно или отдельный URL для каждого типа данных или дополнительный заголовок в запросе (см. ниже почему).
Проблема мне кажется больше именно в GET запросе. Если следовать правилам ReST, то контент полученный по URL должен быть кеширован, а в нашем случае будет кеширован первый формат, в котором контроллер отдаст контент. Было бы удобно чтоб контроллер делал проверку на наличие каких-то заголовков и отдавал в зависимости от них ответ в нужном виде, но вот кеширование этому помешает. Т.к. кеш скеширует контент на первый попавшийся запрос (Ajax/HTML) и для следующего отличного запроса может быть отдан контент в неверном формате. Конечно можно с помощью заголовков вообще отключить кеширование, но это неоправдано, т.к. в протоколе HTTP не зря предусмтрена такая функциональность.
В общем решением была бы возможность реагирования кеша на определенные заголовки от сервера. И да, это указано в стандарте HTTP и реализовано как клиентами так и серверами. Есть такой заголовок в стандарте HTTP—Vary:, обычно его называют Vary Header. Он необходим как раз для осуществления правильного кеширование в зависимости от его значения. По большому счету он нужен чтоб указать кешу для какого заголовка запроса скеширован данный URL. Т.е. контент в кеше теперь будет идентифицироваться не только по URL, но по URL + заголовок указанный директивой Vary. Например, если пользователь залогинен на сайт с использованием Cookie, тогда заголовок Vary должен выглядеть так Vary: Cookie. URL в это случае, например, будет /profile/ (профиль пользователя) и два разных пользователя, попав на страницу из одного кеша будут проверены с помощью директивы Vary на предмет Cookie и получат разный контент. Или например кеширование должно производиться с учетом языка пользователя, тогда директива будет выглядить так Vary: Content-Language.
Зачем это нужно в нашем случае? Все просто—практически все Javascript Toolkits используют заголовок HTTP_X_REQUESTED_WITH со значением ‘XMLHttpRequest’. Так что если мы поставим Vary: HTTP_X_REQUESTED_WITH, то оба запроса к одному URL но от разных инициаторов (сам браузер или javascript/ajax код) будут скешированы отдельно.
Вот такой интересный нюанс. Может кому-то пригодится в деле.
Выбор платформы для разработки, часть первая
Любой разработчик часто или не очень, но решает этот вопрос. Всё зависит от того насколько часто вы меняете работу или учите и используете что-то новое (языки, технологии и т.д.)
Обычно при выборе решают такие вопросы:- какую ось выбрать;
- какой ide;
- как организовать исходники;
- опционально: как организовать команду;
- важный вопрос по организации самого кода;
- стоимость платформы (да бывают и платные компоненты :-)
- скорость развертывания платформы;
- насколько будет портабельно приложение.
В принципе можно много всего перечислить, но это что пришло в голову сразу.
На самом деле на такие темы можно книги писать. Я просто напишу пару наблюдений. Всё дело в том, что практически под каждый язык программирования приходится искать почти с нуля решение по платформе. Но как правило любая команда/фирма/фрилансер уже имеет свой джентельменский набор интсрументов и обычно всё обходится его небольшим дополнением/модификацией.
Лично я и моя команда сейчас наоборот только в поиске интсрументов, с платформой уже определились. Мы выяснили несколько важных вопросов для себя.
Думаю одним из важных принципов при выборе платформы должна быть относительная свобода разработчика. Именно по причине свободы многие сильные программисты не идут работать в большие корпорации, а работают в небольших командах. Им просто дают пользоваться теми инструментами, с которыми они привыкли работать.
Поэтому я хочу чтобы мы разделили платформу с понятием инструмента. Для себя я решил назвать платформой тот минимум, без которого приложение просто не сможет работать + систему контроля версия исходников. Т.е. это ОСь, необходимые библиотеки и сервер приложений/интерпретатор для языка. Всё остальное остается на усмотрение программиста. Я просто могу предложить проверенные инструменты для конкретного частного случая, не более.
Итак, если коснуться конкретики… Наша команда занимается веб-разработкой. Это сайты и веб-приложения средней сложности. На данный момент я активно использую python + django, но бывает еще приходится возвращаться к php. Для веб-приложения очень важен момент портабельности, поэтому выбор платформы в плане ОСи уже пал на юникс. Большинство хостинговых серверов работает на юниксе. Плюс мы делаем портирование при необходимости на винду.
Но в целом программист остается свободным в выборе интсрументов. Есть тестовый сервер, на котором всё должно работать, он просто делает периодические сверки и тесты работоспособности системы. Конкретизируя, приведу пример. Если человек работает на винде, то обычно он использует vmware с юниксом для основной разработки. Если человек изначально работает под юниксом, всё намного проще. Весь софт работает тогда под родной системой. В любом случае в нашем случае программист подстраивается под целевую ОСь, на которой всё будет работать – это и есть ограничение, остальное на его усмотрение.
Конечно же в любой команде есть свои правила по написанию кода, форматированию и документированию, но это уже не относится к теме.
Суть статьи в том, что на самом деле не так уж страшно давать программисту свободу. Просто должны быть средства контроля работы приложения.






