How to add user_id in form params in Rails controller - ruby-on-rails

I have this model, called products, that has an id, user_id and product_name.
I have a simple form_for to pass to my product_controller the product_name param. In this same controller, I have access to the current_user, which I have to pass in order to create a new Product.
The problem is that the user_id always has to be the current user logged in, so I can't allow the user to send me this param in my form_for, thus I can't permit it as well.
What I'm doing right now is to create a new hash with the user_id param, and merge the params that comes from the form.
products_controller.rb:
def create
product_user_id_param = { "user_id": current_user.id.to_s }
#product = Product.new(product_user_id_param.merge(params[:product_name]))
...
end
Is there a more convenient or Rails way to do this?

You can create you product with you current_user reference, this is the more convenient way in my opinion:
current_user.produtcs.create(name: params[:product_name])
But, to above code works, you have to construct you relations correctly, like the following:
class User < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belogs_to :user
end
Now, you can do it!
You can read more about it, in https://guides.rubyonrails.org/v3.2/association_basics.html, the docs recommeds this way!

Solution #1
Product has user_id which means product belongs_to :user and user has_many :products
Hence it can be written as
current_user.products.new(product_name: params[:product_name])
Solution#2
Set current_user explicitly
#product = Product.new(product_name: params[:product_name])
#product.user_id = current_user.id
##product.user = current_user
#product.save

Related

How to update existing model when another is created?

I have two models, User, and Product. Product belongs to User, User has many Products.
When a Product is created I also want to update multiple fields in the User model. I've been developing with Ruby for like 2 years now and still don't understand forms fully when it comes to this stuff. I'm still getting permitted: false. Now I know that for instance if I was creating a user while also creating a product I would just do #product.user.build but in this case I just want to update an already existing record.
I also realize that I probably can't call f.fields_for :user as #product doesn't know about user yet. In my head I believe I should be able to just pass additional params to the form, grab the current_user in the product#create action and then update the attributes manually by calling update_attributes on user.
product.rb
accepts_nested_attributes_for :user
product controller
def new
#product = Product.new
end
params.require(:product).permit(:product_name, user_attributes: [:phone_number, :email_address])
product view
form_for #product do |f|
f.fields_for :user do |c|
c.text_field :phone_number
c.text_field :email_address
f.text_field :product_name
end
I also realize that I probably can't call f.fields_for :user as #product doesn't know about user yet.
You can assign attributes to #product without saving it.
def new
#product = Product.new(
user: current_user
)
end
Now #product.user works.

Rails how to associate 2 models on create?

