Rails 5 - Best way to hold onto params for creating associations? - ruby-on-rails

Here's the scenario to illustrate my question. I have 2 models:
# models/post.rb
belongs_to :user
validates_presence_of :comment
And we have a devise model called Users
# models/user.rb
has_many :posts
What I would like to achieve:
Person comes to the website, is able to create a Post, after creating the Post, they are prompted to create an account. After creating the account, the Post that they just created would be associated to the User they just created.
Usually i'd make use of routes to hold the params[:id] which can be accessed in the controller method. For example the URL may look something like this:
www.foo.com/foo/new/1
And then I can do this:
# foo_controller.rb
def new
#foo = Foo.new
#parent = Parent.find(params[:id])
end
And in the view I can simply access #parent and use a hidden field to fill the parent ID.
But when routing through so many different pages (such as creating a Devise User), how do I hold onto the parent/child ID such that I can still create that association?

Using an hidden field or the route to store the id, with no authorization in the process, would not be secure. What if I just use the browser inspector and change the value of the id ? Your cool post would be mine.
What you could do is, for instance, add a field called guest_id to the Post, in which the value is unique (like SecureRandom.uuid), and also store that value in the session.
Thus, after the user is created, you could do something like that
if (post = Post.find_by(guest_id: session[:guest_id])).present?
post.update(user_id: current_user.id)
end

Related

Create User Account Settings Page in Ruby on Rails with devise

I am new to Ruby on Rails and I have created a project that contains a User table (generated by devise) and a AccountSetting table that contains user specific account settings (this table has a foreign key that relates to the id in the User table thus each User has zero or one AccountSettings). I have my seed data working fine, and I can seed the database with users that have user specific account settings. The User table is related to the AccountSetting table with a "has_one :accountsetting" and the AccountSettings table "belongs_to :user". This all works and makes sense. However, I have a method called "show_user_setting" in my UserSettings controller, and I do not know how to ONLY SHOW the account settings for that specific authenticated user.
So, how can I only display the user setting for the currently logged in user? Again, I am using devise.
My general idea of how to do this would be something like this. However I know this is incorrect, but for the purpose of an explanation, here it is.
def show_user_setting
#setting = AccountSetting.find(current_user)
end
My idea is that the #setting will contain the setting for the currently logged in user. Thanks in advance!
You should do this:
#app/models/account_setting.rb
class AccountSetting < ActiveRecord::Base
belongs_to :user
end
#app/models/user.rb
class User < ActiveRecord::Base
has_one :account_setting
end
This will allow you to call the following:
#setting = current_user.account_setting
Our Setup
For what it's worth, we do something similar:
#app/models/user.rb
class User < ActiveRecord::Base
before_create :build_profile #-> builds a blank profile on user create
has_one :profile
end
#app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
This allows us to put all sorts of different options inside the profile model (we have homepage etc):
The important thing to note here is that the above allows you to delegate various methods to the profile model, allowing you to call the following:
current_user.profile_name
current_user.profile_signin_redirect?
current_user.profile_avatar
etc
Have you tried
def show_user_setting
#setting = AccountSetting.find_by(user_id: current_user.id)
end
The way .find() works is it searches the model for the id passed. So, the way you currently have it is your going to try to search for the id of the model, when you want to find the foreign key. So use Model.find_by(column_name: param). You'll what to change user_id: to the column name of what you're storing the foreign key in, I'm just assuming it's something similar to that.
I'm guessing the show_user_setting function is part of a controller, if it is on a model then read this: accessing devise current_user within model
to set the #setting variable you should be able to do this
#setting = AccountSetting.find(user_id: current_user.id)
or
#setting = AccountSetting.find(user: current_user)

Rails_admin, Assigning Devise's current_user to model inside action

