@@ -470,15 +470,20 @@ func (m *Model) CtorRetVars() string {
470
470
return strings .Join (ret , ", " )
471
471
}
472
472
473
- // SetFields sets all the children fields and their model to the current model.
473
+ // SetFields sets all the children fields and their model to the current
474
+ // model.
474
475
// It also finds the primary key and sets it in the model.
475
476
// It will return an error if more than one primary key is found.
477
+ // SetFields always sets the primary key as the first field of the model.
478
+ // So, all models can expect to have the primary key in the position 0 of
479
+ // their field slice. This is because the Store will expect the ID in that
480
+ // position.
476
481
func (m * Model ) SetFields (fields []* Field ) error {
477
482
var fs []* Field
478
483
var id * Field
479
- for _ , f := range fields {
484
+ for _ , f := range flattenFields ( fields ) {
480
485
f .Model = m
481
- if f .IsPrimaryKey () {
486
+ if f .IsPrimaryKey () && f . Type != BaseModel {
482
487
if id != nil {
483
488
return fmt .Errorf (
484
489
"kallax: found more than one primary key in model %s: %s and %s" ,
@@ -489,14 +494,56 @@ func (m *Model) SetFields(fields []*Field) error {
489
494
}
490
495
491
496
id = f
492
- m .ID = f
497
+ } else if f .IsPrimaryKey () {
498
+ if f .primaryKey == "" {
499
+ return fmt .Errorf (
500
+ "kallax: primary key defined in %s has no field name, but it must be specified" ,
501
+ f .Name ,
502
+ )
503
+ }
504
+
505
+ // the pk is defined in the model, we need to collect the model
506
+ // and we'll look for the field afterwards, when we have collected
507
+ // all fields. The model is appended to the field set, though,
508
+ // because it will not act as a primary key.
509
+ id = f
510
+ fs = append (fs , f )
493
511
} else {
494
512
fs = append (fs , f )
495
513
}
496
514
}
497
515
516
+ // if the id is a Model we need to look for the specified field
517
+ if id != nil && id .Type == BaseModel {
518
+ for i , f := range fs {
519
+ if f .columnName == id .primaryKey {
520
+ f .isPrimaryKey = true
521
+ f .isAutoincrement = id .isAutoincrement
522
+ id = f
523
+
524
+ if len (fs )- 1 == i {
525
+ fs = append (fs [:i ])
526
+ } else {
527
+ fs = append (fs [:i ], fs [i + 1 :]... )
528
+ }
529
+ break
530
+ }
531
+ }
532
+
533
+ // If the ID is still a base model, means we did not find the pk
534
+ // field.
535
+ if id .Type == BaseModel {
536
+ return fmt .Errorf (
537
+ "kallax: the primary key was supposed to be %s according to the pk definition in %s, but the field could not be found" ,
538
+ id .primaryKey ,
539
+ id .Name ,
540
+ )
541
+ }
542
+ }
543
+
498
544
if id != nil {
499
545
m .Fields = []* Field {id }
546
+ m .ID = id
500
547
}
501
548
m .Fields = append (m .Fields , fs ... )
502
549
return nil
@@ -556,6 +603,8 @@ func relationshipsOnFields(fields []*Field) []*Field {
556
603
return result
557
604
}
558
605
606
+ // ImplicitFK is a foreign key that is defined on just one side of the
607
+ // relationship and needs to be added on the other side.
559
608
type ImplicitFK struct {
560
609
Name string
561
610
Type string
@@ -590,6 +639,11 @@ type Field struct {
590
639
// A struct is considered embedded if and only if the struct was embedded
591
640
// as defined in Go.
592
641
IsEmbedded bool
642
+
643
+ primaryKey string
644
+ isPrimaryKey bool
645
+ isAutoincrement bool
646
+ columnName string
593
647
}
594
648
595
649
// FieldKind is the kind of a field.
@@ -645,13 +699,49 @@ func (t FieldKind) String() string {
645
699
646
700
// NewField creates a new field with its name, type and struct tag.
647
701
func NewField (n , t string , tag reflect.StructTag ) * Field {
702
+ pkName , autoincr , isPrimaryKey := pkProperties (tag )
703
+
648
704
return & Field {
649
705
Name : n ,
650
706
Type : t ,
651
707
Tag : tag ,
708
+
709
+ primaryKey : pkName ,
710
+ columnName : columnName (n , tag ),
711
+ isPrimaryKey : isPrimaryKey ,
712
+ isAutoincrement : autoincr ,
652
713
}
653
714
}
654
715
716
+ // pkProperties returns the primary key properties from a struct tag.
717
+ // Valid primary key definitions are the following:
718
+ // - pk:"" -> non-autoincr primary key without a field name.
719
+ // - pk:"autoincr" -> autoincr primary key without a field name.
720
+ // - pk:"foobar" -> non-autoincr primary key with a field name.
721
+ // - pk:"foobar,autoincr" -> autoincr primary key with a field name.
722
+ func pkProperties (tag reflect.StructTag ) (name string , autoincr , isPrimaryKey bool ) {
723
+ val , ok := tag .Lookup ("pk" )
724
+ if ! ok {
725
+ return
726
+ }
727
+
728
+ isPrimaryKey = true
729
+ if val == "autoincr" || val == "" {
730
+ if val == "autoincr" {
731
+ autoincr = true
732
+ }
733
+ return
734
+ }
735
+
736
+ parts := strings .Split (val , "," )
737
+ name = parts [0 ]
738
+ if len (parts ) > 1 && parts [1 ] == "autoincr" {
739
+ autoincr = true
740
+ }
741
+
742
+ return
743
+ }
744
+
655
745
// SetFields sets all the children fields and the current field as a parent of
656
746
// the children.
657
747
func (f * Field ) SetFields (sf []* Field ) {
@@ -667,16 +757,20 @@ func (f *Field) SetFields(sf []*Field) {
667
757
// is the field name converted to lower snake case.
668
758
// If the resultant name is a reserved keyword a _ will be prepended to the name.
669
759
func (f * Field ) ColumnName () string {
670
- name := strings .TrimSpace (strings .Split (f .Tag .Get ("kallax" ), "," )[0 ])
671
- if name == "" {
672
- name = toLowerSnakeCase (f .Name )
760
+ return f .columnName
761
+ }
762
+
763
+ func columnName (name string , tag reflect.StructTag ) string {
764
+ n := strings .TrimSpace (strings .Split (tag .Get ("kallax" ), "," )[0 ])
765
+ if n == "" {
766
+ n = toLowerSnakeCase (name )
673
767
}
674
768
675
- if _ , ok := reservedKeywords [strings .ToLower (name )]; ok {
676
- name = "_" + name
769
+ if _ , ok := reservedKeywords [strings .ToLower (n )]; ok {
770
+ n = "_" + n
677
771
}
678
772
679
- return name
773
+ return n
680
774
}
681
775
682
776
// ForeignKey returns the name of the foreign keys as specified in the struct
@@ -699,13 +793,12 @@ func (f *Field) ForeignKey() string {
699
793
700
794
// IsPrimaryKey reports whether the field is the primary key.
701
795
func (f * Field ) IsPrimaryKey () bool {
702
- _ , ok := f .Tag .Lookup ("pk" )
703
- return ok
796
+ return f .isPrimaryKey
704
797
}
705
798
706
799
// IsAutoIncrement reports whether the field is an autoincrementable primary key.
707
800
func (f * Field ) IsAutoIncrement () bool {
708
- return f .Tag . Get ( "pk" ) == "autoincr"
801
+ return f .isAutoincrement
709
802
}
710
803
711
804
// IsInverse returns whether the field is an inverse relationship.
@@ -1003,6 +1096,22 @@ func toLowerSnakeCase(s string) string {
1003
1096
return buf .String ()
1004
1097
}
1005
1098
1099
+ // flattenFields will recursively flatten all fields removing the embedded ones
1100
+ // from the field set.
1101
+ func flattenFields (fields []* Field ) []* Field {
1102
+ var result = make ([]* Field , 0 , len (fields ))
1103
+
1104
+ for _ , f := range fields {
1105
+ if f .IsEmbedded && f .Type != BaseModel {
1106
+ result = append (result , flattenFields (f .Fields )... )
1107
+ } else {
1108
+ result = append (result , f )
1109
+ }
1110
+ }
1111
+
1112
+ return result
1113
+ }
1114
+
1006
1115
// Event is the name of an event.
1007
1116
type Event string
1008
1117
0 commit comments