I would like to associate Order object wit Dispute Object on create of Dispute but when i go create the object in the log shows:
ActiveRecord::RecordNotFound (Couldn't find Order without an ID)
should i not try to find the order in the method?
Someone know how to associate the objects in the creation?
the Dispute Controller is:
class DisputesController < ApplicationController
def new
if current_user.address.blank?
redirect_to edit_user_path
flash[:error] = 'fill the address'
else
#dispute = Dispute.new
end
end
def create
#order = Order.find(params[:id])
if current_user == #order.buyer
dispute = #order.dispute.nil? ? Dispute.new : #order.dispute
dispute.attributes = params[:dispute]
dispute.user = #order.buyer
dispute.buyer_name = #order.buyer_name
dispute.seller_name = #order.seller_name
if dispute.save
flash[:success] = 'Dispute Created'
end
end
The order model
class Order < ActiveRecord::Base
has_one :dispute
end
the dispute model
class Dispute < ActiveRecord::Base
belongs_to :order
end
Without adding parameters or a nested route the request won't know what order is being referenced. You can use nested routes like orders/:order_id/dispute (http://guides.rubyonrails.org/routing.html#nested-resources) and then you can use #order.build_dispute (http://guides.rubyonrails.org/association_basics.html#methods-added-by-belongs-to)
My first thought based on the error you are getting is to check what params you have available upon the form's submission, because it seems it is not finding an Order based on the param you're passing into the find call.
Also check out strong params for security: http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html

rails create page from

I have Categories and Products. A product has a relation
belongs_to :category
In the categories show page I have a button to add a new product. This button goes to a page where I create the new product, but I need to give the category to the new product.
How can I pass the id from the category page where I was to the new Product? So, if I am in the category Electronic I click 'Add product' and this product automaticaly is associated with Eletronic category.
Hope you can understand what I want.
Thanks
You need to pass the category_id in your link, e.g. new_product_path(category_id: #category.id).
You will also need to have a field in your product form to save the category's ID, e.g <%= f.hidden_field :category_id, params[:category_id] %>
First, I would decide whether each product is contained within a category, or whether it's simply associated with a category. Hints it is contained would be:
You expect each product to have exactly one 'parent' category.
You expect each product will always appear in the context of its parent category.
If and only if you believe this to be the case, I would be tempted to nest the product resource within the category.
# routes.rb
resources :categories do
resources :products
end
# products_controller.rb (SIMPLIFIED!)
class ProductController < ApplicationController
before_filter :get_category
def new
#product = #category.products.build
end
def create
#product = #category.products.build(params[:product])
if #product.save
redirect_to #product
else
render template: "new"
end
end
def get_category
#category = Category.find(params[:category_id])
end
end
If you do this, rails will ensure your product is associated with the right category. The magic happens in #category.products.build, which automatically sets the category_id based on the relationship.
If you'd rather keep categories and products as simple associations, I'd just use a query parameter as per Eric Andres answer, although I'd be tempted to handle it in a slightly different way:
# link:
new_product_path(category_id: #category.id) # So far, so similar.
# products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new
#product.category_id = params[:category_id].to_i if params[:category_id]
end
end
# new.erb
<%= f.hidden_field :category_id %>
This is mostly just a stylistic difference. Eric's answer will work too - I just prefer to set the value on the model itself rather than have the view worry about parameters etc.

How do I not use current_user in the model

I have a form to create posts. This form also has tags on the post and it is owned by a user. The form is using a virtual attribute :tag_ids.
I would like to do something like this, where you find_or_create_by_name_and_user_id and then just pass the current_user.id.
def tag_ids=(tags_string)
self.taggings.destroy_all
tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
tag_names.each do |tag_name|
tag = Tag.find_or_create_by_name_and_user_id(tag_name, current_user.id)
tagging = self.taggings.new
tagging.tag_id = tag.id
end
end
From what I read this isn't how it should work. The current user should be called in the PostsContoller create method.
def create
#post = Post.new(params[:post])
#post.user = current_user
respond_to do |format|
if params[:commit]
#post.save
...
end
This is where I'm stuck. If the params are passing all of the attributes, how can I inject the current_user id into tag model which has a :name and :user_id attribute?
I also read that this should work because of my associations.
User - has_many posts; has_many tags
Post - belongs_to user; has_many taggings; has_many tags through taggings
Tag - has many taggings; has_many posts through taggings; belongs_to user
Tagging - belongs_to post; belongs_to tag
You can't get current_user in model ,you have to pass explicitly,there are may be couple of hack to do this,But simple way is,user is already stored in post so self.user.id is useful
If above method is in post model,post user is tagging user.
Tag.find_or_create_by_name_and_user_id(tag_name, self.user.id)

Rails 3: alias_method_chain to set specific attribute first

When user's create a post I'd like to set the user_id attribute first. I'm trying to do this using alias_method_chain on the arrtibutes method. But I'm not sure if this is right as the problem I thought this would fix is still occurring. Is this correct?
Edit:
When my users create a post they assign 'artist(s)' to belong to each post, using a virtual attribute called 'artist_tokens'. I store the relationships in an artist model and a joined table of artist_ids and post_ids called artisanships.
I'd like to to also store the user_id of whomever created the artist that belongs to their post (and I want it inside the artist model itself), so I have a user_id column on the artist model.
The problem is when I create the artist for each post and try to insert the user_id of the post creator, the user_id keeps showing as NULL. Which is highly likely because the post's user_id attribute hasn't been set yet.
I figured to get around this I needed to set the user_id attribute of the post first, then let the rest of the attributes be set as they normally are. This is where I found alias_method_chain.
post.rb
attr_reader :artist_tokens
def artist_tokens=(ids)
ids.gsub!(/CREATE_(.+?)_END/) do
Artist.create!(:name => $1, :user_id => self.user_id).id
end
self.artist_ids = ids.split(",")
end
def attributes_with_user_id_first=(attributes = {})
if attributes.include?(:user_id)
self.user_id = attributes.delete(:user_id)
end
self.attributes_without_user_id_first = attributes
end
alias_method_chain :attributes=, :user_id_first
EDIT:
class ArtistsController < ApplicationController
def index
#artists = Artist.where("name like ?", "%#{params[:q]}%")
results = #artists.map(&:attributes)
results << {:name => "Add: #{params[:q]}", :id => "CREATE_#{params[:q]}_END"}
respond_to do |format|
format.html
format.json { render :json => results }
end
end
In your controller, why not just do this:
def create
#post = Post.new :user_id => params[:post][:user_id]
#post.update_attributes params[:post]
...
end
But it seems to me that it would be much better to create the artist records after you've done validation on the post rather than when you first assign the attribute.
EDIT
I would change this to a callback like this:
class Post < ActiveRecord::Base
attr_accessor :author_tokens
def artist_tokens=(tokens)
#artist_tokens = tokens.split(',')
end
after_save :create_artists
def create_artists
#artist_tokens.each do |token|
...
end
end
end

Resources