Building Relationships: The Back End
- Chuck Curtis
- Sep 3, 2019
- 5 min read
Today I want to setup the relationship between the user and the groups. This will only be the first step of the user/group relationship. The 'owner' of the group will be the user that creates the group. The owner will be the only user that's authorized to update or delete the group once it's created. For other users to 'join' a group I'll be setting up a many-to-many relationship, which will be a separate series of posts.
I know that I'm going to use a Rails migration to setup the relationship. In my terminal I run the command:
bin/rails g migration AddUserToGroups user:references
This command adds a reference to the user resource into the groups table. Once I run the command, I open up the new migration file and add the null: false modifier. This addition will make it so a user_id is required when creating a new group (by saying that a group has to have a value in the user_id column).

I want to start testing so I run bin/rails db:migrate in the terminal, but I get an error message! I expected to see this because I have several groups that I created before I established the relationship, and adding the user_id column now would mean that the existing groups will have no value for user_id. I fix this by dropping the db and then re-creating. By doing this I lose all of the previous groups, but that's ok because they were test groups for development purposes. If I was trying to make this kind of update post-deployment, I would found a way to populate the already existing groups.
Now when I run bin/rails db:migrate I can see the migrations being successfully applied in the terminal. Before I can start testing there are a few more changes in Rails that I need to make.
I know that I need to update both the group model and the user model to reflect the new relationship. Since one user can create many groups, I add has_many :groups to the model. I also add dependent: :destroy which will make it so if a user is deleted, all of the groups that they created will also be deleted.
I also know that now, each group will belong to a user. In the group model I add belongs_to :user as well as a a line to validate that the user exists. Adding validates :user, presence: true is an extra step in making sure that every group will have a user associated with it.
At this point, I know there are updates that I need to make in the groups_controller.rb file, but I'm not exactly sure what they are right off the bat, so I start running tests using curl scripts to help me move forward.

I already have a curl script for creating a new group, but this was before I added the relationship with a user. Based on previous experience I know that I need to add the Token to the curl script. I receive the token when I sign in, and I need to send the token back along every time I want to do anything with the user (sign-out, change password). Since the group is now required to have a user, I need to send the token along when I want to create a new group.
I add a new line in the curl script header, and add the variable for the TOKEN. In the terminal, I sign-up and then sign-in and then attempt to create a new group. As you can see, I receive an error 422 Unprocessable Entity that {"user":["must exist"]}.

Right off the bat, I'm not sure what this means. To help try and figure out the next steps, I refer to a previous project that I worked on in Rails, where I had a similar setup: a resource that belongs to a user.
It takes me some time, but after about 15 mins of trial and error I manage to figure out the solution. Under the create function in the groups controller I need to update Group.new(group_params) line to read current_user.groups.build(group_params). The key lies in the association between user and groups that I've created. In Ruby, the code Group.new() creates a new instance of a specific model, in this case the Group model. Since there is an Active Record association between groups and users, I need to use the .build() function instead. Since the group requires a user, I can use the current_user described in the application_controller as part of the auth functionality provided by General Assembly.

I also realize that I need to add the user_id to my group_params, so I go ahead and add this at the bottom of the file. Now I fun my test for creating a new group, and success!

Now that I've confirmed that the create functionality is working, I want to test my other features. One by one, I work through updating a group, editing a group, and deleting a group using curl scripts.
As mentioned at the top of this post, a key feature that I want to include is for anyone visiting the site to be able to view the list of groups. Only signed in users will be able to interact (create, join, update, delete) groups, but any visitor will be able to see the groups in hopes of finding a group they like, and wanting to sign up. To accomplish this, I have the GroupsController inherit from the OpenReadController class. By using OpenReadController, the database will skip the authentication when a read (index/show) request is received.
But now I have another issue. I discover that any user can update or delete any group, whether or not they are the creator of that group. To solve this, I need to make more updates in the groups_controller.rb file. I update the set_group function to set @group using current_user.groups.find(). By doing this, the group can only be set if the id of the current user matches the user_id for that group. If there is not a match, then the user will not be able to update or delete the group.
But this creates another problem. Since the group is not being set from the list of groups of the current user, the show function is not working properly. The user can only show groups that they created, and not all the group. To correct this, I need to make a separate set group function specifically for show, which searches the entire Group resource.
In the first image, I added the :set_group_show function, and moved [:show] from set_group down into ;set_group_show. In the second image, I use the Group resource to find the :id that is being requested. By making this update, only the current_user can update or destroy a group that they created, but they can view any group whether they created it or not.
This completes the backend updates for adding the relationship between groups and users. Next up, I'll be updating the front-end to comply with the back end changes.
Comments