Хостинг Django от «Джино»
Table of contents

Глава 7. Формы

Перевод © Попов Руслан <ruslan.popov • gmail>

HTML формы являются основой интерактивных веб-сайтов, от простой формы поиска Google и вездесущих форм для комментирования в блогах до сложных уникальных интерфейсов для ввода данных. Эта глава рассматривает возможности Django по обработке форм, проверки переданных значений. Здесь мы рассмотрим объекты HttpRequest и Form.

Получение данных из объекта запроса

Мы впервые коснулись объектов HttpRequest в главе «Представления и привязки URL» при начальном изучении функций представления, но мы многого тогда не рассказали. Помните, каждая функция представления принимала объект HttpRequest в качестве первого аргумента, как в нашем представлении hello():

from django.http import HttpResponse

def hello(request):
    return HttpResponse("Hello world")

HttpRequest объекты, в нашем случае представленные переменной request, обладают рядом интересных атрибутов и методов, с которыми вам следует познакомиться, чтобы вы знали их возможности. Вы можете использовать данные атрибуты для получения информации о текущем запросе (т.е., о пользователе и браузере, который запросил текущую страницу вашего сайта) во время выполнения функции представления.

Информация об URL

HttpRequest объекты содержат информацию о запрошенном URL:

Таблица 7.1. Информация об URL

Атрибут/МетодОписаниеПример
request.pathПолный путь, без домена, но с начальным слешом./hello/
request.get_host()Имя компьютера (т.е., «домен»).127.0.0.1:8000 или www.example.com
request.get_full_path()Равен path, но со строкой запроса (если она существует)./hello/?print=true
request.is_secure()True в случае использования HTTPS. Иначе False.True или False


Всегда используйте эти атрибуты/методы вместо прямого вписывания URL в ваши представления. Такой подход делает код более гибким и его можно будет повторно использовать в других местах. Простейший пример:

# BAD!
def current_url_view_bad(request):
    return HttpResponse("Welcome to the page at /current/")

# GOOD
def current_url_view_good(request):
    return HttpResponse("Welcome to the page at %s" % request.path)

Остальная информация о запросе

Атрибут request.META является словарём, который содержит все доступные HTTP заголовки текущего запроса, включая IP адрес посетителя и информацию об его браузере (название и версию). Следует отметить, что полный список доступных заголовков зависит от того, какие заголовки были посланы браузером посетителя и какие заголовки были установлены веб-сервером. Перечислим несколько стандартных ключей этого словаря:

  • HTTP_REFERER — ссылка на страницу с которой пришли на текущую, если такая существует.

  • HTTP_USER_AGENT — идентификационная строка браузера. Обычно выглядит так: "Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17".

  • REMOTE_ADDR — IP адрес пользователя, т.е., "12.245.67.89". (Если запрос был передан через прокси, тогда адрес может быть передан в виде списка: "12.245.67.89,23.45.78.90".)

Следует отметить, что так как request.META является обычным словарём языка Python, вы получите исключение KeyError, если попытаетесь использовать несуществующий ключ. (Так как HTTP заголовки являются внешними данными — они передаются браузерами посетителей — им не стоит доверять, и вы должны всегда проектировать ваше приложение на грамотную обработку возникающих ошибок, если нужного заголовка нет или он пустой.) Следует использовать блок try/except или метод get() для обработки случая с неопределёнными ключами:

# BAD!
def ua_display_bad(request):
    ua = request.META['HTTP_USER_AGENT']  # Might raise KeyError!
    return HttpResponse("Your browser is %s" % ua)

# GOOD (VERSION 1)
def ua_display_good1(request):
    try:
        ua = request.META['HTTP_USER_AGENT']
    except KeyError:
        ua = 'unknown'
    return HttpResponse("Your browser is %s" % ua)

# GOOD (VERSION 2)
def ua_display_good2(request):
    ua = request.META.get('HTTP_USER_AGENT', 'unknown')
    return HttpResponse("Your browser is %s" % ua)

Мы рекомендуем вам написать небольшое представление, которое будет отображать все данные из request.META, так вы сможете узнать что там есть. Вот так может выглядеть ваше представление:

def display_meta(request):
    values = request.META.items()
    values.sort()
    html = []
    for k, v in values:
        html.append('<tr><td>%s</td><td>%s</td></tr>' % (k, v))
    return HttpResponse('<table>%s</table>' % '\n'.join(html))

