Create Resource
Explore how to create products and their variants in a Rust web application using Diesel for database migrations and data insertion. Learn to build normalized tables, manage data relationships, and write test functions to ensure your product creation logic works correctly.
We'll cover the following...
Create a product
We can start creating the migration needed for our project. We can name it however we want. Let’s call it create_products to add the migration necessary to create and drop our table.
Note: After we install and configure Diesel according to the first Appendix, we can write the following command in a terminal window:
The previous command will create two files named up.sql and down.sql inside the migrations folder.
We’ll enter the below code in the up.sql file.
Let’s work with the down.sql file now.
Now, we can run our migration to create the products table by running the following command in the terminal:
We add a file called models.rs for our models in the src folder with the products model.
This struct will be our model for inserting data in our database. Therefore, we need it to be Insertable, We also need to give it the name of our table.
We’re now prepared to add the code corresponding to creating a products table.
We are now prepared to add the code corresponding to creating products.
The insert_into function needs a target. In this case, the target is products, which comes from the DSL module from the products schema. We can use the values method to insert the data. It can be a single value or several. For our use case, we need a single product that will be used in the future endpoint.
This function will return the number of rows inserted if it is successful. Otherwise, it will return an error.
We’re now ready to write some tests and verify if our code works as expected.
In the above code, we created a function, create_product_test, to test our product creation. The annotation #[test] tells us it’s a test function.
We’re using a function called establish_connection_test to connect to our test database. Here, we can define another function called test_transaction, which is very useful because it doesn’t commit to the database. This allows us to have a clean database every time we run our tests.
We verify that one insert was created successfully.
Create Variants
We likely need associations in our project—for example, shoes can come in different sizes, styles, and so on.
We can add all the shoe features in the same table, but that would be a massive table with many fields, and it wouldn’t be normalized.
We need to create another table to save shoe variants.
Below is how the up.sql file will look.
Below is how the down.sql file.
We did not include indexes in the above code widgets, but you should do so according to your frequent queries. Next, we run the migrations.
In the below code, we add the models we need to create new variants.
We might need additional models that have a different purpose and that aren’t connected to a table for our business logic.
Next, we’re going to write the code needed to create variants related to our products.
In the above code, we use the macro no_arg_sql_function!, which allows us to use an SQL function in our code. In this case, SQLite does not have RETURNING for our inserts implemented. Therefore, we need a function called last_insert_rowid to get the last id inserted.
Note: We must be careful to use this function in a transaction.
We create a function called create_product that requires an argument of the NewCompleteProduct type. This function will contain the data we need to create a product and its variants.
In the code, we make a loop over the new variants and filter them by name to check if the variant was already created. We do this to avoid duplications and create it if necessary.
Finally, we create the relationships needed for the products and variants.
Next, we need to create a unit test to verify if the code is working as expected.
The function list_products is created to list the last products with their variants.