Давайте рассмотрим SQL запрос, который возвращает несколько записей из базы данных. Обновим метод SnippetModel.Latest(), чтобы он возвращал десять последних заметок (при условии, что срок их действия не истёк).
Мы будем использовать следующий SQL запрос:
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
|
1 2 |
SELECT id, title, content, created, expires FROM snippets WHERE expires > UTC_TIMESTAMP() ORDER BY created DESC LIMIT 10 |
Откройте файл pkg/models/mysql/snippets.go и обновите код для метода Latest():
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
package mysql import ( "database/sql" "errors" "golangify.com/snippetbox/pkg/models" ) type SnippetModel struct { DB *sql.DB } ... // Latest - Метод возвращает последние 10 заметок. func (m *SnippetModel) Latest() ([]*models.Snippet, error) { // Пишем SQL запрос, который мы хотим выполнить. stmt := `SELECT id, title, content, created, expires FROM snippets WHERE expires > UTC_TIMESTAMP() ORDER BY created DESC LIMIT 10` // Используем метод Query() для выполнения нашего SQL запроса. // В ответ мы получим sql.Rows, который содержит результат нашего запроса. rows, err := m.DB.Query(stmt) if err != nil { return nil, err } // Откладываем вызов rows.Close(), чтобы быть уверенным, что набор результатов из sql.Rows // правильно закроется перед вызовом метода Latest(). Этот оператор откладывания // должен выполнится *после* проверки на наличие ошибки в методе Query(). // В противном случае, если Query() вернет ошибку, это приведет к панике // так как он попытается закрыть набор результатов у которого значение: nil. defer rows.Close() // Инициализируем пустой срез для хранения объектов models.Snippets. var snippets []*models.Snippet // Используем rows.Next() для перебора результата. Этот метод предоставляем // первый а затем каждую следующею запись из базы данных для обработки // методом rows.Scan(). for rows.Next() { // Создаем указатель на новую структуру Snippet s := &models.Snippet{} // Используем rows.Scan(), чтобы скопировать значения полей в структуру. // Опять же, аргументы предоставленные в row.Scan() // должны быть указателями на место, куда требуется скопировать данные и // количество аргументов должно быть точно таким же, как количество // столбцов из таблицы базы данных, возвращаемых вашим SQL запросом. err = rows.Scan(&s.ID, &s.Title, &s.Content, &s.Created, &s.Expires) if err != nil { return nil, err } // Добавляем структуру в срез. snippets = append(snippets, s) } // Когда цикл rows.Next() завершается, вызываем метод rows.Err(), чтобы узнать // если в ходе работы у нас не возникла какая либо ошибка. if err = rows.Err(); err != nil { return nil, err } // Если все в порядке, возвращаем срез с данными. return snippets, nil } |
Важно: Закрытие набора результатов с использованием
defer rows.Close()является очень важным моментом. Пока набор результатов открыт, он будет поддерживать соединение с базовой базой данных открытым… поэтому, если в этом методе что-то пойдет не так и набор результатов не будет закрыт, это приведет к тому, что соединения с базой данных будет израсходовано в пустую.
Использование модели в обработчиках
Вернемся в файл cmd/web/handlers.go и обновим обработчик home(), таким образом, чтобы он вызвал метод SnippetModel.Latest() который выведет на страницу последние 10 заметок из базы данных .
А пока, просто закомментируем код, относящийся к рендерингу шаблона. Сделаем это следующим образом:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package main import ( "errors" "fmt" // "html/template" "net/http" "strconv" "golangify.com/snippetbox/pkg/models" ) func (app *application) home(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { app.notFound(w) return } s, err := app.snippets.Latest() if err != nil { app.serverError(w, err) return } for _, snippet := range s { fmt.Fprintf(w, "%v\n", snippet) } // files := []string{ // "./ui/html/home.page.tmpl", // "./ui/html/base.layout.tmpl", // "./ui/html/footer.partial.tmpl", // } // ts, err := template.ParseFiles(files...) // if err != nil { // app.serverError(w, err) // return // } // err = ts.Execute(w, nil) // if err != nil { // app.serverError(w, err) // } } ... |
Запускаем наше веб-приложение из терминала:
|
1 |
go run ./cmd/web |
Переходим на главную страницу http://127.0.0.1:4000 вы должны увидеть ответ, похожий на следующий:

У нас появились только 2 записи, хотя в базе данных у нас как минимум 4 заметок. Ниже вы можете увидеть содержимое таблицы snippets:
|
1 2 3 4 5 6 7 8 9 10 |
mysql> SELECT id, title, expires FROM snippets; +----+---------------------------------------------+---------------------+ | id | title | expires | +----+---------------------------------------------+---------------------+ | 1 | Не имей сто рублей | 2022-01-27 13:09:34 | | 2 | Лучше один раз увидеть | 2022-01-27 13:09:40 | | 3 | Не откладывай на завтра | 2021-02-03 13:09:44 | | 4 | История про улитку | 2021-04-28 14:39:12 | +----+---------------------------------------------+---------------------+ |
Проверяем данные из столбика expires и понимаем, что из за нашего SQL запроса, а именно WHERE expires > UTC_TIMESTAMP() который понимается как «все записи срок которых еще не истёк». SQL функция UTC_TIMESTAMP() возвращает текущую дату с точности до секунды.
Вот и получается, что записи с ID 3 и 4 имеют старый срок годности и они нам не видны на главной странице сайта на golang!
Я не знаю в какой год вы прочтете эту статью так, что я обновлю для вас срок годности записей с ID 1 и 2 до 2030 года. В архиве (папка SQL) будет свежая резервная копия базы данных, вы можете заменить её на свою. Пишу ваш текущий год в комментариях 🙂
Скачать исходный код
В конце каждого урока — мы публикуем архив с исходным кодом урока на текущий момент.
Скачать: snippetbox-21.zip

Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
2024г
2024
2025
2025