Rails: Associate posts with user and location - ruby-on-rails

I have a Post model in my app, which belongs_toa User model, as well as a Location model. Both the User and Location models use the has_many relation with Post.
Now, in my Post controller's create action, I want to automatically associate the Post with the currently logged in user (available to me as current_user through Devise).
Also, it should associate with a pre-existing Location if one exists with the same address field (which he enters through the form), or create a new one and associate it with that if not.
My Post model has a user_id field as well a location_id field for this purpose.
How do I accomplish the two associations automatically in the create action when the user creates a new Post?

You'll need to use 2 statements.
#post = current_user.posts.build(params[:post])
#post.location_id = params[:location_id] # change this to whatever you're passing.
#post.save

Related

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

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

How to access variables in model

I am trying to get access to a property contained inside my user object.
My user model has_many: posts. In the controller how would i gain access to these posts? Would i create a method in the model?
def posts
#posts = Post.find(User_id: params[:id])
end
or can i directly access the posts for the user. User.posts Since i am currently residing in the controller, is the controller aware of the currently selected model? Or do i have to pull the information again?
You can query the database for all the posts with a specific user_id, like this:
#posts = Post.where(user_id: params[:id])
Alternatively, you can find the user first and then fetch all posts associated with that user, like this:
user = User.find(params[:id])
#posts = user.posts
Assuming your id in params is the id of your user, you can use user = User.find(params[:id]) to get the user and #posts = user.posts to get all the posts of this user.
So, it is not about where you are, It is about what you are calling.
I'm sure you are familiar with relationships...
When you have relationships, it means that you can get to one relation from the other through whatever association exists between them.
If I am my father's son, then you can get me directly by checking my father's children. ( you don't necessarily have to get all children in the village first )
So, bringing all my story above together, with the association between your Post and User, you can always call user.posts (user being an instance of User) and post.user ( with post being an instance of Post)
The Ruby on Rails guides have a section on associations, which is what you want. It's here: http://guides.rubyonrails.org/association_basics.html
In a nutshell, because you have added an association in your user model to a number of post records, Rails will build a helper method in your user model called posts. You can use that to access all the posts associated with that user.
When you create a post, the post record needs to have a column called user_id. This will provide the 'physical' link between the user and post models. You can access the posts from a user like so:
user.posts each do |post|
# do something with post.content
end
To get posts that match some criteria in the posts collection you can query like this:
posts = user.posts.where(:something => 'matches criteria')
If you know there's only one post that matches the criteria, you can do this:
post = user.posts.where(:something => 'matches criteria').first
The post model also needs a belongs_to :user association. (The belongs_to will generate a helper method called user in the post model which you can then use to access the user record from the post.) For example:
user_email = post.user.email
The user record does not require a post_id column since Rails knows that user.post refers to the post table and automagically generates a query using user_id.
Anyway, the guide I linked to above will give you all the information you need and more too.

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

What query do I perform to access the last of a collection of models before they have been saved?

I have a Workflow model, an Action model, and a Role model. Actions are nested attributes of a workflow, and an action has and belongs to many roles.
The associations work fine. However, in my form view, I need to add a role to the last action that has been build (note but not created).
The Workflow controller:
def create
#workflow = Workflow.new(workflow_params)
if params[:add_role] # from a submit button
Action.last.roles << Role.find(params[:role_id])
# doesn't work as no actions have been created
...
elsif params[:add_notify_action]
#workflow.actions.build # cannot save because parent hasn't been saved
end
In short, how do I get to the last Action that has been built in my controller? By definition, it's not in the database.
In long, if I can't, what's another option to get the roles added to the actions?
If an Action is a nested attribute of Workflow, when you initialize a new workflow passing the params, you initialize a new Action association. Then you could just go ahead and do:
#workflow.actions
to access the Actions. You cannot get the last one unless the Action has some attribute that defines that "last" characteristic (like a date given by the user). So consider saving them and then getting you can the last one by ordering them (created_at and updated_at fields). And before adding the persisted Roles, you'll need to also persist the action.
In the end, I decided to create a #current_action_id in my controller that I would update each time I called #workflow.actions.build. I ended up with this add_role method.
def add_role
unless #workflow.actions.empty?
#workflow.save!
role = Role.find(params[:role_id])
roles = Action.find(current_action_id).roles
roles << role unless roles.include? role or current_action_id <= 0
end
end
Thank you #engineersmnky for the tip on saving my workflow before adding the role.

How do I perform multiple modifications and save a has_many relationship

class Company < ActiveRecord::Base
has_many :users
end
The user hits on Company->Users href link and assuming it shows 3 users for a certain company.
I would want to set user.age (an integer) for all the users and press save. How do I do this in my controller / view code?
We usually have an Edit link against each User to modify its details, but I would like to set age information for all users.
Use a form_tag to submit to an arbitrary controller action set up specifically for editing multiple users (not one of your user scaffold methods). Then, add one fields_for tag per user inside of the form you created, each with a field for the age attribute. When submitted, you can simply iterate over the user params in the controller to manipulate your users.

Resources