Gorm Association Delete does not remove rows, instead update rows - postgresql

A client has many Roles. I want to delete all Roles once a client is deleted.
type Client struct {
Id string `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
Roles [] Role
}
type Role struct {
Id uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
ClientID string
}
return db.Transaction(func(tx *gorm.DB) error {
err = db.Model(&clientToRemove).Association("Roles").Delete(&clientToRemove.Roles)
if err != nil {
return err
}
err = db.Delete(&clientToRemove).Error
if err != nil {
return err
}
return nil
})
I expect related rows in role to be removed, instead of delete query, it executes an update query to remove client_id.
[210.834ms] [rows:1] UPDATE "role" SET "client_id"=NULL WHERE "role"."client_id" = 'xxxxxxxxxxx' AND "role"."id" = 9
How to completely remove rows in associated role table?
Database is Postgres

As stated in the documentation, the delete with association operation will just remove the references between Client and TenantRole. In your case, it just updated the TenantRole records to set the client_id to NULL.
If you want to delete the objects as well, you can try using Select with the delete operation. Please note that this only works if the primary key is not zero, so your query might look something like this:
err = db.Select("TenantRoles").Delete(&Client{Id: clientId}).Error
or just use clientToRemove if it already has the Id field populated
err = db.Select("TenantRoles").Delete(&clientToRemove).Error

Related

GORM unable to update data in one to many relationship

I have two tables users and documents. They are related in such a way that each document must belong to a user using a one to many relationship. When I try updating a document I get the following error
ERROR: insert or update on table "documents" violates foreign key
constraint "fk_users_documents" (SQLSTATE 23503)
Here are my structs definition and update function
type User struct {
gorm.Model
Name string
Email string
Password string
Documents []Document
}
type Document struct {
gorm.Model
Name string
UserID uint
}
//Update document by id
func (h handler)UpdateDocument(w http.ResponseWriter, r *http.Request) {
// once again, we will need to parse the path parameters
var updatedDoc Document
reqBody, _ := ioutil.ReadAll(r.Body)
json.Unmarshal(reqBody, &updatedDoc)
var document Document
vars := mux.Vars(r)
id := vars["id"]
if result := Db.First(&updatedDoc, id); result.Error != nil {
fmt.Println(result.Error)
}
document.Name=updatedDoc.Name
Db.Save(&document)
json.NewEncoder(w).Encode(&updatedDoc)
}
You are calling Db.Save(&document) but document has only its Name field populated. This means that the UserID is set to 0. I'm guessing you don't have any user with ID 0 present in the User table, therefore this violates the foreign key constraint.
The UserID field shall always be set to an existing user when updating a document otherwise the query will fail.
Regardless of this, I'd suggest you to study a bit of database and golang basics because the code you posted is quite messy.

What is the best way to update `HAS ONE` using gorm?

I have two models
type User {
ID uint `gorm:"primarykey" json:"-"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email"`
Profile Profile `gorm:"constraint:OnDelete:CASCADE;"`
}
and
type Profile struct {
ID uint `gorm:"primarykey" json:"-"`
UserID uint `gorm:"uniqueIndex:idx_uniqueProfile" json:"-"`
PhoneNumber string `json:"phoneNumber"`
}
Assuming i have json data to update it like this
data := schema.UserUpdate{
FirstName: "ABC", LastName: "XYZ",
Profile: schema.Profile{PhoneNumber: "123445666"}}
and I update user like this
var user authmodels.User
// get user object to be updated
if err := database.Db.Joins("Profile").First(&user, "uid = ?", uid).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return c.Status(fiber.StatusNotFound).JSON(er.NotFound(""))
}
return c.Status(fiber.StatusBadRequest).JSON(er.BadRequest(err.Error()))
}
// data used to update user object
var updateData = authmodels.User{FirstName: data.FirstName, LastName: data.LastName,
Profile: authmodels.Profile{PhoneNumber: data.Profile.PhoneNumber}}
// update user object and it's profile as well
if err := database.Db.Model(&user).Updates(updateData).Error; err != nil {
return c.Status(fiber.StatusBadRequest).JSON(er.BadRequest(err.Error()))
}
Output results
User Model it only update selected fields (OK)
UPDATE "users" SET "updated_at"='2022-07-07 00:03:18.57',"first_name"='Fiber',"last_name"='Go lang' WHERE "id" = 11
Profile Model it insert instead of updating and it uses original data instead of new data(phoneNumber)
INSERT INTO "profiles" ("created_at","updated_at","user_id","phone_number","id") VALUES ('2022-07-06 23:58:25.61','2022-07-06 23:58:25.61',11,'255765889960',15) ON CONFLICT ("id") DO UPDATE SET "user_id"="excluded"."user_id" RETURNING "id"
You have to set FullSaveAssociations to true if you want to update associations data. Check it out: https://gorm.io/docs/session.html
Therefore your update query shall look like:
err := database.Db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&updateData).Error
And make sure to specify the User and Profile IDs in updateData or through a WHERE clause.

ObjectID automatically set to "0...0" in go with official mongoDB driver