I have Devise Model Person which will log in and manage rails_admin app.
I have model called Model which has updater that was added by mongoid-history.
In order to set who updated my story I need to do something like this:
model = Model.new
model.updater = Person.first
model.save
According to this link to github, I can not set current_person, which created by Devise automatically. That's means that I need to set updater manually each time when something happens to my Model.
How to get current_person and set it to Model where rails_admin's action happens?
I only know that I need to write something to each action in initializers/rails_admin.rb
There's no access to current_user from the Model, because ActiveRecord models can be used independently from Rails apps, they can be used without logging in.
It is the controller that (often) requires the user to login and so has access to the user session and the current_user method. The controller must therefore do the work of setting the updater on the Model.
Perhaps you could set a callback method in the controller that acts on any action that modifies the model and sets the updater there.
I have found one solution for my question, but I don't know if it is good aproach or not.
If I want to assign my current_person in edit action I needed to write this:
config.model Model do
edit do
field :updater_id, :hidden do
visible true
def value
bindings[:view]._current_user.id
end
end
end
end
It createds hidden field which has asigned ID of current person which is logged in.
Mr.D's answer seems not to work (at least as of rails_admin 2.0.2); bindings doesn't have a :view key. The correct key is now :controller:
bindings[:controller].current_user # returns current User object
And if you not have a attribute to store this id and just need id to validate another operation, you can create a attribute to it:
config.model Model do
edit do
field :updater_id, :hidden do
visible true
def value
bindings[:view]._current_user.id
end
end
end
end
MODEL:
class TaxPlan < ApplicationRecord
attr_accessor :updater_id
validate :validate_updater_id
def validate_updater_id
yor code validation here
end
end

how can I create a form for User model which can be updated after successfully logged into application

Requirement: I need to create an application where user can logged in and can change the background color of that page, can change the font of the page and it should persist with session. so I am trying to create form which accept those attributes from user and can save in database. I am using devise for authentication.
How can I create form for user who is successfully logged into application(authentication is done by devise gem, so user table is already existing) and upon submission of form those attributes should get updated in user table. Any suggestion will be appreciated.
Q1 .What should be the name of controller and view for this form ?
Q2. How the routes can be define.
Q3. Whether controller should have update action to update user table with the extra attributes present in the form.
Thanks. Please comment below if I missed some information needed to provide. You can suggest me if you think this can be achieve in easier way also.
Whatever you want. Sounds li?ke you are just updated user attributes, so i would just use the User#update action
resources :users, only: [:update, :edit] #and any other user routes
? see 1
Also you will want to make sure that people can only edit their own account. In a before action you will want to add.
redirect_to root_path unless current_user == user_you_are_editing
What I would do:
Create a Model called UserPreferences that belongs_to :user, give this Model the attributes 'background_color', etc...
Add statement to user has_one :user_preferences
Create a form for the prefs like any Rails Model, that can only be accessed by current_user.
Use current_user.user_preferences to refer to these values, you can enhance this by placing alias methods in User, for example:
class User < ActiveRecord::Base
def background_color
self.user_preferences.background_color
end
end

Ruby on Rails 3 - create an instance of one model from another model

I have the following problem, but first I will make some assumptions
The example is just to explain my problem in an easy way
The tables models are not related
I have two tables (models) Users an Emails
Users
id
name
email
Emails
id
account
So, the idea is every time I create a user, I want to create an instance of Email, where Emails.account = Users.email
I tried using callback
def after_create
Email.create!(:account => user.email)
end
But it didn't work.
Is there another way to achieve this?
You are almost there, except you don't need to reference a 'user' variable in your after_create because you are in the User model.
Try the following:
class User < ActiveRecord::Base
after_create :create_email
def create_email
Email.create!(:account => email)
end
end

whats the difference between these create methods

Hey I am stuck with my orientation in rails.
I got a User model, a Course Model and a CourseEnrollment Model.
When I want to add a link in my Course Index View like
link_to 'join' CourseEnrollment.create(:course_id => course.id, :user_id => current_user)
Does this create method belong to my Model? I am confused because in my User Model I defined a method that uses role_assignments.create(.....). What is the difference between these 2 create methods? I cant use course_enrollments.create by the way. Thx for your time
I'm a bit confused as to what you're asking, but I'll try my best.
(First of all, in your example, current_user should probably be current_user.id.)
When you call CourseEnrollment.create, you are simply creating a new CourseEntrollment model with the specified attributes.
Assuming that your User model has_many :role_assignments:
When you call #role_assignments.create from within your User model, Rails automatically creates the association for you (e.g. sets the user_id to the id of the user). This doesn't have to be done within the model itself, though:
current_user.role_assignments.create(...) # automatically sets the association
Assuming that your User model also has_many :course_enrollments, the following will create a CourseEnrollment model and automatically associate it with the current user:
current_user.course_enrollments.create(...)

Resources