Продолжим работу с проектом из прошлой темы и добавим в него возможность редактирования данных.
Редактирование данных, как и добавление, разбивается на две части. Вначале нам надо отобразить пользователю форму для изменения выбранного объекта. Потом нам надо получить отправленные данные и сохранить их в базу данных.
Прежде всего определим форму для редактирования. Для этого в папке templates создадим файл edit.html.
Определим в файле edit.html следующий код:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Edt Product</title> </head> <body> <h3>Edit Product</h3> <form method="POST"> <input type="hidden" name="id" value="{{.Id}}" /> <label>Model</label><br> <input type="text" name="model" value="{{.Model}}" /><br><br> <label>Company</label><br> <input type="text" name="company" value="{{.Company}}" /><br><br> <label>Price</label><br> <input type="number" name="price" value="{{.Price}}" /><br><br> <input type="submit" value="Send" /> </form> </body> </html>
Данный файл представляет шаблон, в который из кода сервера будут передаваться редактируемые данные.
Теперь изменим код сервера, добавив в него возможность редактирования:
package main import ( "fmt" "database/sql" _ "github.com/go-sql-driver/mysql" "net/http" "html/template" "log" "github.com/gorilla/mux" ) type Product struct{ Id int Model string Company string Price int } var database *sql.DB // возвращаем пользователю страницу для редактирования объекта func EditPage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] row := database.QueryRow("select * from productdb.Products where id = ?", id) prod := Product{} err := row.Scan(&prod.Id, &prod.Model, &prod.Company, &prod.Price) if err != nil{ log.Println(err) http.Error(w, http.StatusText(404), http.StatusNotFound) }else{ tmpl, _ := template.ParseFiles("templates/edit.html") tmpl.Execute(w, prod) } } // получаем измененные данные и сохраняем их в БД func EditHandler(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { log.Println(err) } id := r.FormValue("id") model := r.FormValue("model") company := r.FormValue("company") price := r.FormValue("price") _, err = database.Exec("update productdb.Products set model=?, company=?, price = ? where id = ?", model, company, price, id) if err != nil { log.Println(err) } http.Redirect(w, r, "/", 301) } func CreateHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { err := r.ParseForm() if err != nil { log.Println(err) } model := r.FormValue("model") company := r.FormValue("company") price := r.FormValue("price") _, err = database.Exec("insert into productdb.Products (model, company, price) values (?, ?, ?)", model, company, price) if err != nil { log.Println(err) } http.Redirect(w, r, "/", 301) }else{ http.ServeFile(w,r, "templates/create.html") } } func IndexHandler(w http.ResponseWriter, r *http.Request) { rows, err := database.Query("select * from productdb.Products") if err != nil { log.Println(err) } defer rows.Close() products := []Product{} for rows.Next(){ p := Product{} err := rows.Scan(&p.Id, &p.Model, &p.Company, &p.Price) if err != nil{ fmt.Println(err) continue } products = append(products, p) } tmpl, _ := template.ParseFiles("templates/index.html") tmpl.Execute(w, products) } func main() { db, err := sql.Open("mysql", "root:password@/productdb") if err != nil { log.Println(err) } database = db defer db.Close() router := mux.NewRouter() router.HandleFunc("/", IndexHandler) router.HandleFunc("/create", CreateHandler) router.HandleFunc("/edit/{id:[0-9]+}", EditPage).Methods("GET") router.HandleFunc("/edit/{id:[0-9]+}", EditHandler).Methods("POST") http.Handle("/",router) fmt.Println("Server is listening...") http.ListenAndServe(":8181", nil) }
По сравнению с прошлой темой здесь добавлены функции EditPage и EditHandler и изменена функция main.
Чтобы указать, какой объект будет редактироваться, мы будем передавать через адрес id этого объекта. И для упрощения маршрутизации в данном случае мы будем использовать пакет gorilla/mux.
В функции IndexPage мы получаем id объекта, который надо изменить, извлекаем из БД даные этого объекта и передаем их в шаблон edit.html:
func EditPage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] row := database.QueryRow("select * from productdb.Products where id = ?", id) prod := Product{} err := row.Scan(&prod.Id, &prod.Model, &prod.Company, &prod.Price) if err != nil{ log.Println(err) http.Error(w, http.StatusText(404), http.StatusNotFound) }else{ tmpl, _ := template.ParseFiles("templates/edit.html") tmpl.Execute(w, prod) } }
На случай, если в базе данных не окажется объекта с подобным id, с помощью функции http.Error()
возвращаем статусный код 404, который
указывает, что объект не найден.
В функции EditHandler получаем данные из отправленной формы и с их помощью изменяем объект в базе данных по определенному id.
func EditHandler(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { log.Println(err) } id := r.FormValue("id") model := r.FormValue("model") company := r.FormValue("company") price := r.FormValue("price") _, err = database.Exec("update productdb.Products set model=?, company=?, price = ? where id = ?", model, company, price, id) if err != nil { log.Println(err) } http.Redirect(w, r, "/", 301) }
После обновления БД выполняется редирект на главную страницу.
В функции main эти функции EditPage и EditHandler связываются с определенными маршрутами. По сути они привязаны к одному и тому же маршруту, однако для разного типа запросов: EditPage для запросов GET, а EditHandler - для запросов POST.
router.HandleFunc("/edit/{id:[0-9]+}", EditPage).Methods("GET") router.HandleFunc("/edit/{id:[0-9]+}", EditHandler).Methods("POST")
Стоит отметить, что добавление данных, которое представлено в данном случае функцией CreateHandler, также фактически выполняет два действия в зависимости от типа запроса: отображает страницу для добавления и собственно добавляет данные. И в прицнипе организацию добавления можно сделать также, как и редактирование, разделив на две функции для каждого типа запросов.
Для упрощения управлением объектами изменим файл index.html, добавив в него ссылки на редактирование:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Products</title> </head> <body> <h2>Список товаров</h2> <p><a href="/create">Добавить</a></p> <table> <thead><th>Id</th><th>Model</th><th>Company</th><th>Price</th><th></th></thead> {{range . }} <tr> <td>{{.Id}}</td> <td>{{.Model}}</td> <td>{{.Company}}</td> <td>{{.Price}}</td> <td><a href="/edit/{{.Id}}">Изменить</a> </td> </tr> {{end}} </table> </body> </html>
И после запуска приложения мы сможем отредактировать нужные объекты: