diff --git a/models/git/lfs_lock.go b/models/git/lfs_lock.go
index 2f65833fe3..07ce7d4abf 100644
--- a/models/git/lfs_lock.go
+++ b/models/git/lfs_lock.go
@@ -6,6 +6,7 @@ package git
 import (
 	"context"
 	"errors"
+	"fmt"
 	"strings"
 	"time"
 
@@ -21,11 +22,12 @@ import (
 
 // LFSLock represents a git lfs lock of repository.
 type LFSLock struct {
-	ID      int64     `xorm:"pk autoincr"`
-	RepoID  int64     `xorm:"INDEX NOT NULL"`
-	OwnerID int64     `xorm:"INDEX NOT NULL"`
-	Path    string    `xorm:"TEXT"`
-	Created time.Time `xorm:"created"`
+	ID      int64            `xorm:"pk autoincr"`
+	RepoID  int64            `xorm:"INDEX NOT NULL"`
+	OwnerID int64            `xorm:"INDEX NOT NULL"`
+	Owner   *user_model.User `xorm:"-"`
+	Path    string           `xorm:"TEXT"`
+	Created time.Time        `xorm:"created"`
 }
 
 func init() {
@@ -37,6 +39,35 @@ func (l *LFSLock) BeforeInsert() {
 	l.Path = util.PathJoinRel(l.Path)
 }
 
+// LoadAttributes loads attributes of the lock.
+func (l *LFSLock) LoadAttributes(ctx context.Context) error {
+	// Load owner
+	if err := l.LoadOwner(ctx); err != nil {
+		return fmt.Errorf("load owner: %w", err)
+	}
+
+	return nil
+}
+
+// LoadOwner loads owner of the lock.
+func (l *LFSLock) LoadOwner(ctx context.Context) error {
+	if l.Owner != nil {
+		return nil
+	}
+
+	owner, err := user_model.GetUserByID(ctx, l.OwnerID)
+	if err != nil {
+		if user_model.IsErrUserNotExist(err) {
+			l.Owner = user_model.NewGhostUser()
+			return nil
+		}
+		return err
+	}
+	l.Owner = owner
+
+	return nil
+}
+
 // CreateLFSLock creates a new lock.
 func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
 	dbCtx, committer, err := db.TxContext(ctx)
@@ -94,7 +125,7 @@ func GetLFSLockByID(ctx context.Context, id int64) (*LFSLock, error) {
 }
 
 // GetLFSLockByRepoID returns a list of locks of repository.
-func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) ([]*LFSLock, error) {
+func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (LFSLockList, error) {
 	e := db.GetEngine(ctx)
 	if page >= 0 && pageSize > 0 {
 		start := 0
@@ -103,7 +134,7 @@ func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (
 		}
 		e.Limit(pageSize, start)
 	}
-	lfsLocks := make([]*LFSLock, 0, pageSize)
+	lfsLocks := make(LFSLockList, 0, pageSize)
 	return lfsLocks, e.Find(&lfsLocks, &LFSLock{RepoID: repoID})
 }
 
diff --git a/models/git/lfs_lock_list.go b/models/git/lfs_lock_list.go
new file mode 100644
index 0000000000..cab1e61cab
--- /dev/null
+++ b/models/git/lfs_lock_list.go
@@ -0,0 +1,54 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+	"context"
+	"fmt"
+
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/container"
+)
+
+// LFSLockList is a list of LFSLock
+type LFSLockList []*LFSLock
+
+// LoadAttributes loads the attributes for the given locks
+func (locks LFSLockList) LoadAttributes(ctx context.Context) error {
+	if len(locks) == 0 {
+		return nil
+	}
+
+	if err := locks.LoadOwner(ctx); err != nil {
+		return fmt.Errorf("load owner: %w", err)
+	}
+
+	return nil
+}
+
+// LoadOwner loads the owner of the locks
+func (locks LFSLockList) LoadOwner(ctx context.Context) error {
+	if len(locks) == 0 {
+		return nil
+	}
+
+	usersIDs := container.FilterSlice(locks, func(lock *LFSLock) (int64, bool) {
+		return lock.OwnerID, true
+	})
+	users := make(map[int64]*user_model.User, len(usersIDs))
+	if err := db.GetEngine(ctx).
+		In("id", usersIDs).
+		Find(&users); err != nil {
+		return fmt.Errorf("find users: %w", err)
+	}
+	for _, v := range locks {
+		v.Owner = users[v.OwnerID]
+		if v.Owner == nil { // not exist
+			v.Owner = user_model.NewGhostUser()
+		}
+	}
+
+	return nil
+}
diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go
index c18cb6a8c8..7e3634375a 100644
--- a/routers/web/repo/setting/lfs.go
+++ b/routers/web/repo/setting/lfs.go
@@ -95,6 +95,11 @@ func LFSLocks(ctx *context.Context) {
 		ctx.ServerError("LFSLocks", err)
 		return
 	}
+	if err := lfsLocks.LoadAttributes(ctx); err != nil {
+		ctx.ServerError("LFSLocks", err)
+		return
+	}
+
 	ctx.Data["LFSLocks"] = lfsLocks
 
 	if len(lfsLocks) == 0 {
diff --git a/templates/repo/settings/lfs_locks.tmpl b/templates/repo/settings/lfs_locks.tmpl
index 98f0e49097..9a18f525e9 100644
--- a/templates/repo/settings/lfs_locks.tmpl
+++ b/templates/repo/settings/lfs_locks.tmpl
@@ -30,9 +30,9 @@
 								{{end}}
 							</td>
 							<td>
-								<a href="{{$.Owner.HomeLink}}">
-									{{ctx.AvatarUtils.Avatar $.Owner}}
-									{{$.Owner.DisplayName}}
+								<a href="{{$lock.Owner.HomeLink}}">
+									{{ctx.AvatarUtils.Avatar $lock.Owner}}
+									{{$lock.Owner.DisplayName}}
 								</a>
 							</td>
 							<td>{{TimeSince .Created ctx.Locale}}</td>
diff --git a/tests/integration/lfs_view_test.go b/tests/integration/lfs_view_test.go
index 8817986b79..791eeac877 100644
--- a/tests/integration/lfs_view_test.go
+++ b/tests/integration/lfs_view_test.go
@@ -4,12 +4,21 @@
 package integration
 
 import (
+	"context"
+	"fmt"
 	"net/http"
+	"strings"
 	"testing"
 
+	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/lfs"
+	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 // check that files stored in LFS render properly in the web UI
@@ -103,3 +112,56 @@ func TestLFSRender(t *testing.T) {
 		assert.Equal(t, 1, doc.Find(`.sha.label[href="/user2/lfs/commit/73cf03db6ece34e12bf91e8853dc58f678f2f82d"]`).Length(), "could not find link to commit")
 	})
 }
+
+// TestLFSLockView tests the LFS lock view on settings page of repositories
+func TestLFSLockView(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})       // in org 3
+	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // own by org 3
+	session := loginUser(t, user2.Name)
+
+	// create a lock
+	lockPath := "test_lfs_lock_view.zip"
+	lockID := ""
+	{
+		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks", repo3.FullName()), map[string]string{"path": lockPath})
+		req.Header.Set("Accept", lfs.AcceptHeader)
+		req.Header.Set("Content-Type", lfs.MediaType)
+		resp := session.MakeRequest(t, req, http.StatusCreated)
+		lockResp := &api.LFSLockResponse{}
+		DecodeJSON(t, resp, lockResp)
+		lockID = lockResp.Lock.ID
+	}
+	defer func() {
+		// release the lock
+		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/%s/unlock", repo3.FullName(), lockID), map[string]string{})
+		req.Header.Set("Accept", lfs.AcceptHeader)
+		req.Header.Set("Content-Type", lfs.MediaType)
+		session.MakeRequest(t, req, http.StatusOK)
+	}()
+
+	t.Run("owner name", func(t *testing.T) {
+		defer tests.PrintCurrentTest(t)()
+
+		// make sure the display names are different, or the test is meaningless
+		require.NoError(t, repo3.LoadOwner(context.Background()))
+		require.NotEqual(t, user2.DisplayName(), repo3.Owner.DisplayName())
+
+		req := NewRequest(t, "GET", fmt.Sprintf("/%s/settings/lfs/locks", repo3.FullName()))
+		resp := session.MakeRequest(t, req, http.StatusOK)
+
+		doc := NewHTMLParser(t, resp.Body).doc
+
+		tr := doc.Find("table#lfs-files-locks-table tbody tr")
+		require.Equal(t, 1, tr.Length())
+
+		td := tr.First().Find("td")
+		require.Equal(t, 4, td.Length())
+
+		// path
+		assert.Equal(t, lockPath, strings.TrimSpace(td.Eq(0).Text()))
+		// owner name
+		assert.Equal(t, user2.DisplayName(), strings.TrimSpace(td.Eq(1).Text()))
+	})
+}