Главная / Веб-программирование / Веб-программирование Go: Создание веб-приложения с помощью Beego – Часть 2

Веб-программирование Go: Создание веб-приложения с помощью Beego – Часть 2

Эта статья является второй из двух частей цикла о разработке веб-приложений с помощью Beego:

  • Go: Создание веб-приложения с помощью Beego;
  • Go: Создание веб-приложения с помощью Beego – Часть 2

Go: Создание веб-приложения с помощью Beego – Часть 2

Приветствую вас во второй части цикла статей, где мы продолжим рассмотрение фреймворка Beego. Если вы пропустили первую часть, я рекомендую вам прочитать ее, так как она закладывает базу для этой серии статей.

В первой части, мы успешно начали работу с Beego, установили сам фреймворк и инструмент командной строки Bee, создали базовый проект, добавили действие контроллера, создали базовый шаблон представления, добавили пользовательский маршрут и завершили статью рассмотрением того, как работать с параметрами запроса.

В этой статье мы рассмотрим несколько интересных аспектов построения веб-приложений за счет интеграции данных, в частности, с SQLite3, а также я приведу обзор моделей, форм и валидации. Я надеюсь, что вы готовы начать, это будет интересно.

Двухуровневые представления

Обратите внимание, что в следующем коде у нас есть несколько функций в контроллере управления:

manage.Layout = "basic-layout.tpl"
manage.LayoutSections = make(map[string]string)
manage.LayoutSections["Header"] = "header.tpl"
manage.LayoutSections["Footer"] = "footer.tpl"

Данный код задает структуру двухуровневого представления. Если вы не знакомы с этим термином, то это структура, при которой у нас есть внешняя конструкция, которая всегда отображается одинаково, например, сайдбар, меню навигация, шапка и футер, и внутренний контент, который изменяется в зависимости от выполняемых действий:

Go: Создание веб-приложения с помощью Beego – Часть 2

Приведенное выше изображение иллюстрирует, что я имею в виду. Зеленые секторы — это наружное представление оболочки, красные — контент, который изменяется на основе выполнения действия.

Ссылаясь на Layout и LayoutSections, мы можем указать шаблон представления внешней структуры, basic-layout.tpl и другие подчиненные шаблоны, в нашем случае это шапка и футер, доступные в файлах header.tpl и footer.tpl соответственно.

Таким образом, контент, генерируемый нашим шаблоном действий, вставляется в шаблон представления оболочки, а после указания {{.LayoutContent}} шапка и подвал будут доступны в {{.Header}} и {{.Footer}} соответственно.

Модели

Чтобы добавить в Beego поддержку баз данных, нам нужно сделать несколько вещей. Во-первых, мы должны настроить некоторые модели.

Модели, по сути, представляют собой всего лишь структуры с некоторой дополнительной информацией. Ниже приводится содержимое файла модели, который вы можете найти по адресу models/models.go, его мы будем использовать в остальной части приложения:

package models

type Article struct {
Id int `form:"-"`
Name string `form:"name,text,name:" valid:"MinSize(5);MaxSize(20)"`
Client string `form:"client,text,client:"`
Url string `form:"url,text,url:"`
}

func (a *Article) TableName() string {
return "articles"
}

Вы можете видеть, что здесь есть одна модель, Article, которая задает очень простую статью сайта и содержит четыре свойства: Id, Name, Client и Url. Хочу обратить ваше внимание на то, что для каждого свойства существуют дополнительные данные, обозначаемые form и valid.

Это очень простой способ задействовать эту модель для обработки, как генерации формы, так и ее проверки. Давайте теперь разберем каждое из четырех свойств и выясним, что каждое из них делает:

Id int `form:"-"`

В нашей базе данных идентификатор является автоматически заполняемым полем. Это значение создается только в случае добавления новой записи, а в случае удаления, обновления или поиска записи нам предоставляется уже существующее значение. Так что, при определении form:»-«, Id не требуется:

Name string `form:"name,text,name:" valid:"MinSize(5);MaxSize(20)"`

Это уже несколько более сложный пример, так что давайте разберем его по частям, начиная с «name,text,name:«. Это означает, что когда происходит парсировка формы (о чем пойдет речь позже):

  • Значение из поля формы с названием name инициализирует свойство Name;
  • Поле является текстовым полем;
  • Значение метки будет установлено на ‘name:’.