В качестве упражнения, вы можете преобразовать данный код для использования возможностей шаблонной системы Django, а не использовать «сырой» HTML.

Информация о переданных данных

Кроме основных метаданных о запросе, HttpRequest имеет два атрибута, которые содержат информацию переданную пользователем: request.GET и request.POST. Оба этих атрибута являются словарными объектами, которые предоставляют доступ к данным GET и POST.

Словарные объекты

Когда мы говорим, что request.GET и request.POST являются «словарными» объектами, мы подразумеваем, что они ведут себя как обычные словари языка Python, но технически не являются словарями. Например, request.GET и request.POST обладают методами get(), keys() и values() и вы можете выполнять итерацию по их ключам, так for key in request.GET.

Так в чём разница? В том, что эти объекты обладают дополнительными методами, которых нет у обычных словарей. Мы ещё остановимся на этом.

Вы можете встретиться с подобным термином — «файловые объекты» — такие объекты обладают несколькими основными методами, подобными read(), которые позволяют работать с ними почти как с настоящими файлами.

POST данные обычно получают из HTML <form>, а GET данные могут приходить как от форм, так и из URL.

kinev 3 months, 2 weeks ago
Answer Link

reply to aliksanderz
Ну что же вы делаете? Вы же решили изучить Джанго, значит надо руководствоваться идеологией фреймворка и советами авторов книги(как собственно авторов самого фреймворка). Ваше представление, не спорю, наверняка работает, но ведь это не "торт". Вот как на мой взгляд должно выглядеть представление:

def display_meta(request):
val_meta = request.META.items()
val_meta.sort()
return render_to_response('http_meta/list_meta.html', {'list_meta': val_meta})

В свою очередь у вас должен быть дочерний шаблон list_meta.html(названия могут быть, какие ваша душа пожелает, и это относиться ко всем рукотворным элементам), со следующим содержанием:

{% extends "base.html" %}

{% block title %}The list of Request_META{% endblock %}

{% block content %}
<table>
{% for k, v in list_meta %}
<tr><td>{{ k }}</td><td>{{ v }}</td></tr>
{% endfor %}
</table>
{% endblock %}

Спасибо большое за помощь, точно к такому же решению пришел, прочитав еще 3 главы далее. Увидел похожую схему в разборе одного из шаблона, только там вместо табличных тегов стояли <ul></ul>. Сразу же понял что ковырять надо не представления а шаблон.

aliksanderz 3 months, 2 weeks ago
Answer Link

Как видно дочерний шаблон обращается к основному шаблону base.html. Код этого шаблона:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<h1>My exsample site</h1>
{% block content %}{% endblock %}
{% block footer %}
<hr>
<p>Thanks for visiting my site.</p>
{% endblock %}
</body>
</html>

Я, конечно, не претендую на истину в последней инстанции, но считаю, что так будет гораздо "православней" и более идеологически верно:)

aliksanderz 3 months, 2 weeks ago
Answer Link

reply to kinev
Спасибо за ссылки и информацию к размышлению. Тему не открывал, думаю не такая уж большая у меня проблема что бы ради неё плодить темы на форумах. Пока что придумал вот такой ход.

