Today we continue learning about GORM. In this topic, you'll learn about models, which are struct types representing specific database tables. We will cover important aspects of model declaration, including structuring models and defining field types.
Declaring models for existing tables
When working with GORM, you can declare models to represent existing tables in your database. For instance, you may have the employees table in a SQLite database, with the following columns:
| employees | |
|---|---|
id |
INTEGER |
first_name |
TEXT |
last_name |
TEXT |
salary |
INTEGER |
The model that represents this table would be the Employee struct:
type Employee struct {
ID uint
FirstName string
LastName string
Salary uint
}
Each field in the Employee struct maps to a column in the employees table. With this model, GORM knows how to interact with the table and allows you to use methods and functions that simplify querying employee table.
Declaring models from scratch
Apart from declaring models for existing tables, GORM also allows you to create new models you plan to add to your database schema.
Declaring a new model is like giving GORM a blueprint of what the new table should look like, making it easier for you to interact with the database.
Suppose you're building a new application and want to generate a database schema from scratch using GORM. Following GORM's conventions, you would declare every new model with the gorm.Model struct. It uses the ID field as a primary key and uses CreatedAt, UpdatedAt, and DeletedAt to track creating/updating and soft deletion time:
type Model struct { // gorm.Model declaration
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
Now, consider a scenario where your new application requires user accounts. You would declare the User model to store user information and embed the gorm.Model struct as an anonymous field like this:
type User struct {
gorm.Model
Name string
Email string
}
If you follow the conventions adopted by GORM to create your model structs, you'll need to write very little configuration code. However, if GORM's default conventions do not match your project requirements, GORM allows you to configure them.
Model field types
GORM makes it easy to work with databases by automatically matching Go data types to their equivalent database types; this means you can focus on building your application's logic while GORM handles the difference in data representation in Go and Relational databases.
Below are some common Go data types and their corresponding SQLite database types:
| Go data type | SQLite data type |
|---|---|
string |
TEXT |
int, int64, uint, uint64 |
INTEGER |
float32, float64 |
REAL |
time.Time |
DATETIME |
bool |
BOOLEAN |
SQLite lacks native support for these data types, while PostgreSQL supports Array and JSON column types. If you use unsupported data types like slices, structs, or maps in models with SQLite, you might encounter model creation errors. It's essential to know the specific data type support and limitations for the database you're using with GORM so you can design your models effectively.Finally, you can check out GORM's datatypes package to explore more options for handling complex data types.
Field tags
Field tags in GORM models allow you to enforce specific rules and validations on your fields, such as defining primary keys, ensuring uniqueness, adding non-null constraints, specifying column data size or precision, and more. They help maintain data integrity and prevent incorrect data from being stored in the database.
You previously learned about the gorm.Model struct. It has the gorm:"primaryKey" tag to define a primary key, which is the main identifier for the data in the row, and provides a key to access a specific record in the table.
And about the gorm:"index" tag to define an index on the DeletedAt field. Indexes are crucial for optimizing database performance, enabling quicker data retrieval when querying specific columns.
Apart from those two tags, there are other commonly used field tags: gorm:"unique" to ensure that values within a column are unique across all records, and the gorm:"not null" tag to enforce that a column must contain a value and cannot be empty.
For example, to ensure that the Email field in the User model is unique and not empty, you can add these tags to the Email field:
type User struct {
gorm.Model
Name string
Email string `gorm:"unique;not null"`
}
To learn about other field tags and how to use them in your models, refer to the official GORM documentation: Fields Tags.
Conclusion
In this topic, you learned how to declare models for existing tables and create new models from scratch. Understanding the intricacies of model declaration is essential for working effectively with GORM, as it allows you to interact seamlessly with your database.
Now that you've learned the fundamentals of declaring models, you're likely eager to discover more advanced concepts, such as:
- How to relate two models together?
- How to generate a database schema using GORM?
- How to add new models to an existing schema?
Fear not! In future topics, you'll learn how to establish relationships between models to express connections between different models in your applications and run migrations, which allow you to propagate changes you make to the database.
Now, it's time to apply your new knowledge to some theory and coding tasks; let's go!