Теперь давайте рассмотрим valid:»MinSize(5);MaxSize(20)». Этот код определяет два правила проверки: MinSize и MaxSize. По сути, предоставляемое значение должно состоять не менее чем из 5 символов, но не более чем из 20.

Существует и ряд других правил валидации, которые вы можете использовать, в том числе Range, Email, IP, Mobile, Base64 и Phone:

Client string `form:"client,text,client:"`
Url string `form:"url,text,url:"`

В последних двух примерах, Client принимает значение из поля формы client, является текстовым полем и имеет метку client:. Url принимает значение от поля формы url, является текстовым полем и имеет метку url:. Теперь, что касается функции TableName.

Причина, по которой я добавил ее, заключается в том, что название таблицы статьи не совпадает с названием структуры. Она называется articles. Если они будут называться одинаково, она может автоматически быть найденной ORM Beego.

Тем не менее, я сознательно изменил это, потому что хотел показать вам, что нужно будет сделать, если названия ваших таблиц и структур отличаются. Теперь, когда мы заговорили о схеме таблицы, я должен включить и ее:

CREATE TABLE "articles" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(200) NOT NULL,
"client" varchar(100),
"url" varchar(400) DEFAULT NULL,
"notes" text,
UNIQUE (name)
);

Интеграция моделей в приложение

Теперь, когда мы создали и настроили нашу модель с сопроводительной формой и валидационной информацией, мы должны сделать ее доступной в нашем приложении. В файле main.go, нам нужно вставить еще три оператора импорта:

"github.com/astaxie/beego/orm"
_ "github.com/mattn/go-sqlite3"
models "sitepointgoapp/models"

Первый импортирует библиотеку ORM Beego, второй обеспечивает поддержку SQLite3, которая необходима, так как мы используем базу данных SQLite3. Третий импортирует модели, которые мы только что создали, давая им псевдоним models:

func init() {
orm.RegisterDriver("sqlite", orm.DR_Sqlite)
orm.RegisterDataBase("default", "sqlite3", "database/orm_test.db")
orm.RegisterModel(new(models.Article))
}

Последний шаг, который мы должны предпринять, это зарегистрировать driver, database и models, которые мы будем использовать в нашем приложении. Мы делаем это с помощью трех приведенных выше заявлений.

Указываем, что мы используем драйвер SQLite и устанавливаем его в качестве соединения с базой данных по умолчанию, при подключении к нашей тестовой базе данных, расположенной в файле database/orm_test.db.

И в конце мы регистрируем модели, которые собираемся использовать, в нашем случае, только models.Article.

CRUD-операции

После этого мы получаем интегрированную в приложение поддержку баз данных. Давайте начнем с двух простых CRUD-операций, delete и update. Ни одна из них не использует форму, так как я хотел, чтобы этот раздел был по возможности простым, больше ориентированным на ORM код, в отличие от кода формы и валидации. Мы будем осуществлять эти операции через действие Add.

Удаление записи

Мы собираемся задать действие удаления, которое должно удалять из нашей базы данных статью, основываясь на значении параметра id. В файле routers/routers.go добавьте в функцию инициализации следующий маршрут:

beego.Router("/manage/delete/:id([0-9]+)", &controllers.ManageController{}, "*:Delete")

Затем добавьте приведенный ниже код в файл controllers/manage.go. А затем давайте поэтапно разберем его:

func (manage *ManageController) Delete() {
// convert the string value to an int
articleId, _ := strconv.Atoi(manage.Ctx.Input.Param(":id"))

Здесь мы пытаемся получить параметр id и преобразовать его из строки в целое число, используя метод Atoi из пакета strconv. Это простой пример, поэтому я не рассматриваю возможные случаи возникновения различных ошибок, и сохраняю парсированное значение в articleId:

o := orm.NewOrm()
o.Using("default")
article := models.Article{}

Далее, мы инициализируем новый экземпляр объекта ORM и указываем, что мы используем базу данных по умолчанию. Мы можем создать любое количество соединений с базой данных, например одно для чтения, другое для записи и т.д. Наконец, мы создали новый, пустой экземпляр объекта модели Article:

// Сначала проверяем, существует ли статья
if exist := o.QueryTable(article.TableName()).Filter("Id", articleId).Exist(); exist {
if num, err := o.Delete(&models.Article{Id: articleId}); err == nil {
beego.Info("Record Deleted. ", num)
} else {
beego.Error("Record couldn’t be deleted. Reason: ", err)
}
} else {
beego.Info("Record Doesn’t exist.")
}
}

