Preloading
When doing a join, conditions can be applied on joined models but, by default, only the information of the main model is returned as a result. To also get the joined models, it is necessary to use the Preload() method.
Examples
Preload a related model
In this example we query all MyModels and preload whose related MyOtherModel.
type MyOtherModel struct {
model.UUIDModel
}
type MyModel struct {
model.UUIDModel
Related MyOtherModel
RelatedID model.UUID
}
myModels, err := cql.Query[MyModel](
gormDB,
conditions.MyModel.Related().Preload(),
).Find()
Nested preloads
type Parent struct {
model.UUIDModel
}
type MyOtherModel struct {
model.UUIDModel
Parent Parent
ParentID model.UUID
}
type MyModel struct {
model.UUIDModel
Related MyOtherModel
RelatedID model.UUID
}
myModels, err := cql.Query[MyModel](
gormDB,
conditions.MyModel.Related(
conditions.MyOtherModel.Parent().Preload(),
),
).Find()
As we can see, it is not necessary to add the preload to all joins, it is enough to do it in the deepest one, to recover, in this example, both Related and Parent.
Relation getters
At the moment, with the PreloadConditions, we can choose whether or not to preload a relation. The problem is that once we get the result of the query, we cannot determine if a null value corresponds to the fact that the relation is really null or that the preload was not performed, which means a big risk of making decisions in our business logic on incomplete information.
For this reason, cql provides the Relation getters. These are methods that will be added to your models to safely navigate a relation, responding cql.ErrRelationNotLoaded in case you try to navigate a relation that was not loaded from the database. They are created in a file called cql.go in your model package when generating conditions.
Here is an example of its use:
type MyOtherModel struct {
model.UUIDModel
}
type MyModel struct {
model.UUIDModel
Related MyOtherModel
RelatedID model.UUID
}
myModel, err := cql.Query[MyModel](
conditions.MyModel.Related().Preload(),
).FindOne()
if err == nil {
firstRelated, err := myModel.GetRelated()
if err == nil {
// you can safely apply your business logic
} else {
// err is cql.ErrRelationNotLoaded
}
}
Unfortunately, these relation getters cannot be created in all cases but only in those in which:
The relation is made with an object directly instead of a pointer (which is not recommended as described here).
The relation is made with pointers and the foreign key (typically the ID) is in the same model.
The relation is made with a pointer to a list.
Preload collections
Model collections can also be preloaded (relations has many or many to many):
type Seller struct {
model.UUIDModel
Company *Company
CompanyID *model.UUID // Company HasMany Seller (Company 0..1 -> 0..* Seller)
}
type Company struct {
model.UUIDModel
Sellers *[]Seller // Company HasMany Seller (Company 0..1 -> 0..* Seller)
}
company, err := cql.Query[Company](
conditions.Company.Sellers.Preload(),
).FindOne()
if err == nil {
sellers, err := company.GetSellers()
if err == nil {
// you can safely apply your business logic
} else {
// err is cql.ErrRelationNotLoaded
}
}
Nested preloads can also be applied to preload model relationships within the collection:
type Office struct {
model.UUIDModel
Seller *Seller
SellerID *model.UUID `gorm:"not null"` // Seller HasOne Office (Seller 1 -> 1 Office)
}
type Seller struct {
model.UUIDModel
Office *Office // Seller HasOne Office (Seller 1 -> 1 Office)
Company *Company
CompanyID *model.UUID // Company HasMany Seller (Company 0..1 -> 0..* Seller)
}
type Company struct {
model.UUIDModel
Sellers *[]Seller // Company HasMany Seller (Company 0..1 -> 0..* Seller)
}
company, err := cql.Query[Company](
conditions.Company.Sellers.Preload(
conditions.Seller.Office().Preload()
),
).FindOne()
if err == nil {
sellers, err := company.GetSellers()
if err == nil {
for _, seller := range sellers {
office, err := seller.GetOffice()
if err == nil {
// you can safely apply your business logic
} else {
// err is cql.ErrRelationNotLoaded
}
}
} else {
// err is cql.ErrRelationNotLoaded
}
}