Create your first model
In this tutorial you will learn how to create a model which consist of properties that can be stored in a database. It is sometimes referred to as an entity or a repository. Regardless of naming convention, it is essentially a table in a database.
A model can have relationships to other models. It can and should contain all business logic. Let's get started and create our first model.
Create a new model
In our phonebook project, we will need a way to store and retrieve contacts.
Potentially search through them, and even sort them.
To do this, we will create a new model called Contact
.
It's as simple as creating a new file named contact.go
in the models
directory.
Within that file, we will define the Contact
struct and it's properties as if we're describingh a new table in a database.
Contact
package models
type Contact struct {
ModelBase
FirstName string `gorm:"size:255"`
LastName string `gorm:"size:255"`
Nickname string `gorm:"size:255"`
Email string `gorm:"size:255"`
Phone string `gorm:"size:255"`
}
Let's go throught the above code and unpack what we have done.
- We have defined a new struct called
Contact
.
This corresponds to a table in our database namedcontacts
(automatic plural of contact). - We have added the
ModelBase
struct to theContact
struct.
This is a helper struct that contains the fieldsID
,CreatedAt
andUpdatedAt
properties.
Those fields are automatically populated and updated by Gorm.
For IDs, we use the uuid
package to generate a UUID.
- We have added the
FirstName
,LastName
,Email
andPhone
fields to theContact
struct.
These fields correspond to the columns in our database table.
The type of each field isstring
which means aVARCHAR
column in our database.
The double ticks after the type is Go's way of having tags and annotations for each property.
The gorm:""
tag is used to specify the column type, column name, size, etc. More on that here.
Leaving the gorm
tag empty would mean a VARCHAR(255)
, however it's a good practice to be specific.
Now that we've defined the Contact
struct, we've locked in how our application will view that table in the database.
If that table changes somehow, it won't affect our application for as long as those columns exist.
Next up, we need to create a way to interact with that table.
Saving a new contact
We need a save method to save a new contact to the database. Here's how that looks like:
It's very straight forward. We use the db
object to create a new record.
The db
object holds the database connection.
We pass in the model
to the db.Model
function that helps gorm identify the table.
Then we pass in the &model
itself to the Create
function that creates the new record.
Next we'll look at how we can retrieve our contacts.
Contact
...
func (model *Contact) Save() error {
return db.Model(model).Create(&model).Error
}
Retrieving contacts
To retrieve our contacts, we need to create a method that queries the database. Here's how that looks like:
The FindAll
method is expected to return two named values.
The first is a slice of contacts named ms
.
The second is an error named err
.
We use the db
object which holds the connection to the database.
We pass in the model
to the db.Model
function that helps gorm identify the table.
Then we pass in the &ms
variable to the Find
function that retrieves all records within our database table and assigns them to the ms
variable.
The Find
function is a Gorm function that retrieves all records from the database.
It's a shortcut for SELECT * FROM contacts
.
Also, you're not required to manually map each column to a property in your struct.
Gorm does that for you automatically.
Contact
...
func (model *Contact) FindAll() (ms []*Contact, err error) {
result := db.Model(model).Find(&ms)
return ms, result.Error
}
Model accessibility
You're most likely to access your models from your API handlers or controllers.
Hence, it's a good idea to create an easy way to gain access our Contact
struct methods very quickly with little code.
To do this, you can create a new function that returns a pointer to your model.
What it returns is a pointer to an empty Contact
struct.
Placing this function at the very top of the file (before the Contact
struct definition) is a good idea because:
- It's unlikely to ever change.
- It's easy to access when you need to remember the model name.
- It's good to maintain a standard throughout your application code.
Contact
var contact *Contact = &Contact{}
func ContactModel() *Contact {
return contact
}
...
Complete code
Contact
package models
var contact *Contact = &Contact{}
func ContactModel() *Contact {
return contact
}
type Contact struct {
ModelBase
FirstName string `gorm:"size:255"`
LastName string `gorm:"size:255"`
Nickname string `gorm:"size:255"`
Email string `gorm:"size:255"`
Phone string `gorm:"size:255"`
}
func (model *Contact) Save() error {
return db.Model(model).Create(&model).Error
}
func (model *Contact) FindAll() (ms []*Contact, err error) {
result := db.Model(model).Find(&ms)
return ms, result.Error
}