Теперь что касается сути функции. Сначала, мы запрашиваем таблицу статьи, проверяем, существует ли статья со значением id, соответствующим параметру id. Если это так, мы вызвать метод ORM Delete, передавая в него новый объект Article только с одним установленным свойством Id.

Если сообщение об ошибке нам не выдается, значит, статья была удалена и beego.Info вызывает создание в журнале лога об удалении записи, используя метод Info. Если выполнить операцию удаления не удалось, вызывается Error через передачу объекта err, который выводит на экран причину, по которой запись не может быть удалена.

Обновление записи

Мы рассмотрели удаление записи. Теперь давайте обновим запись. На этот раз для большей наглядности мы будем использовать мессенджер:

func (manage *ManageController) Update() {
o := orm.NewOrm()
o.Using("default")
flash := beego.NewFlash()

Как и прежде, мы инициализируем переменную ORM и указываем базу данных по умолчанию. После этого мы начинаем обработку объекта Beego Flash, который может хранить сообщения между запросами:

// преобразование строчного значения в целое число
if articleId, err := strconv.Atoi(manage.Ctx.Input.Param(":id")); err == nil {
article := models.Article{Id: articleId}

На этот раз мы попытаемся получить параметр id и инициализировать новую модель Article, если она доступна:

if o.Read(&article) == nil {
article.Client = "Sitepoint"
article.Url = "http://www.google.com"
if num, err := o.Update(&article); err == nil
{
flash.Notice("Record Was Updated.")
flash.Store(&manage.Controller)
beego.Info("Record Was Updated. ", num)
}

Затем мы вызываем метод Read, передавая ему объект статьи Article. Если запись, идентификатор которой совпадает с идентификатором, заданным в Article.Id, существует, он должен загрузить остальные свойства статьи из базы данных.

Если все так и произошло, мы устанавливаем для объекта свойства Client и Url и передаем его в метод Update, который будет обновлять запись в базе данных.

Если ошибки не произошло, мы вызываем функцию Notice для объекта Flash, передавая его в простое сообщение, а затем вызываем Store, чтобы сохранить информацию:

} else {
flash.Notice("Record Was NOT Updated.")
flash.Store(&manage.Controller)
beego.Error("Couldn’t find article matching id: ", articleId)
}
} else {
flash.Notice("Record Was NOT Updated.")
flash.Store(&manage.Controller)
beego.Error("Couldn’t convert id from a string to a number. ", err)
}

Если что-то пошло не так, например, запись обновить невозможно или мы не смогли преобразовать параметр id в целое число, мы отмечаем это во флэш-сообщении, а также в сообщении журнала логов:

// последующее перенаправление
manage.Redirect("/manage/view", 302)
}

И в конце мы вызываем метод Redirect, передавая ему URL-адрес, по которому мы хотим перенаправить пользователя и код статуса HTTP. Далее, независимо от того, смогли ли мы обновить запись или нет, мы будем перенаправлены по адресу /manage/view. Об этом я расскажу уже в следующем пункте.

Просмотр всех записей

Функция представлений имеет две основные задачи: во-первых, она отображает все существующие в таблице статьи, а, во-вторых, выводит любые флэш-сообщения, которые были установлены при обновлении записи. Таким образом, мы сразу узнаем, удалось ли это действие или нет:

func (manage *ManageController) View() {
flash := beego.ReadFromRequest(&manage.Controller)

if ok := flash.Data["error"]; ok != "" {
// Вывод сообщения об ошибке
manage.Data["errors"] = ok
}

if ok := flash.Data["notice"]; ok != "" {
// Вывод сообщения об ошибке
manage.Data["notices"] = ok
}

Сначала, считав запрос и отследив свойства error и notice, мы инициализируем переменную flash. Данные свойства определяют, что будет вызываться: flash.Notice или flash.Error. Если информация задана, она передается свойству Data, поэтому мы можем в шаблоне получить к ней доступ:

o := orm.NewOrm()
o.Using("default")

var articles []*models.Article
num, err := o.QueryTable("articles").All(&articles)

if err != orm.ErrNoRows && num > 0 {
manage.Data["records"] = articles
}
}

