複合主キー関連のその他のエラーはこちら
TL; DR
複合主キーの field.ID
の引数の順序を逆にすれば解消する
func (StudentCourse) Annotations() []schema.Annotation {
return []schema.Annotation{
// 複合主キー
field.ID("student_id", "course_id"),
}
}
エラーの再現
まずはスキーマを作成します。今回は以下のテーブルを考えます。
-- 講義受講者の成績
CREATE TABLE StudentCourses
(student_id integer NOT NULL.
course_id integer NOT NULL,
grade integer NOT NULL
PRIMARY KEY (student_id, course_id));
-- 学生一覧
CREATE TABLE Students
(id integer NOT NULL,
name varchar(255) NOT NULL
PRIMARY KEY (id));
-- 講義一覧
CREATE TABLE Courses
(id integer NOT NULL,
name varchar(255) NOT NULL
PRIMARY KEY (id));
複合主キーと関連テーブルを以下のように定義します。
// StudentCourse holds the schema definition for the StudentCourse entity.
type StudentCourse struct {
ent.Schema
}
func (StudentCourse) Annotations() []schema.Annotation {
return []schema.Annotation{
// 複合主キーを指定(デフォルトでは `id` というフィールドが主キーとなる)
field.ID("course_id", "student_id"),
}
}
// Fields of the StudentCourse.
func (StudentCourse) Fields() []ent.Field {
return []ent.Field{
field.Int("student_id"),
field.Int("course_id"),
field.Int("grade"),
}
}
// Edges of the StudentCourse.
func (StudentCourse) Edges() []ent.Edge {
return []ent.Edge{
edge.To("student", Student.Type).
Unique().
Required().
Field("student_id"),
edge.To("course", Course.Type).
Unique().
Required().
Field("course_id"),
}
}
// ...
// Fields of the Student.
func (Student) Fields() []ent.Field {
return []ent.Field{
field.Int("id"),
field.String("name"),
}
}
// Edges of the Student.
func (Student) Edges() []ent.Edge {
return []ent.Edge{
edge.To("registered_courses", Course.Type).
Through("student_courses", StudentCourse.Type),
}
}
// ...
// Fields of the Course.
func (Course) Fields() []ent.Field {
return []ent.Field{
field.Int("id"),
field.String("name"),
}
}
// Edges of the Course.
func (Course) Edges() []ent.Edge {
// 逆向きの関連をこちらにも指定
return []ent.Edge{
edge.From("students", Student.Type).
Ref("registered_courses").
Through("student_courses", StudentCourse.Type),
}
}
Edge()
を定義している理由については以下の記事をご覧ください。
この状態でソースコードを自動生成使用とすると以下のエラーが発生します。
$ go generate ./ent
entc/gen: resolving edges: edge schema primary key can only be defined on "id" or ("student_id", "course_id") in the same order
exit status 1
ent/generate.go:3: running "go": exit status 1
対処法
エラーメッセージにもあるように、複合主キーを ("student_id", "course_id")
の順序で定義することで解消します。
func (StudentCourse) Annotations() []schema.Annotation {
return []schema.Annotation{
// 複合主キーを指定(デフォルトでは `id` というフィールドが主キーとなる)
- field.ID("course_id", "student_id"),
+ field.ID("student_id", "course_id"),
}
}
これは、複合主キーとedge schemaを対応付ける際に順序依存で比較を行っているためです。
// Edge schema contains a composite primary key, and it was not resolved in previous iterations.
if ant := fieldAnnotate(edgeT.Annotations); ant != nil && len(ant.ID) > 0 && len(edgeT.EdgeSchema.ID) == 0 {
r1, r2 := e.Rel.Columns[0], e.Rel.Columns[1]
if len(ant.ID) != 2 || ant.ID[0] != r1 || ant.ID[1] != r2 {
return fmt.Errorf(`edge schema primary key can only be defined on "id" or (%q, %q) in the same order`, r1, r2)
}
// ...
}
field.ID
の引数は
-
edge.To
で指定したもの -
edge.From
で指定したもの
の順に指定する必要があります。
そのため、edge.To
で指定した student
に対応する student_id
が先行することで解消されます。
entc/gen/graph.go
// Edges of the StudentCourse.
func (StudentCourse) Edges() []ent.Edge {
return []ent.Edge{
edge.To("student", Student.Type).
Unique().
Required().
Field("student_id"),
edge.To("course", Course.Type).
Unique().
Required().
Field("course_id"),
}
}