def display_meta(request):
values = request.META.items()
values.sort()
html = []
for k, v in values:
html.append((k, v))
return render_to_response('chablon.html',{'key': html[i] [0], 'vol': html[i] [1] )

теперь думаю как значение переменной i перебрать.

За помощь спасибо!

Ну что же вы делаете? Вы же решили изучить Джанго, значит надо руководствоваться идеологией фреймворка и советами авторов книги(как собственно авторов самого фреймворка). Ваше представление, не спорю, наверняка работает, но ведь это не "торт". Вот как на мой взгляд должно выглядеть представление:

def display_meta(request):
val_meta = request.META.items()
val_meta.sort()
return render_to_response('http_meta/list_meta.html', {'list_meta': val_meta})

В свою очередь у вас должен быть дочерний шаблон list_meta.html(названия могут быть, какие ваша душа пожелает, и это относиться ко всем рукотворным элементам), со следующим содержанием:

{% extends "base.html" %}

{% block title %}The list of Request_META{% endblock %}

{% block content %}
<table>
{% for k, v in list_meta %}
<tr><td>{{ k }}</td><td>{{ v }}</td></tr>
{% endfor %}
</table>
{% endblock %}

kinev 3 months, 4 weeks ago
Answer Link

reply to alerion
А вы создавали тему на форуме с вопросом? http://djbook.ru/forum/topic/205/ в этой теме есть линки на примеры шаблонов. А вообще фильтр safe вам в помощь.

Спасибо за ссылки и информацию к размышлению. Тему не открывал, думаю не такая уж большая у меня проблема что бы ради неё плодить темы на форумах. Пока что придумал вот такой ход.

def display_meta(request):
values = request.META.items()
values.sort()
html = []
for k, v in values:
html.append((k, v))
return render_to_response('chablon.html',{'key': html[i] [0], 'vol': html[i] [1] )

теперь думаю как значение переменной i перебрать.

За помощь спасибо!

alerion 3 months, 4 weeks ago
Answer Link

reply to kinev
пытаюсь вынести html разметку в шаблон вот уже вторую неделю, на форуме информации по этому вопросу ноль. Может направите в какую сторону размышлять. Сейчас представление выглядит так
def display_meta(request):
values = request.META.items()
values.sort()
html = []
for k, v in values:
html.append('<tr><td>%s</td><td>%s</td></tr>' % (k, v))
return render_to_response('chablon.html',{'meta': '\n'.join(html))

chablon.html выглядит так
...
...
<table>{{meta}}</table>
...
...

все символы html разметки заменяются, почему то заменяются подстановками

<table> &lt;tr&gt; &lt;td&gt;

и вообще как всю разметку из представления вынести, подскажите пожалуйста!!! Заранее спасибо!

А вы создавали тему на форуме с вопросом? http://djbook.ru/forum/topic/205/ в этой теме есть линки на примеры шаблонов. А вообще фильтр safe вам в помощь.

kinev 4 months ago
Answer Link

пытаюсь вынести html разметку в шаблон вот уже вторую неделю, на форуме информации по этому вопросу ноль. Может направите в какую сторону размышлять. Сейчас представление выглядит так
def display_meta(request):
values = request.META.items()
values.sort()
html = []
for k, v in values:
html.append('<tr><td>%s</td><td>%s</td></tr>' % (k, v))
return render_to_response('chablon.html',{'meta': '\n'.join(html))

chablon.html выглядит так
...
...
<table>{{meta}}</table>
...
...

все символы html разметки заменяются, почему то заменяются подстановками

<table> &lt;tr&gt; &lt;td&gt;

и вообще как всю разметку из представления вынести, подскажите пожалуйста!!! Заранее спасибо!

alerion 7 months ago
Answer Link

reply to gamak
Блин, не то скопировал
return render_to_response('display_meta.html', {'html' : html})
конецчно же

Для вопросов есть форум: http://djbook.ru/forum/

gamak 7 months, 1 week ago
Answer Link

Блин, не то скопировал
return render_to_response('display_meta.html', {'html' : html})
конецчно же

rad 1 year ago
Answer Link

В XXI веке правильнее будет везде использовать UTF-8.

DenisKilimnik 1 year ago
Answer Link

Мне помогла такая конструкция:

html.append(('<tr><td>%s</td><td>%s</td></tr>' % (k, v)).decode('cp1251'))

alerion 1 year, 2 months ago
Answer Link

Может нужно было не str, а unicode использовать при формировании html.

rad 1 year, 2 months ago
Answer Link

В Meta передаётся информация на языке, который сложнее 7 бит ;)

ssyroezhkin 1 year, 2 months ago
Answer Link

У меня что-то не вышло с display_meta, ругается на UnicodeDecodeError


Ищем Python программистов

Found misprint?
Select it with the mouse and hit Enter
Ctrl-Enter
Processed:
33 1 199 25


The full repository of DjangoBook translation you can get on GitHub.
We appreciate your patches!

We are glad to hear your questions, comments or suggestions!
(Open in new tab)

Users number: 601

Русская группа

на поддержку перевода
Яндекс Яндекс.Деньги Хочу такую же кнопку
Ускорить процесс перевода!
ЯМ:41001223475816


© 2008-2012 Ruslan Popov @ gmail.com Powered by Django 1.2.5