Creating a new nested resource - ruby-on-rails

I'm working on a basic garden logging application which consists of gardens, plants and planted plants. Each user has one or many gardens, plants are master definitions, and a planted plant can be thought of as an instance of a plant in a specific user's garden.
In my routes.rb file I have the following:
map.resources :gardens do |gardens|
gardens.resources :planted_plants, :has_many => :plant_log_entries, :collection => { :filter => :post, :choose_garden => :post}
gardens.resources :garden_log_entries
end
map.resources :plants
This makes sense to me when retrieving a list of planted_plants in a user's garden, but I'd like to create a planted_plant record from the index of plant. The problem is, a user can have multiple gardens. How can I create a new form for a planted_plant that allows the user to specify which garden should be used?
The current route requires a garden_id - which makes sense for retrieval, but I'd like to supply that as a parameter for creation.
Thanks in advance for any help!

I would add another (non-nested) route, to allow access to the PlantedPlantsController#create action without specifying the garden_id in the URL:
map.resources :planted_plants, :only => :create
This will allow you to post your form to /planted_plants. As for the form itself, you'll probably need something like this:
<% form_for #planted_plant do |p| %>
<%=p.label "garden_id", "Garden" %>
<%=p.collection_select :garden_id, current_user.gardens, :id, :name %>
... other fields ...
<% end %>

Related

Look for an attribute in an eager loded subsection of a subsection, without causing a SQL call?

I have a set of models like below (the actual setup displayed here is a simplified mockup):
Blog
has_many :posts
Post
has_many :comments
# contributor_id is a part of Post
Contributor
# having fields such as :id, :name, etc.
I iterate through them like this:
Blog.all.includes(:posts => :contributors).each do |blog|
render partial: 'blog_item', :locals => { :blog => blog }
end
Inside 'blog_item' partial view I would like to determine whether a specific Contributor (id: 123, name:"john") is included among the posts. As a general problem it is quite easy, but I want to do this without having an SQL call (causing N+1).
Currently, I am solving this like so:
blog.posts.pluck(:contributor_id).include?(123)
which causes an SQL call (and subsequently an N+1).
I can't do something like this:
blog.posts.include?(:contributor_id)
So, how can this be solved without having that extra SQL call?
I have been trying to solve this for quite some time. Posted the question, read a subsection of an article directly after and solved it in 2 mins.
#contributor = Contributor.find(123)
Blog.all.includes(:posts => :contributors).each do |blog|
render partial: 'blog_item', :locals => { :blog => blog }
end
# Inside blog_item
blog.posts.map(&:contributor).include?(#contributor)

Pass a parameter to the new action in Active Admin

I have two related models, Bunny has_many BunnyData (which belongs_to Bunny). From the show page of a particular Bunny (in Active Admin), I want to create a link to make a related BunnyData. I've tried a few different ways, with no success, and am currently trying this:
sidebar :data, :only => :show do
link_to 'New Data', new_admin_bunny_datum(:bunny_id => bunny.id)
end
The link being generated ends up as something like:
.../admin/bunny_data/new?bunny_id=5
But when you go to that page, the dropdown for Bunny is set to the blank default as opposed to showing the name of Bunny with ID 5.
Thanks in advance.
Rails namespaces form fields to the data model, in this case BunnyData. For the form to be pre-filled, any fields provided must also include the namespace. As an example:
ActiveAdmin.register Post do
form do |f|
f.inputs "Post Details" do
f.input :user
f.input :title
f.input :content
end
f.actions
end
end
The fields can be pre-filled by passing a hash to the path helper.
link_to 'New Post', new_admin_post_path(:post => { :user_id => user.id })
Which would generate the following path and set the form field.
/admin/posts/new?post[user_id]=5
In the case of BunnyData, it might be slightly different due to the singular and plural forms of datum. But that can be verified by inspecting the generated HTML to find the name attribute of the inputs.

Rails - Get 3 ID's in a 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)
#honor = Honor.create(:group => Group.find(params[:group_id]),
:person => Person.find(current_person), :honored => Person.find(current_person))
if #honor.save
...
end
The problem is in this :honored => Person.find(current_person), that is not getting the right ID and I don`t know how to get it.
In my view:
<% #asked_groupmembership.each do |agm| %>
<% form_for(:honor, :url => honors_path(:group_id => #group.id, :person => current_person.id,:honor => agm.member.id)) do |f| %>
Any help?
Thanks.
If you need 3 components to properly create an honor record, you need to pass them from the form. You seem to be doing that part correctly.
:group_id => #group.id
:person => current_person.id
:honor => agm.member.id
To create the record, access the passed variables.
Honor.create(:group => Group.find(params[:group_id]),
:person => Person.find(params[:person]),
:honored => Person.find(params[:honor]))
Understanding the above isn't the most efficient, but used for demonstrative purposes. You'd likely want to avoid redundant database hits, e.g. :person => current_person rather than another query

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.

Using sortable_element in Rails on a list generated by a find()

I'm trying to use the scriptaculous helper method sortable_element to implement a drag-and-drop sortable list in my Rails application. While the code for the view looks pretty simple, I'm really not quite sure what to write in the controller to update the "position" column.
Here's what I've got in my view, "_show_related_pgs.erb":
<ul id = "interest_<%=#related_interest.id.to_s%>_siblings_list">
<%= render :partial => "/interests/peer_group_map", :collection => #maps, :as => :related_pg %>
</ul>
<%= sortable_element("interest_"+#related_interest.id.to_s+"_siblings_list", :url => {:action => :resort_related_pgs}, :handle => "drag" ) %>
<br/>
And here's the relevant line from the partial, "interests/peer_group_map.erb"
<li class = "interest_<%=#related_interest.id.to_s%>_siblings_list"
id = "interest_<%=related_pg.interest_id.to_s%>_siblings_list_<%=related_pg.id.to_s%>">
The Scriptaculous UI magic works fine with these, but I am unsure as to how to change the "position" column in the db to reflect this. Should I be passing the collection #maps back to the controller and tell it to iterate through that and increment/decrement the attribute "position" in each? If so, how can I tell which item was moved up, and which down? I couldn't find anything specific using Chrome dev-tools in the generated html.
After each reordering, I also need to re-render the collection #maps since the position is being printed out next to the name of each interest (I'm using it as the "handle" specified in my call to sortable_element() above) - though this should be trivial.
Any thoughts?
Thanks,
-e
I typically create a sort action in my controller that looks like this:
def sort
order = params[:my_ordered_set]
MyModel.order(order)
render :nothing => true
end
Don't forget to add a route:
map.resources :my_model, :collection => { :sort => :put }
Now, on MyModel I add a class method that updates all of the sorted records with one query (this only works in mysql, I think..):
def self.order(ids)
update_all(
['ordinal = FIND_IN_SET(id, ?)', ids.join(',')],
{ :id => ids }
)
end
The single query method comes from Henrik Nyh.

Resources