How to save data to multiple tables in Rails - ruby-on-rails

I am using Devise for authentication in my app and got a model named as "Users". I created an another model for Company informations. I generated the migration with references and everything is okay.
I want to add user informations to users table and company informations to companies table in same registration view.
Also both users and companies tables got the same field as "name".
I tried to specify it as ;
<%= f.text_field :company["name"], autocomplete: "company_name" %>
<%= f.text_field :company[:name], autocomplete: "company_name" %>
<%= f.text_field :companies[:name], autocomplete: "company_name" %>
And i got the errors ;
undefined method `' for #<User:0x00007fd5588cb588>
no implicit conversion of Symbol into Integer
I think somehow i should override devise_controller but i want to learn the general practice for these kind of situations.
Sum: I want to save data to different tables which has same column names in database.

Assuming user belongs_to company and you want to save user and their associated company from one form submit, you can use fields_for for this
<%= f.fields_for :company do |cf| %>
<%= cf.text_field :name %>
<% end %>
This will require accepts_nested_attributes_for :company in User.

Related

Adding role selection to devise sign up form, when role name needs to match role_id (using devise)

I'm new to rails (using rails 6), and I have a question that I didn't see answered on the site for my specific use case.
The basic scenario is the same as in many question - adding role based authentication to a rails application when using devise.
The way I implemented this was to create a roles table, and having a one to many relations between it and the users table.
I need only two users: guest and admin, and they have 1, 2 ids, respectively.
I inserted the values into the roles table manually, using the rails console.
Now, I'm having trouble with the sign up form, which is the default one devise gem created:
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
What I want to add is a select field that will show "admin" and "guest", and when the user submit the form, the right role_id will be added to the user record in the user table, but I don't get how to inform the devise view on the role table existence and how to match the string value of the role with the role id the I want the user to accualy have.
So my questions are:
How to add the roles to a select field in the devise sign up form?
How to handle after the user selects one of the roles string names, and match it with role id that will be added to the user when the form is processed?
I saw the following questions and many like them, but I didn't saw any of them dealing with matching string with id before the form is submitted:
Devise Add Admin Role
how to automatically add a role to a user when signing up using devise
Adding Fields To Devise Sign Up Using Rails 4
Multiple models associated with devise user - fill in one model based on selection of role
If there is a question answering my issue, I'm sorry and I would be glad to get the link and close this question.
If there is any more info you need, please let me know.
Thanks!
Using an enum to differentiate admins from guests seems like the best option and will prevent you from having to complicate your model/form with unnecessary things like nested attributes.
Here's an example...
Create a role column on the users table.
$ rails g migration add_role_to_users role:integer
In your migration file, make sure to set the default to 0.
add_column :users, :role, :integer, default: 0
Then, after migrating the db, in your User model add the enum...
class User < ApplicationRecord
enum role: { guest: 0, admin: 1 }
...
end
Adding another role is as simple as adding a key/value to the role enum.
Then, in your devise form, you can add something like...
<%= f.label :role %>
<%= f.select :role, User.roles.keys %>
You will also need to make sure that you are adding role as a permitted param...you seem to be adding it as a field in signup, so in ApplicationController...
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:role])
end
end
If you go with this you will also have to remove your roles table and associations.

undefined method `map' for "MD":String

