I have the following pieces of code:
Interface & function definition:
package helper
import "gopkg.in/mgo.v2/bson"
// Defines an interface for identifiable objects
type Identifiable interface {
GetIdentifier() bson.ObjectId
}
// Checks if a slice contains a given object with a given bson.ObjectId
func IsInSlice(id bson.ObjectId, objects []Identifiable) bool {
for _, single := range objects {
if single.GetIdentifier() == id {
return true
}
}
return false
}
The definition of the user struct which satisfies 'Identifiable':
package models
import (
"github.com/my/project/services"
"gopkg.in/mgo.v2/bson"
)
type User struct {
Id bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"`
Username string `json:"username" bson:"username"`
Email string `json:"email" bson:"email"`
Password string `json:"password" bson:"password"`
Albums []bson.ObjectId `json:"albums,omitempty" bson:"albums,omitempty"`
Friends []bson.ObjectId `json:"friends,omitempty" bson:"friends,omitempty"`
}
func GetUserById(id string) (err error, user *User) {
dbConnection, dbCollection := services.GetDbConnection("users")
defer dbConnection.Close()
err = dbCollection.Find(bson.M{"_id": bson.ObjectIdHex(id)}).One(&user)
return err, user
}
func (u *User) GetIdentifier() bson.ObjectId {
return u.Id
}
A test which checks for the existence of an object inside a slice:
package controllers
import (
"github.com/my/project/helper"
"gopkg.in/mgo.v2/bson"
)
var testerId = bson.NewObjectId()
var users = []models.Users{}
/* Some code to get users from db */
if !helper.IsInSlice(testerId, users) {
t.Fatalf("User is not saved in the database")
}
When I try to compile the test it I get the error: undefined helper.IsInSlice. When I rewrite the IsInSlice method to not take []Identifiable but []models.User it works fine.
Any ideas?
Your problem is that you're trying to use a value of type []models.Users{} as a value of type []Identifiable. While models.Users implements Identifiable, Go's type system is designed so that slices of values implementing an interface cannot be used as (or converted to) slices of the interface type.
See the Go specification's section on conversions for more details.
Apparently, Go did not rebuild my package and was looking for the function in an old build. It was therefore undefined. Performing rm -fr [GO-ROOT]/pkg/github.com/my/project/models did the trick.
Related
import (
"gorm.io/gorm"
"gorm.io/driver/postgres"
)
type School struct {
gorm.Model
Students []Student `json:"students"`
}
type Student struct {
gorm.Model
Name string `json:"name"`
}
func init() {
//connect to db first
conn, err := gorm.Open(postgres.New(postgres.Config{
DSN: dbUri,
PreferSimpleProtocol: true,
}), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
db = conn
db.AutoMigrate(&Student{}, &School{})
}
Create the structs and automigrating it gives me an error. Do you know why this is? Also how to you work with has many relation in gorm, what kind of data does it create in postgres?
Error -
Need to define a valid foreign key for relations or it need to implement the Valuer/Scanner interface
You need to add a SchoolID field to your Student. See the docs here for full usage.
type Student struct {
gorm.Model
SchoolID uint
Name string `json:"name"`
}
To answer the second part, it will create two tables for you. Schools and students. The students will have a foreign key that points to the ID of a school. I would read the docs to learn how this works more.
In a db package, I don't want to expose MongoDB's primitive.ObjectID type as part of its public API. Instead, I want to define type Id interface{} within the db package and expose that. Since the driver doesn't know how to transform the database's ObjectID type into my custom ID type, I registered a type decoder like so:
type Id interface{}
var idType = reflect.TypeOf((*Id)(nil)).Elem()
type User struct {
Id `bson:"_id,omitempty"`
}
func main() {
reg := bson.NewRegistryBuilder().
RegisterTypeDecoder(idType, bsoncodec.ValueDecoderFunc(idDecodeValue)).
Build()
client, err := mongo.Connect(context.TODO(), options.Client().
ApplyURI("...").
SetRegistry(reg))
coll := client.Database("...").Collection("...")
var u0 User
coll.InsertOne(context.TODO(), u0)
var u1 User
coll.FindOne(context.TODO(), bson.D{}).Decode(&u1)
}
func idDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.IsValid() || val.Type() != idType {
return bsoncodec.ValueDecoderError{
Name: "IdDecodeValue",
Types: []reflect.Type{idType},
Received: val,
}
}
oid, _ := vr.ReadObjectID()
val.Set(reflect.ValueOf(oid))
return nil
}
I saw that there exists a bsoncodec.NewEmptyInterfaceCodec() to use in place of idDecodeValue, but that only seems to work when User.Id's type is exactly interface{}. I wrote idDecodeValue based off of the existing codecs, but I'm not entirely sure what's going on (mostly due to not knowing when val.IsValid() would ever return false). Is all of this the best, most idiomatic way to go about supporting a custom ID type?
I'm new to GO language.
Trying to learn GO by building real web application.
I'm using revel framework.
And here is my resource routes:
GET /resource/:resource Resource.ReadAll
GET /resource/:resource/:id Resource.Read
POST /resource/:resource Resource.Create
PUT /resource/:resource/:id Resource.Update
DELETE /resource/:resource/:id Resource.Delete
for example:
GET /resource/users calls Resource.ReadAll("users")
And this is my Resource controller (it's just a dummy actions for now):
type Resource struct {
*revel.Controller
}
type User struct {
Id int
Username string
Password string
}
type Users struct {}
func (u Users) All() string {
return "All"
}
func (c Resource) ReadAll(resource string) revel.Result {
fmt.Printf("GET %s", resource)
model := reflect.New(resource)
fmt.Println(model.All())
return nil
}
I'm trying get instance of Users struct by converting resource string to object to call All function.
and the error:
cannot use resource (type string) as type reflect.Type in argument to
reflect.New: string does not implement reflect.Type (missing Align
method)
I'm new to GO please don't judge me :)
Your problem is here:
model := reflect.New(resource)
You can't instantiate a type from a string that way. You need to either use a switch there and do stuff depending on the model:
switch resource {
case "users":
model := &Users{}
fmt.Println(model.All())
case "posts":
// ...
}
Or use reflect correctly. Something like:
var types = map[string]reflect.Type{
"users": reflect.TypeOf(Users{}) // Or &Users{}.
}
// ...
model := reflect.New(types[resource])
res := model.MethodByName("All").Call(nil)
fmt.Println(res)
I get an array of all the users with an attribute ID in their document:
Users := []backend.User{}
err := Collection.Find(bson.M{"channel_id": bson.ObjectIdHex(chId)}).All(&Users)
if err != nil {
println(err)
}
Which I want to send as a JSON response back to the browser/client. However, the User struct contains things like IDs and Hahsed Passwords which i don't want to send back!
I was looking at something like using the reflect package to select the fields of the struct and then putting them into a map[string]interface{} but im not sure how to do it with an array of users.
You can ignore struct fields while json.Marshal.
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Id int `json:"-"`
Name string `json:"name"`
}
type Users []*User
func main() {
user := &Users{
&User{1, "Max"},
&User{2, "Alice"},
&User{3, "Dan"},
}
json, _ := json.Marshal(user)
fmt.Println(string(json))
}
Runnable example in Play Golang: http://play.golang.org/p/AEC_TyXE3B
There is a very useful part about using the tags in the doc. Same for XML, but it's more complicated for obvious reasons.
In Go, you can marshall a struct to XML, e.g.:
package main
import (
"encoding/xml"
"fmt"
)
type person struct {
Name string
Starsign string
}
func main() {
p := &person{"John Smith", "Capricorn"}
b,_ := xml.MarshalIndent(p,""," ")
fmt.Println(string(b))
}
produces output:
<person>
<Name>John Smith</Name>
<Starsign>Capricorn</Starsign>
</person>
My problem is, the person type is lower-case "p" because I want that to be private to the package. But I'd prefer the XML element to be uppercase: <Person>. The fields within the struct can be marshalled to other names using tags (e.g. `xml:"name"`) against the structure fields but this doesn't seem to be an option for the structure type.
I have a work-around using templates, but it would be nice to know a better answer.
According to the encoding/xml.Marshal documentation:
The name for the XML elements is taken from, in order of preference:
the tag on the XMLName field, if the data is a struct
the value of the XMLName field of type xml.Name
the tag of the struct field used to obtain the data
the name of the struct field used to obtain the data
the name of the marshalled type
You can use a tag on the XMLName field in the struct to override the person struct's XML tag name. In order to avoid putting it in your actual person struct, you can create an anonymous struct that embeds the person struct you are marshaling.
package main
import (
"encoding/xml"
"fmt"
)
type person struct {
Name string
Starsign string
}
func marshalPerson(p person) ([]byte, error) {
tmp := struct {
person
XMLName struct{} `xml:"Person"`
}{person: p}
return xml.MarshalIndent(tmp, "", " ")
}
func main() {
p := person{"John Smith", "Capricorn"}
b, _ := marshalPerson(p)
fmt.Println(string(b))
}
This also works, though I don't think it's particularly pretty.
However, this worked in a lot more straight forward manner for me than the other accepted solution from 5 years ago.
package main
import (
"encoding/xml"
"fmt"
)
type person struct {
XMLName xml.Name
Name string
Starsign string
}
func main() {
p := &person{xml.Name{Local: "Person"}, "John Smith", "Capricorn"}
b,_ := xml.MarshalIndent(p,""," ")
fmt.Println(string(b))
}
I think the easiest thing is just to add a dummy field to the person struct with the XML tag.
A struct{} element does not use any storage, I checked with unsafe.Sizeof().
package main
import (
"encoding/xml"
"fmt"
)
type person struct {
Name string
Starsign string
XMLName struct{} `xml:"Person"`
}
func main() {
p := &person{Name: "John Smith", Starsign: "Capricorn"}
b, _ := xml.MarshalIndent(p, "", " ")
fmt.Println(string(b))
}
go playground
If you prefer to initialize the struct without using field names, it is necessary to add an item to initialize the empty struct, like this:
p := &person{"John Smith", "Capricorn", struct{}{}}