Как и в последних двух примерах, после этого мы устанавливаем подключение к базе данных по умолчанию и инициализируем фрагмент модели Article в articles. Затем мы вызываем метод ОРМ QueryTable, указав имя таблицы, а затем вызываем All, передавая фрагмент статьи, который будет выводиться в результатах поиска, если у нас есть соответствующие статьи.

Если все прошло нормально, у нас будет набор соответствующих записей, и мы сохраним их в переменной шаблона.

Добавление записи

Теперь давайте рассмотрим добавление записей с помощью действия Add, которое кроме ORM-взаимодействий использует также и формы, и валидацию:

func (manage *ManageController) Add() {
o := orm.NewOrm()
o.Using("default")
article := models.Article{}

Я пропускаю пояснение этого фрагмента кода, так как мы уже рассматривали его выше:

if err := manage.ParseForm(&article); err != nil {
beego.Error("Couldn’t parse the form. Reason: ", err)
} else {
manage.Data["Articles"] = article

Здесь мы вызываем метод ParseForm, передавая ему объект статьи. Если при этом не возникла ошибка, мы устанавливаем статью в качестве переменной шаблона, который помогает визуализировать форму, о чем я расскажу чуть позже:

if manage.Ctx.Input.Method() == "POST" {
valid := validation.Validation{}
isValid, _ := valid.Valid(article)
if !isValid {
manage.Data["Errors"] = valid.ErrorsMap
beego.Error("Form didn’t validate.")
} else {

В этом коде мы проверяем, использовался ли POST-метод. Если это так, мы устанавливаем новый объект Validation и передаем объект статьи в метод валидации, чтобы проверить валидность POST-данных в соответствии с правилами модели.

Если предоставленные данные не являются валидными, мы сохраняем все ошибки валидации, доступные в valid.ErrorsMap, в переменной шаблона Errors, а также создаем запись в журнале логов о том, что валидация не была пройдена. В противном случае, мы пытаемся вставить статью.

В любом случае произошла ли ошибка или нет, мы создаем следующую запись в журнале логов:

id, err := o.Insert(&article)
if err == nil {
msg := fmt.Sprintf("Article inserted with id:", id)
beego.Debug(msg)
} else {
msg := fmt.Sprintf("Couldn’t insert new article. Reason: ", err)
beego.Debug(msg)
}
}
}
}

Заключение

Мы закончили краткое рассмотрение работы с Beego. Конечно, в данном фреймворке существует еще так много различных функций, что описание их всех никак бы не уместилось в серию статей из двух частей.

Более того, некоторые из примеров, приведенных в этой статье, могли показаться вам немного странными. Это потому что я использовал не реальные примеры из рабочей среды, а более упрощенные случаи, чтобы нагляднее пояснить различные функции фреймворка.

Если вы удивляетесь, почему я выбрал для рассмотрения именно данные темы, то это потому, что я надеялся заинтересовать вас и дать стимул самостоятельно ознакомиться с другими функциями Beego. Я и сам до сих пор продолжаю отрывать его для себя и действительно наслаждаюсь работой с этим фреймворком. Планирую и в дальнейшем продолжать его использовать.

Если у Вас возникли вопросы, обязательно ознакомьтесь с онлайн документацией или напишите об этом в комментариях. Не забывайте, что код к этой серии статей доступен на Github, так что посмотрите его и поэкспериментируйте.

Перевод статьи «Go: Building Web Applications With Beego – Part 2» был подготовлен дружной командой проекта Сайтостроение от А до Я.

О нас seoexpert

продвижение сайта,seo оптимизация,поисковое продвижение,раскрутка сайтов,поисковая оптимизация,продвижение сайта в гугл,seo раскрутка,продвижение сайтов в яндексе,продвижение сайта в google,продвижение сайтов в топ 10,Оптимизация и продвижение сайтов,услуги продвижения сайта,заказать продвижение,продвижение сайтов в топ,сео раскрутка сайта

Смотрите также

Определение типа данных double в C, C++ и C#

Double – 64-разрядная переменная с плавающей запятой Тип double — это основной тип данных, который ...