Rails 3, change or fill submitted form values in controller - ruby-on-rails

I have two independent "component" models, contacts and articles. Contacts come in many types (producers, exporters etc...).
In the new article form I have a dropdown selector with contacts (id and title) and want to store the selected value and text in the article table.
In the new article form view:
<%= f.select :producer_id, options_for_select(producers, #article.producer_id) %>
That works and producer_id is stored in article table.
That's clear and logical to me, but in some cases I also need to store the selected contact's title in producer_title.
I have read many different options like "do it in model, before save", or "do it in controller", and I have done it inside controller.
Article controller (only part from update):
#cont_name is producer title from Contacts
def update
params[:article][:producer_title] = Contact.where(id: params[:article][:producer_id]).pluck(:cont_name).first
end
This works, but is it the best-practices approach to this problem?
Also, why I can't get it to work if I change the params[producer_id] part to use: id: params[:producer_id] ?
Best regards and thanks.

How about something like the following instead:
def edit
#article = Article.find(params[:id])
#producers = Contact.where(type: "producer") # or however you distinguish between producers and other contacts
end
Then in your form change it to:
f.select :producer_id, options_from_collection_for_select(#producers, "cont_name")
# you might need to do (#producers, "cont_name", "cont_name"), can't quite remember
Then your update action will be much simpler:
def update
#article = Article.find(params[:id])
if #article.update_attributes(params[:article])
...
else
...
end
end
The reason :id => params[:producer_id] doesn't work is that params is a nested hash, so something like:
params = { :article => { :producer_id => 34, :title => "Cool Article" }, :another_key => { :key => :value } }
So to access the producer_id you first have to retrieve the article hash, otherwise it will only look through the first set of keys which include article and another_key in the example above, but don't include producer_id.

Related

Rails Craiglist like list of items

So I have a state model and city model associated with has_many and belongs_to. I want to display a page with each state with its associated cities underneath.
I created a page controller and page called "Locations" and manually entered in
<%= link_to "Allentown", allentown_path %>
which then takes you to the allentown page.
On the allentown page I filtered the listings by adding this code to the pages controller
def allentown
#title = "Allentown Listings"
#tattoo_briefs = TattooBrief.where( :city_id => 1 ).find(:all, :order => "id DESC" )
end
I know this isn't DRY. Also can get very cumberson if I have 200 cities. Is there a better way?
You need to add a resource to your routes:
routes.rb
resources :city
That essentially gives you all the RESTful actions for the City model. Then, in your controller, use the show action to..wait for it..show your city page
cities_controller.rb
def show
#city = City.find(params[:id])
#title = "#{#city.name} Listings"
#tattoo_briefs = TattooBrief.where( :city_id => params[:id] ).find(:all, :order => "id DESC" )
end
you can still modify this by studying more on routes and controllers from the rails api. With added knowledge, you can get to allentown by modifying your route to use the city name instead of the id.

How to send user.id with :user in params even though user.name is what is in the form drop down?

Currently, I have an action in my customers controller generating an array of names, #username_array, of all objects of class User with which to populate a drop down menu in a form that creates a new object of class Customer. The form element looks like this right now:
<%= f.select :user_id, #username_array %>
What I'd really like is for the id of the user to be sent into params[:customer][:user_id] instead of the name of that user that is chosen in the drop down. So in my create action in my customers controller I have the following code:
#customer = Customer.new(params[:customer])
#user = User.find_by_name(params[:customer][:user_id]) # where :user_id is actually currently the name selected from the drop down
#customer.user_id = #user.id
Is there an easier way of doing this?
Change your #username_array to include both the name and the id of the user:
Instead of:
["Bob","Sally","Dave"]
Make it:
[["Bob", 1],["Sally",2],["Dave",3]]
This could be accomplished by something like this:
#username_array = User.all.map {|user| [user.name, user.id]}
Then, f.select will display the name in the dropdown, but the actual value passed in through params[:customer][:user_id] will be the id of the user, which is what you want. With this in place, the following is all you need in the controller action code:
#customer = Customer.new(params[:customer])
You won't have to look up the user by name, the params hash will already have the correct id value.
Note that instead of making #username_array an instance variable you could just create a utility method in the helper for this controller, or the application helper:
def user_select
User.all.map {|user| [user.name, user.id]}
end
Then, in your view:
<%= f.select :user_id, user_select %>
If you put this in your application helper, you can use it everywhere and only have the code in one place (DRY).
you can do
#user = User.find_by_name(params[:customer][:user_id])
#user.customers.new(params[:customer])
or
#user = User.find_by_name(params[:customer][:user_id])
#customer = #user.customers.create(params[:customer])
but to do that you must have the relation (has_many, belongs_to,...)
or
Customer.new(params[:customer], :user_id => params[:customer][:user_id])
or
f.collection_select :user_id, #username_array, :id, :name

Rails - Is there a way to update a single attribute?

I was just wondering, I have a model that, besides the id's from other models (FK), it has a single attribute boolean. I want to know how can I create a button that changes this boolean and just that
My model in question is this one:
class Squad
belongs_to :player
belongs_to :team
end
I want to create a button on the team#show page so the player that owns this team can change the boolean of squad. How can I do this and how would look like my controllers?
Thanks :)!
-Edit-
I'm using a link like this:
<%=link_to("Change status", squad_path(sqd, :status => true), :method => :put, :confirm => "Sure?")%>
Where sqd is part of my query. Is this link wrong?
<%= link_to("Change status", squad_path(sqd, "squad[status]" => true), :method => :put, :confirm => "Sure?") %>
in your controller (it is pretty common)
def update
#squad = Squad.find params[:id]
if #squad.update_attributes params[:squad]
...
end
end
Yes there is. The method is called "update_attribute". It takes two arguments, the name of the field and the value.
squad.update_attribute(:boolean_field,true) # or false
Based on updated question
def update
#squad = Squad.find(params[:id])
if #squad.update_attribute(:status,params[:status])
...
end
end
What's the name of your attributes ?
Since it belongs to player, you can access it with player.squad.name_of_your_attributes = new_value. Don't forget to save your object if you want the changes to be saved in your DB.
Also, you could read that
EDIT: fl00r answered your edited question, no need that i repeat what he wrote.

Passing IDs through a new Form

This question is about a different approach I'm trying to the one asked here:
Passing IDs through a new Form
I have a group#view page, that is accessed by a Person. In this page, the Person can see the members of the group via methods I developed. The problem is that I need to create the model Honors using the Id from the group, the id from the person accessing the page, and the id from a member of this group.
In my Honors controller I have:
def create
#person = Person.find(current_person)
#asked_groupmembership = #person.owned_group_memberships.find_all_by_status(true,:include => [:group, :member])
#asked_groupmembership.each do |agm|
#honor = Honor.create(:group => Group.find(params[:group_id]),
:person => Person.find(current_person), :honored => Person.find(agm.member.id))
end
if #honor.save
...
end
In my view I have a link that directs the person to the form in order to create a new honor:
<% #asked_groupmembership.each do |agm| %>
<%= link_to "Create Honor", new_honor_path(:group_id => #group.id, :person => current_person.id,
:honored => agm.member.id) %>
But in my forms I can't get the ids and stuff
<% form_for(:honor, :url => honors_path(:group_id, :person,
:honored)) do |f| %>
The error I get is that I can't find Group without an Id.
Any ideas? Thanks.
##Edited2##
Changed my crontroller
def new
##person = Person.find(params[:person])
##honored = Person.find(params[:honored])
##group = Group.find(params[:group_id])
#honor = Honor.new
end
def create
#person = Person.find(current_person)
#honor = Honor.create(:group => Group.find(params[:group_id]),
:person => Person.find(params[:person]),
:honored => Person.find(params[:honored]))
if #honor.save
...
end
First, it seems like you are missing a controller method. Along with every form that creates a new object there are typically two controller methods
new
Gathers up any data that the form needs to render itself
Renders the form
create
Collects the data from the form
Creates the new object
It looks to me like you are missing the new method. In the new method you would gather up all the hidden data that the form needs (e.g. the information that the user is not going to type in directly, like the #person info). Then, I would put this information in your form using hidden form parameters (rather then trying to put it in the form URL).
Objective: From the group#view, loop through and create and "Add Honor" link for each member of the group, while keeping track of the specific member being honored, group in which said member is honored, and the person who is honoring.
The following accomplishes that objective.
Route:
match "honors/:group/:person/:honored" => "honors#create", :as=>"my_custom_new_honor"
Group view:
<% #asked_groupmembership.each do |agm| %>
<%= link_to "Create Honor", my_custom_new_honor_path(:group=> #group.id, :person => current_person.id,:honored => agm.member.id) %>
Honor Controller, Create Method
#honor = Honor.create(:group => Group.find(params[:group_id]),
:person => Person.find(params[:person]),
:honored => Person.find(params[:honored]))
In this scenario you do not need the honor#new method, you're going directly to the create method. This assumes all the relationships are established correctly.

nested attributes overwrite related model

I have a new action in my controller that I'm passing parameters into so the form for my new object will be filled out with the values from the record the user is duplicating, specifcally this gives the user the opportunity to 'edit' the new content before submitting it.
like this:
def new
#parent = Recipe.find(params[:parent_id])
#recipe = Recipe.new(
:name => #parent.name,
:description => #parent.description,
:ingredients => #parent.ingredients,
:steps => #parent.steps
)
end
But the problem is that both my ingredients and steps have nested attributes, each with an id of the original. The problem is that because rails is not giving my nested attributes a new id it doesn’t create those records, in fact i think it may try to save over the other ones.
What can I do here? is there a way to pass the #parent.ingredients object to my :ingredients parameter and give it a new id?
Like I know the issue might be the first line in the action
#parent = Recipe.find(params[:parent_id])
Because it's a find it's going to bring the parameters with it, but is there away to find that object, and create new id's for all the objects nested attributes?
#recipe = Recipe.new(:name => #parent.name, :description => #parent.description)
#parent.ingredients.each { |i| #recipe.ingredients.build(:name => i.incredient_name, :description => i.ingredient_description) }
Make sense?
Wow! my jaw was just on the floor.
#recipe = #parent.dup

Resources