.
version: '3'
services:
db:
image: postgres:13
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: testdb
ports:
- "5432:5432"
volumes:
- dbdata:/var/lib/postgresql/data
app:
build: .
ports:
- "8080:8080"
depends_on:
- db
environment:
DB_HOST: db
DB_USER: postgres
DB_PASSWORD: password
DB_NAME: testdb
volumes:
- .:/app
command: ["go", "run", "main.go"]
volumes:
dbdata:
.
FROM golang:1.22-alpine
WORKDIR /app
# Copy the go.mod and go.sum files
COPY go.mod go.sum ./
# Download the Go modules
RUN go mod download
COPY . .
EXPOSE 8080
CMD ["go", "run", "main.go"]
first letter must be Uppercase
package models
import (
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string `json:"name"` // first letter must be Uppercase
Email string `json:"email"`
}
.
package main
import (
"Gin_Gorm_Postgres/models"
"fmt"
"log"
"net/http"
"os"
"github.com/gin-gonic/gin"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
var DB *gorm.DB
func initDB() {
var err error
dbHost := os.Getenv("DB_HOST")
dbUser := os.Getenv("DB_USER")
dbPassword := os.Getenv("DB_PASSWORD")
dbName := os.Getenv("DB_NAME")
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=5432 sslmode=disable", dbHost, dbUser, dbPassword, dbName)
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database: ", err)
}
DB.AutoMigrate(&models.User{})
}
func main() {
router := gin.Default()
initDB()
router.LoadHTMLGlob("templates/*")
router.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
router.GET("/", func(c *gin.Context) {
var users []models.User
DB.Find(&users)
c.HTML(http.StatusOK, "index.html", gin.H{"users": users})
})
router.GET("/users/new", func(c *gin.Context) {
c.HTML(http.StatusOK, "create.html", nil)
})
router.POST("/users", func(c *gin.Context) {
var user models.User
if err := c.ShouldBind(&user); err != nil { // ShouldBindJSON
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if result := DB.Create(&user); result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
return
}
c.JSON(http.StatusOK, user)
// c.Redirect(http.StatusMovedPermanently, "/")
})
router.GET("/users/edit/:id", func(c *gin.Context) {
id := c.Param("id")
var user models.User
if err := DB.First(&user, id).Error; err != nil {
c.String(http.StatusNotFound, "User not found")
return
}
c.HTML(http.StatusOK, "edit.html", gin.H{"user": user})
})
router.POST("/users/update/:id", func(c *gin.Context) {
id := c.Param("id")
var user models.User
if err := DB.First(&user, id).Error; err != nil {
c.String(http.StatusNotFound, "User not found")
return
}
if err := c.ShouldBind(&user); err != nil {
c.HTML(http.StatusBadRequest, "edit.html", gin.H{"error": err.Error(), "user": user})
return
}
DB.Save(&user)
c.Redirect(http.StatusMovedPermanently, "/")
})
router.GET("/users/delete/:id", func(c *gin.Context) {
id := c.Param("id")
var user models.User
if err := DB.Delete(&user, id).Error; err != nil {
c.String(http.StatusNotFound, "User not found")
return
}
c.Redirect(http.StatusMovedPermanently, "/")
})
router.Run(":8080")
}
Uppercase...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Create User</title>
</head>
<body>
<h1>Create New User</h1>
<form action="/users" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="Name"> <!-- Uppercase name -->
<label for="email">Email:</label>
<input type="email" id="email" name="Email"> <!-- Uppercase name -->
<button type="submit">Create</button>
</form>
<a href="/">Back to List</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Users</title>
</head>
<body>
<h1>Users List</h1>
<a href="/users/new">Create New User</a>
<ul>
{{range .users}}
<li>
{{.Name}} ({{.Email}})
<a href="/users/edit/{{.ID}}">Edit</a>
<a href="/users/delete/{{.ID}}">Delete</a>
</li>
{{else}}
<li>No users found</li>
{{end}}
</ul>
</body>
</html>