Назидательный logout html view. Представления log in и log out
Отредактируйте файл urls.py приложения account :
from django.conf.urls import url from . import views urlpatterns = [ # previous login view # url(r"^login/$", views.user_login, name="login"), # login / logout urls url(r"^login/$" , "django.contrib.auth.views.login" , name="login" ), url(r"^logout/$" , "django.contrib.auth.views.logout" , name="logout" ), url(r"^logout-then-login/$" , "django.contrib.auth.views.logout_then_login" , name="logout_then_login" ), ]Мы заккоментировали шаблон URL-адреса для представления user_login , созданного ранее для использования представления login Джанго.
Создайте новый каталог в каталоге шаблонов приложения account и назовите его registration. Создайте новый файл в новом каталоге, назовите его login.html
{% extends "base.html" %} {% block title %}Log-in{% endblock %} {% block content %}
Log-in
{% if form.errors %}Your username and password didn"t match. Please try again.
{% else %}Please, use the following form to log-in:
{% endif %}Этот шаблон для входа очень похож на созданный ранее. Джанго использует AuthenticationForm , расположенную в django.contrib.auth.forms . Эта форма пытается проверить подлинность пользователя и порождает ошибку проверки, если имя пользователя было не верно. В этом случае мы можем искать ошибки с помощью команды {% if form.errors %} . Обратите внимание, что мы добавили скрытый элемент для отправки значения переменной с именем next .
Параметр next должен быть URL-адресом. Если этот параметр указан, то после входа пользователя в систему он перенаправляется на заданный URL-адрес.
Теперь создайте шаблон logged_out.html внутри каталога шаблона registration и вставьте в него следующий код:
{% extends "base.html" %} {% block title %}Logged out{% endblock %} {% block content %}
Logged out
You have been successfully logged out. You can log-in again.
{% endblock %}Это шаблон, который будет отображаться после входа пользователя в систему.
После добавления шаблонов URL-адресов и шаблонов для входных и выходных представлений сайт готов к входу в систему с использованием представлений аутентификации Джанго.
Обратите внимание, что представление logout_then_login , включенное в наш urlconf , не нуждается в шаблоне, поскольку он выполняет перенаправление на log in view .
Теперь создадим новый view для отображения панели мониторинга для пользователя, чтобы знать когда пользователь войдет в свою учетную запись. Откройте файл views.py приложения account и добавьте в него следующий код:
from django.contrib.auth.decorators import login_required @login_required def dashboard (request) : return render(request, "account/dashboard.html" , {"section" : "dashboard" })Мы добавляем в наше представление декоратор login_required authentication framework. Декоратор login_required проверяет, прошел ли текущий пользователь аутентификацию. Если пользователь прошел аутентификацию, рпедставление выполнится; Если пользователь не прошел аутентификацию, он будет перенаправлян на страницу входа.
Мы также определили переменную section . Мы собираемся использовать эту переменную для отслеживания того раздела сайта, за которым наблюдает пользователь.
Теперь необходимо создать шаблон для представления панели мониторинга. Создайте новый файл внутри шаблонов/учетной templates/account/ и назовите его dashboard.html :
{% extends "base.html" %} {% block title %}Dashboard{% endblock %} {% block content %}
Dashboard
Welcome to your dashboard.
{% endblock %}Затем добавьте следующий шаблон URL-адреса для этого измените файл urls.py приложения account :
Urlpatterns = [ # ... url(r"^$" , views.dashboard, name="dashboard" ), ]
Теперь отредактируйте файл settings.py :
from django.core.urlresolvers import reverse_lazy LOGIN_REDIRECT_URL = reverse_lazy("dashboard" ) LOGIN_URL = reverse_lazy("login" ) LOGOUT_URL = reverse_lazy("logout" )- LOGIN_REDIRECT_URL : Сообщает о том, на какой URL-адрес перенаправлять пользователя после входа в систему.
- LOGIN_URL : URL-адрес для перенаправления пользователя на вход (например, с помощью декоратора login_required )
- LOGOUT_URL : URL-адрес для перенаправления пользователя на выход
Теперь мы собираемся добавить к нашему базовому шаблону ссылки на вход и выход с сайта.
Для этого необходимо определить, вошел ли текущий пользователь в систему или нет, чтобы отобразить соответствующую текущему состоянию пользователя, ссылку. Текущий пользователь задается в HttpRequest объекте промежуточного класса authentication. Доступ к нему можно получить с помощью request.user . В запросе будет найден объект user, даже если user не прошел аутентификацию. Неаутентифицированный пользователь, задается в запросе в качестве экземпляра AnonymousUser . Наилучший способ проверки состояния аутентификаци текущего пользователя — вызов request.user.is_authenticated()
Отредактируйте в шаблоне base.html
Как можно видеть, меню сайта отображается только для пользователей, прошедших аутентификации. Мы также проверяем текущий раздел, чтобы добавить выбранный атрибут класса к соответствующему элементу
Откройте в браузере http://127.0.0.1:8000/account/login/ . Вы должны увидеть стриницу входа. Введите валидный логин и пароль. Вы увидите следующее:
Можно увидеть, что раздел My dashboard выделен с помощью CSS, так как он имеет class selected . Поскольку пользователь прошел аутентификацию, имя пользователя отображается в правой части header-а. Щелкните ссылку Logout . Вы увидите следующую страницу:
На этой странице можно увидеть, что пользователь вышел из системы, и поэтому больше не отображается меню веб-сайта. Ссылка в правой стороне хедера показывает теперь Log-in .
Если вы видите страницу log out из сайта администрирования Джанго, а не собственную страницу выхода из системы, проверьте настройки INSTALLED_APPS и убедитесь, что django.contrib.admin находится после account . Оба шаблона находятся в том же относительном пути, и загрузчик шаблона Джанго будет использовать первый найденный.
Многие начинают писать проект для работы с единственной задачей, не подразумевая, что это может вырасти в многопользовательскую систему управления, ну допустим, контентом или упаси бог, производством. И всё вроде здорово и классно, всё работает, пока не начинаешь понимать, что тот код, который написан — состоит целиком и полностью из костылей и хардкода. Код перемешанный с версткой, запросами и костылями, неподдающийся иногда даже прочтению. Возникает насущная проблема: при добавлении новых фич, приходится с этим кодом очень долго и долго возиться, вспоминая «а что же там такое написано то было?» и проклинать себя в прошлом.Вы можеть быть даже слышали о шаблонах проектирования и даже листали эти прекрасные книги:
- Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидесс «Приемы объектно ориентированного проектирования. Паттерны проектирования»;
- М. Фаулер «Архитектура корпоративных программных приложений».
Представленная статья будет полезна в первую очередь новичкам. Во всяком случае, я надеюсь что за пару часов вы сможете получить представление о реализации MVC паттерна, который лежит в основе всех современных веб-фреймворков, а также получить «пищу» для дальнейших размышлений над тем — «как стоит делать». В конце статьи приводится подборка полезных ссылок, которые также помогут разобраться из чего состоят веб-фреймворки (помимо MVC) и как они работают.
Прожженные PHP-программисты вряд ли найдут в данной статье что-то новое для себя, но их замечания и комментарии к основному тексту были бы очень кстати! Т.к. без теории практика невозможна, а без практики теория бесполезна, то сначала будет чуть-чуть теории, а потом перейдем к практике. Если вы уже знакомы с концепцией MVC, можете пропустить раздел с теорией и сразу перейти к практике.
1. Теория
Шаблон MVC описывает простой способ построения структуры приложения, целью которого является отделение бизнес-логики от пользовательского интерфейса. В результате, приложение легче масштабируется, тестируется, сопровождается и конечно же реализуется.Рассмотрим концептуальную схему шаблона MVC (на мой взгляд — это наиболее удачная схема из тех, что я видел):
В архитектуре MVC модель предоставляет данные и правила бизнес-логики, представление отвечает за пользовательский интерфейс, а контроллер обеспечивает взаимодействие между моделью и представлением.
Типичную последовательность работы MVC-приложения можно описать следующим образом:
- При заходе пользователя на веб-ресурс, скрипт инициализации создает экземпляр приложения и запускает его на выполнение.
При этом отображается вид, скажем главной страницы сайта. - Приложение получает запрос от пользователя и определяет запрошенные контроллер и действие. В случае главной страницы, выполняется действие по умолчанию (index ).
- Приложение создает экземпляр контроллера и запускает метод действия,
в котором, к примеру, содержаться вызовы модели, считывающие информацию из базы данных. - После этого, действие формирует представление с данными, полученными из модели и выводит результат пользователю.
Модель не должна напрямую взаимодействовать с пользователем. Все переменные, относящиеся к запросу пользователя должны обрабатываться в контроллере.
Модель не должна генерировать HTML или другой код отображения, который может изменяться в зависимости от нужд пользователя. Такой код должен обрабатываться в видах.
Одна и та же модель, например: модель аутентификации пользователей может использоваться как в пользовательской, так и в административной части приложения. В таком случае можно вынести общий код в отдельный класс и наследоваться от него, определяя в наследниках специфичные для подприложений методы.
Вид
— используется для задания внешнего отображения данных, полученных из контроллера и модели.
Виды cодержат HTML-разметку и небольшие вставки PHP-кода для обхода, форматирования и отображения данных.
Не должны напрямую обращаться к базе данных. Этим должны заниматься модели.
Не должны работать с данными, полученными из запроса пользователя. Эту задачу должен выполнять контроллер.
Может напрямую обращаться к свойствам и методам контроллера или моделей, для получения готовых к выводу данных.
Виды обычно разделяют на общий шаблон, содержащий разметку, общую для всех страниц (например, шапку и подвал) и части шаблона, которые используют для отображения данных выводимых из модели или отображения форм ввода данных.
Контроллер
— связующее звено, соединяющее модели, виды и другие компоненты в рабочее приложение. Контроллер отвечает за обработку запросов пользователя. Контроллер не должен содержать SQL-запросов. Их лучше держать в моделях. Контроллер не должен содержать HTML и другой разметки. Её стоит выносить в виды.
В хорошо спроектированном MVC-приложении контроллеры обычно очень тонкие и содержат только несколько десятков строк кода. Чего, не скажешь о Stupid Fat Controllers (SFC) в CMS Joomla. Логика контроллера довольно типична и большая ее часть выносится в базовые классы.
Модели, наоборот, очень толстые и содержат большую часть кода, связанную с обработкой данных, т.к. структура данных и бизнес-логика, содержащаяся в них, обычно довольно специфична для конкретного приложения.
1.1. Front Controller и Page Controller
В большинстве случае, взаимодействие пользователя с web-приложением проходит посредством переходов по ссылкам. Посмотрите сейчас на адресную строку браузера — по этой ссылке вы получили данный текст. По другим ссылкам, например, находящимся справа на этой странице, вы получите другое содержимое. Таким образом, ссылка представляет конкретную команду web-приложению.Надеюсь, вы уже успели заметить, что у разных сайтов могут быть совершенные разные форматы построения адресной строки. Каждый формат может отображать архитектуру web-приложения. Хотя это и не всегда так, но в большинстве случаев это явный факт.
Рассмотрим два варианта адресной строки, по которым показывается какой-то текст и профиль пользователя.
Первый вариант:
- www.example.com/article.php?id=3
- www.example.com/user.php?id=4
Второй вариант:
- www.example.com/index.php?article=3
- www.example.com/index.php?user=4
Подход с множеством точек взаимодействия вы можете наблюдать на форумах с движком phpBB. Просмотр форума происходит через сценарий viewforum.php , просмотр топика через viewtopic.php и т.д. Второй подход, с доступом через один физический файл сценария, можно наблюдать в моей любимой CMS MODX, где все обращения проходят черезindex.php .
Эти два подхода совершенно различны. Первый — характерен для шаблона контроллер страниц (Page Controller), а второй подход реализуется паттерном контроллер запросов (Front Controller). Контроллер страниц хорошо применять для сайтов с достаточно простой логикой. В свою очередь, контроллер запросов объединяет все действия по обработке запросов в одном месте, что даёт ему дополнительные возможности, благодаря которым можно реализовать более трудные задачи, чем обычно решаются контроллером страниц. Я не буду вдаваться в подробности реализации контроллера страниц, а скажу лишь, что в практической части будет разработан именно контроллер запросов (некоторое подобие).
1.2. Маршрутизация URL
Маршрутизация URL позволяет настроить приложение на прием запросов с URL, которые не соответствуют реальным файлам приложения, а также использовать ЧПУ , которые семантически значимы для пользователей и предпочтительны для поисковой оптимизации.К примеру, для обычной страницы, отображающей форму обратной связи, URL мог бы выглядеть так:
http://www.example.com/contacts.php?action=feedback
Приблизительный код обработки в таком случае:
switch
($_GET
["action"
])
{
case
"about"
:
require_once
("about.php"
); // страница "О Нас"
break
;
case
"contacts"
:
require_once
("contacts.php"
); // страница "Контакты"
break
;
case
"feedback"
:
require_once
("feedback.php"
); // страница "Обратная связь"
break
;
default
:
require_once
("page404.php"
); // страница "404"
break
;
}
Думаю, почти все так раньше делали.
С использованием движка маршрутизации URL вы сможете для отображения той же информации настроить приложение на прием таких запросов:
http://www.example.com/contacts/feedback
Здесь contacts представляет собой контроллер, а feedback — это метод контроллера contacts, отображающий форму обратной связи и т.д. Мы еще вернемся к этому вопросу в практической части.
Также стоит знать, что маршрутизаторы многих веб-фреймворков позволяют создавать произвольные маршруты URL (указать, что означает каждая часть URL) и правила их обработки.
Теперь мы обладаем достаточными теоретическими знаниями, чтобы перейти к практике.
2. Практика
Для начала создадим следующую структуру файлов и папок:
Забегая вперед, скажу, что в папке core будут храниться базовые классы Model, View и Controller.
Их потомки будут храниться в директориях controllers, models и views. Файл index.php
это точка в хода в приложение. Файлbootstrap.php
инициирует загрузку приложения, подключая все необходимые модули и пр.
Будем идти последовательно; откроем файл index.php и наполним его следующим кодом:
ini_set("display_errors"
, 1
);
require_once
"application/bootstrap.php"
;
Тут вопросов возникнуть не должно.
Следом, сразу же перейдем к фалу bootstrap.php
:
require_once
"core/model.php"
;
require_once
"core/view.php"
;
require_once
"core/controller.php"
;
require_once
"core/route.php"
;
Route::start(); // запускаем маршрутизатор
Первые три строки будут подключать пока что несуществующие файлы ядра. Последние строки подключают файл с классом маршрутизатора и запускают его на выполнение вызовом статического метода start.
2.1. Реализация маршрутизатора URL
Пока что отклонимся от реализации паттерна MVC и займемся мрашрутизацией. Первый шаг, который нам нужно сделать, записать следующий код в .htaccess :RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule .* index.php [L]
Этот код перенаправит обработку всех страниц на index.php , что нам и нужно. Помните в первой части мы говорили о Front Controller?!
Маршрутизацию мы поместим в отдельный файл route.php в директорию core. В этом файле опишем класс Route, который будет запускать методы контроллеров, которые в свою очередь будут генерировать вид страниц.
Содержимое файла route.php
class Route { static function start () { // контроллер и действие по умолчанию $controller_name = "Main" ; $action_name = "index" ; $routes = explode("/" , $_SERVER ["REQUEST_URI" ]); // получаем имя контроллера if (!empty ($routes )) { $controller_name = $routes ; } // получаем имя экшена if (!empty ($routes )) { $action_name = $routes ; } // добавляем префиксы $model_name = "Model_" .$controller_name ; $controller_name = "Controller_" .$controller_name ; $action_name = "action_" .$action_name ; // подцепляем файл с классом модели (файла модели может и не быть) $model_file = strtolower($model_name ).".php" ; $model_path = "application/models/" .$model_file ; if (file_exists($model_path )) { include "application/models/" .$model_file ; } // подцепляем файл с классом контроллера $controller_file = strtolower($controller_name ).".php" ; $controller_path = "application/controllers/" .$controller_file ; if (file_exists($controller_path )) { include "application/controllers/" .$controller_file ; } else { /* правильно было бы кинуть здесь исключение, но для упрощения сразу сделаем редирект на страницу 404 */ Route::ErrorPage404(); } // создаем контроллер $controller = new $controller_name ; $action = $action_name ; if (method_exists($controller , $action )) { // вызываем действие контроллера $controller ->$action (); } else { // здесь также разумнее было бы кинуть исключение Route::ErrorPage404(); } } function ErrorPage404 () { $host = "http://" .$_SERVER ["HTTP_HOST" ]."/" ; header("HTTP/1.1 404 Not Found" ); header("Status: 404 Not Found" ); header("Location:" .$host ."404" ); } }
Замечу, что в классе реализована очень упрощенная логика (несмотря на объемный код) и возможно даже имеет проблемы безопасности. Это было сделано намерено, т.к. написание полноценного класса маршрутизации заслуживает как минимум отдельной статьи. Рассмотрим основные моменты…
В элементе глобального массива $_SERVER["REQUEST_URI"] содержится полный адрес по которому обратился пользователь.
Например: example.ru/contacts/feedback
С помощью функции explode производится разделение адреса на составлющие. В результате мы получаем имя контроллера, для приведенного примера, это контроллер contacts и имя действия, в нашем случае — feedback .
Далее подключается файл модели (модель может отсутствовать) и файл контроллера, если таковые имеются и наконец, создается экземпляр контроллера и вызывается действие, опять же, если оно было описано в классе контроллера.
Таким образом, при переходе, к примеру, по адресу:
example.com/portfolio
или
example.com/portfolio/index
роутер выполнит следующие действия:
- подключит файл model_portfolio.php из папки models, содержащий класс Model_Portfolio;
- подключит файл controller_portfolio.php из папки controllers, содержащий класс Controller_Portfolio;
- создаст экземпляр класса Controller_Portfolio и вызовет действие по умолчанию — action_index, описанное в нем.
example.com/ufo
то его перебросит на страницу «404»:
example.com/404
То же самое произойдет если пользователь обратится к действию, которое не описано в контроллере.
2.2. Возвращаемся к реализации MVC
Перейдем в папку core и добавим к файлу route.php еще три файла: model.php, view.php и controller.php
Напомню, что они будут содержать базовые классы, к написанию которых мы сейчас и приступим.
Содержимое файла model.php
class
Model
{
public
function
get_data
()
{
}
}
Класс модели содержит единственный пустой метод выборки данных, который будет перекрываться в классах потомках. Когда мы будем создавать классы потомки все станет понятней.
Содержимое файла view.php
class
View
{
//public $template_view; // здесь можно указать общий вид по умолчанию.
function
generate
($content_view
, $template_view
, $data
= null)
{
/*
if(is_array($data)) {
// преобразуем элементы массива в переменные
extract($data);
}
*/
include
"application/views/"
.$template_view
;
}
}
Не трудно догадаться, что метод generate
предназначен для формирования вида. В него передаются следующие параметры:
- $content_file — виды отображающие контент страниц;
- $template_file — общий для всех страниц шаблон;
- $data — массив, содержащий элементы контента страницы. Обычно заполняется в модели.
для отображения контента конкретной страницы.
В нашем случае общий шаблон будет содержать header, menu, sidebar и footer, а контент страниц будет содержаться в отдельном виде. Опять же это сделано для упрощения.
Содержимое файла controller.php
class
Controller
{
public
$model
;
public
$view
;
function
__construct
()
{
$this
->view = new
View();
}
}
}
Метод action_index
— это действие, вызываемое по умолчанию, его мы перекроем при реализации классов потомков.
2.3. Реализация классов потомков Model и Controller, создание View"s
Теперь начинается самое интересное! Наш сайт-визитка будет состоять из следущих страниц:- Главная
- Услуги
- Портфолио
- Контакты
- А также — страница «404»
На предыдущем рисунке отдельно выделен файл template_view.php
— это шаблон, содержащий общую для всех страниц разметку. В простейшем случае он мог бы выглядеть так:
<html
lang
="ru"
>
<head
>
<meta
charset
="utf-
8
">
<title
>
Главнаяtitle
>
head
>
<body
>
$content_view
; ?>
body
>
html
>
Для придания сайту презентабельного вида сверстаем CSS шаблон и интегририруем его в наш сайт путем изменения структуры HTML-разметки и подключения CSS и JavaScript файлов:
<link
rel
="stylesheet"
type
="text/css"
href
="/css/style.css"
/>
<script
src
="/js/jquery-1.6.2.js"
type
="text/javascript"
>
script
>
В конце статьи, в разделе «Результат», приводится ссылка на GitHub-репозиторий с проектом, в котором проделаны действия по интеграции простенького шаблона.
2.3.1. Создадаем главную страницу
Начнем с контроллера controller_main.php , вот его код:class Controller_Main extends Controller { function action_index () { $this ->view->generate("main_view.php" , "template_view.php" ); } }
В метод generate экземпляра класса View передаются имена файлов общего шаблона и вида c контентом страницы.
Помимо индексного действия в контроллере конечно же могут содержаться и другие действия.
Файл с общим видом мы рассмотрели ранее. Рассмотрим файл контента main_view.php
:
<h1
>
Добро пожаловать!h1
>
<p
>
<img
src
="/images/office-small.jpg"
align
="left"
>
<a
href
="/"
>
ОЛОЛОША TEAMa
>
- команда первоклассных специалистов в области разработки веб-сайтов с многолетним опытом коллекционирования мексиканских масок, бронзовых и каменных статуй из Индии и Цейлона, барельефов и изваяний, созданных мастерами Экваториальной Африки пять-шесть веков назад...
p
>
Здесь содержиться простая разметка без каких либо PHP-вызовов.
Для отображения главной странички можно воспользоваться одним из следующих адресов:
- методы библиотек, реализующих абстракицю данных. Например, методы библиотеки PEAR MDB2;
- методы ORM;
- методы для работы с NoSQL;
- и др. Для простоты, здесь мы не будем использовать SQL-запросы или ORM-операторы. Вместо этого мы сэмулируем реальные данные и сразу возвратим массив результатов.
- Controller_Login в котором генерируется вид с формой для ввода логина и пароля, после заполнения которой производится процедура аутентификации и в случае успеха пользователь перенаправляется в админку.
- Contorller_Admin с индексным действием, в котором проверяется был ли пользователь ранее авторизован на сайте как администратор (если был, то отображается вид админки) и действием logout для разлогинивания.
Файл модели model_portfolio.php поместим в папку models. Вот его содержимое:
class Model_Portfolio extends Model { public function get_data () { return array (array ("Year" => "2012" , "Site" => "http://DunkelBeer.ru" , "Description" => "Промо-сайт темного пива Dunkel от немецкого производителя Löwenbraü выпускаемого в России пивоваренной компанией "CАН ИнБев"." ), array ("Year" => "2012" , "Site" => "http://ZopoMobile.ru" , "Description" => "Русскоязычный каталог китайских телефонов компании Zopo на базе Android OS и аксессуаров к ним." ), // todo ); } }
Класс контроллера модели содержится в файле controller_portfolio.php
, вот его код:
class
Controller_Portfolio
extends
Controller
{
function
__construct
()
{
$this
->model = new
Model_Portfolio();
$this
->view = new
View();
}
function
action_index
()
{
$data
= $this
->model->get_data();
$this
->view->generate("portfolio_view.php"
, "template_view.php"
, $data
);
}
}
В переменную data
записывается массив, возвращаемый методом get_data
, который мы рассматривали ранее.
Далее эта переменная передается в качестве параметра метода generate
, в который также передаются: имя файла с общим шаблон и имя файла, содержащего вид c контентом страницы.
Вид содержащий контент страницы находится в файле portfolio_view.php
.
Портфолио
Год | Проект | Описание | " .$row ["Year" ]." | " .$row ["Site" ]." | " .$row ["Description" ]." | " ; }