I'm struggling to create a dynamic dropdown box arrangement. Thanks to StackExchange Ruby community, I was able to create the first dropdown box. What I'd like to do, is have the user select a group of banks in a particular state, but keep getting the following error message from my f.grouped_collection_select form: undefined method `map' for "MD":String. I looked at ActionView::Helpers::Forms and this video, but nothing seems to work. Any help you could provide would be greatly appreciated. I think I also have to add jquery code too? Here is my current view code:
<%= form_for #boli do |f| %>
<%= f.label :state %>
<%= f.collection_select :state, (Boli.order(:state).select("DISTINCT ON (state) id, state")), :id, :state, include_blank: true %>
<div>
<%= f.label :bank %>
<%= f.grouped_collection_select :bank, Boli.order(:bank), :state, :name, :id, :name, include_blank: true %>
</div>
<% end %>
You need to group banks by state. This implies that there is either a one-to-many (one state to many banks) or a many-to-many relationship between them.
The one-to-many relationship implies Bank is a model that either has state as an attribute or references another model State.
The many-to-many relationship implies that both Bank and State are models joined by another table.
With both bank and state as attributes of another model it's unfeasible to model the relationship between them.
In order to use grouped_collection_select you should have both as models.

No method error displaying foreign key value instead of ID in show.html & index.html ruby on rails

I have three models: user, language and role. User model holds the foreign key of language and role. User (belongs_to: language) and (belongs_to: role). Language and role (has_many: users)
class User < ActiveRecord::Base
belongs_to :language
belongs_to :role
end
class Language < ActiveRecord::Base
has_many :users
end
class Role < ActiveRecord::Base
has_many :users
end
In _form.html.erb I use the following code to select the value iso_code of language and name of role.
<div class="field">
<%= f.label t("language") %><br>
<%= f.collection_select :language_id, Language.order(:iso_code),:id,:iso_code, include_blank: false %>
</div>
<div class="field">
<%= f.label t("Role") + '?' %><br>
<%= f.collection_select :type_role_id, Role.order(:name),:id,:name, include_blank: false %>
</div>
Up to here all works fine. The user selects the value (e.g.: es) and the code saves the primary key in the database. (e.g.: 2)
The in show.html.erb, to show the value I used the following code:
<%= #user.language.iso_code %>
It works great!
The issue comes when I try to do exactly the same for role:
<%= #user.role.name %>
I get an error message:
NoMethodError in Users#show
<%= #user.role.name %>
undefined method `name' for nil:NilClass
Rails thinks name is a method instead of a field in the model role.
Why does the same syntax works for language and not for role?
Thanks in advance : )
You need to save the values in role_id not type_role_id. Change the collection_select to below
<div class="field">
<%= f.label t("Role") + '?' %><br>
<%= f.collection_select :role_id, Role.order(:name),:id,:name, include_blank: false %>
</div>
Now you can do <%= #user.role.name %> which gives you the name of the role for the associated user object.
Update:
If you don't have role_id in users table, try creating a migration to add role_id to users table or you can create migration to rename family_role_id to role_id as well.
And finally do rake db:migrate

Rails tutorial - displaying username instead of user_id

In the Rails Tutorial there is a great chapter on creating a toy app with users and microposts. However, when editing the microposts, I can only edit user_id which is a numeric value, not user name. Is there a simple way to enforce displaying user's name instead of user's id in the app?
I've looked app/views/microposts/_form.html.erb and it says:
<div class="field">
<%= f.label :user_id %><br>
<%= f.number_field :user_id %>
</div>
What should I change to be able to select the users by name instead of the id?
Try using a select helper rather than a number_field.
<%= f.collection_select(:user_id, #users, :id, :first_name) %>
In your controller, you'd need the following line (or something similar):
#users = User.all
If you want to display each user's full name, you'd need to create a method in user.rb to concatenate first and last names, like so:
def fullname
fullname = "#{last_name}, #{first_name}"
end
Your select would then use the method name, like this:
<%= f.collection_select(:user_id, #users, :id, :fullname) %>
You should probably take some time to read up on all the different form helpers.
The feature you're looking for is called a collection_select. http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select
f.collection_select :user_id, User.all, :id, :username

Save other model's fields from a certain view

I'm really new at Ruby On Rails and I'm working with a baseApp that comes with the login and user registration functionalities. It also creates an empty profile for a user when they sign up.
Then, I've created a migration to add fields to the profiles table, such as firstName, gender, state, country, etc.
What I'd like to do, is to show those fields at the registration form and then save them on the profiles table. How can I do this?
Thanks,
Brian
accepts_nested_attributes_for provides everything you need.
So long as you've st up your user to have one profile you're just about good to go.
Add accepts_nested_attributes_for :profile to the User model. Then set up your form to be something like this:
<% form_for :user do |f| %>
...
User model fields
...
<% #user.build_profile if #user.profile.nil? %>
<% f.fields_for :profile do |p| %>
<%= p.label :first_name %>
<%= p.text_field :first_name %>
...
Profile model fields
...
<% end %>
<% end %>
The controller needs no modification.
Read up on the nested form section of FormHelper for more details.

Resources