I'm trying to save user entries in a MongoDB database with Go. Users should get an ID automatically. I'm using the offical MongoDB Go driver.
My sources were especially https://vkt.sh/go-mongodb-driver-cookbook/ and https://www.mongodb.com/blog/post/mongodb-go-driver-tutorial.
Struct looks like this:
type User struct {
ID primitive.ObjectID `json:"_id" bson:"_id"`
Fname string `json:"fname" bson:"fname"`
Lname string `json:"lname" bson:"lname"`
Mail string `json:"mail" bson:"mail"`
Password string `json:"password" bson:"password"`
Street string `json:"street" bson:"street"`
Zip string `json:"zip" bson:"zip"`
City string `json:"city" bson:"city"`
Country string `json:"country" bson:"country"`
}
Setting up the database (connection works) and signing up users (based on an HTTP-Request r with a user in it's body):
ctx := context.Background()
uriDB := "someURI"
clientOptions := options.Client().ApplyURI(uriDB)
client, err := mongo.Connect(ctx, clientOptions)
collection := client.Database("guDB").Collection("users")
...
var user User
err := json.NewDecoder(r.Body).Decode(&user)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := collection.InsertOne(ctx, user)
...
When I enter the first user, it is added to the collection, but the ID looks like this:
_id:ObjectID(000000000000000000000000)
If I now want to enter another user, I get the following error:
multiple write errors: [{write errors: [{E11000 duplicate key error collection: guDB.users index: _id_ dup key: { : ObjectId('000000000000000000000000') }}]}, {<nil>}]
So it seems like again ObjectID 000000000000000000000000 is assigned.
I expected the ID to be automatically set to a unique value for each entry.
Do I have to manually set the ID or how can I assign unique IDs to users?
Per the documentation you linked, you must set the object ID yourself when using structs:
_, err := col.InsertOne(ctx, &Post{
ID: primitive.NewObjectID(), // <-- this line right here
Title: "post",
Tags: []string{"mongodb"},
Body: `blog post`,
CreatedAt: time.Now(),
})
The examples before that using bson.M don't need to specify an ID because they don't send the _id field at all; with a struct, the field is being sent with its zero value (as you've seen).
If you set a document _id, mongodb will use that _id for the document during insertion and will not generate. You have to either omit it, or set it manually with primitive.NewObjectID().

Sqlx Get with prepared statements

I am trying to fetch some data from postgress table using prepared statements
If I try with database.Get() everything is returned.
Table:
create table accounts
(
id bigserial not null
constraint accounts_pkey
primary key,
identificator text not null,
password text not null,
salt text not null,
type smallint not null,
level smallint not null,
created_at timestamp not null,
updated timestamp not null,
expiry_date timestamp,
qr_key text
);
Account struct:
type Account struct {
ID string `db:"id"`
Identificator string `db:"identificator"`
Password string `db:"password"`
Salt string `db:"salt"`
Type int `db:"type"`
Level int `db:"level"`
ExpiryDate time.Time `db:"expiry_date"`
CreatedAt time.Time `db:"created_at"`
UpdateAt time.Time `db:"updated_at"`
QrKey sql.NullString `db:"qr_key"`
}
BTW i tried using ? instead of $1 & $2
stmt, err := database.Preparex(`SELECT * FROM accounts where identificator = $1 and type = $2`)
if err != nil {
panic(err)
}
accounts := []account.Account{}
err = stmt.Get(&accounts, "asd", 123)
if err != nil {
panic(err)
}
The error I get is
"errorMessage": "scannable dest type slice with \u003e1 columns (10) in result",
In the table there are no records I tried to remove all fields except the ID from Account (struct), however it does not work.
Documentation for sqlx described Get and Select as:
Get and Select use rows.Scan on scannable types and rows.StructScan on
non-scannable types. They are roughly analagous to QueryRow and Query,
where Get is useful for fetching a single result and scanning it, and
Select is useful for fetching a slice of results:
For fetching a single record use Get.
stmt, err := database.Preparex(`SELECT * FROM accounts where identificator = $1 and type = $2`)
var account Account
err = stmt.Get(&account, "asd", 123)
If your query returns more than a single record use Select with statement as:
stmt, err := database.Preparex(`SELECT * FROM accounts where identificator = $1 and type = $2`)
var accounts []Account
err = stmt.Select(&accounts, "asd", 123)
In your case if you use stmt.Select instead if stmt.Get. It will work.

Why does mgo not return the ID of inserted document?

According to the documentation (http://godoc.org/launchpad.net/mgo/v2) you can obtain the ID of your "Upserted" document if you use the Upsert method.
There is also an Insert method that does not provide this functionality.
Why is that? What if I want to perform an Insert instead of an Upsert? (or wouldn't ever be any valid reason to want to do that? I'm starting to wonder.)
You use bson.NewObjectId() to generate an ID to be inserted.
This is how you'd insert a new document:
i := bson.NewObjectId()
c.Insert(bson.M{"_id": i, "foo": "bar"})
Since you don't know if you're going to insert or update when you issue an Upsert, it would be superfluous to generate an ID just to drop it right after the query (in case an update happens). That's why it's generated db-side and returned to you when applicable.
This should not happen at all, the mgo should insert and return the Id, since, if we generated the ObjectId from the application itself, If the application is restarted, the Object Id generator will start from the beginning generating the same IDs again and again, thus updating existing records in the database.
That is wrong, MGO should rely on the database in generating those IDs and updating the object or returning the objectId of the inserted object immediately like what other languages binding to MongoDB does like in Python or Java.
You can always try the Upsert function to get the generated ID.
db := service.ConnectDb()
sessionCopy := db.Copy()
defer sessionCopy.Close() // clean up
collection := sessionCopy.DB(service.MongoDB.DTB).C(MessageCol.tbl)
log.Println("before to write: ", msg)
// Update record inserts and creates an ID if wasn't set (Returns created record with new Id)
info, err := collection.Upsert(nil, msg)
if err != nil {
log.Println("Error write message upsert collection: ", err)
return MessageMgo{}, err
}
if info.UpsertedId != nil {
msg.Id = info.UpsertedId.(bson.ObjectId)
}
// gets room from mongo
room, err := GetRoom(msg.Rid)
if err != nil {
return msg, err
}
// increments the msgcount and update it
room.MsgCount = room.MsgCount + 1
err = UpdateRoom(room)
if err != nil {
return msg, err
}
return msg, err
This is a sample code I have and works fine.....