Experienced Java developer, new to Rails - wondering about belongs_to relationship in scaffolding.
Saw another answer like this
Does rails scaffold command support generate belongs_to or many to many model middle table migration info?
and followed the rails generate scaffold_controller obj:references pattern.
The index/show page is showing #<MyClass:xxxx> instead of the string I want - is there a method in the target class (parent side of the belongs_to) I need to override to specify the identifier?
Also in the edit view, it looks like it's trying to modify the reference as a string rather than as drop-down - is there something I need to specify to make that happen?
Thanks!
BTW - I was able to get similar scaffolding to work in Django and Grails, where the foreign key turned into a drop-down; I'm hoping Rails is equally easy and I'm just missing it.
You can override the #to_s method on the instances, as it is the one being called.
class FooDoodle < ActiveRecord::Base
def to_s
name
end
end
That's when showing a record.
However, when you're actually using the form to set the associations, scaffold will only generate an input field in the view so you can enter the id. You could have a dropdown menu for example, but the options for that dropdown would somehow have to be selected in a manner.
For example, if there are 2000 possible associated records, which ones do you show? Do you show the 2000? Only the first 10? That logic would go into your controller.
So, for example:
class FooDoodlesController < ApplicationController
def edit
#foodoodle = FooDoodle.find(params[:id])
#friends = #foodoodle.possible_friends # or else
end
end
and using select and options_for_select as choices
# _form.html.erb
<%= form_for #foodoodle do |f| %>
<%= f.label :friend %>
<%= f.select :friend, #friends.map{ |p| [p.to_s, p.id] } %>
Related
I want for every users to have only one row of data in a table named business. He can edit it for the second time.
Also i want to submit each column value separately using form_for but when i do like this when submitting the form for the second time for a different column entry, it goes to the second row making the previous row empty.
How can i achieve this?
Here is my code...
<%= form_for #new_business do |f| %>
<%= f.text_area :first_problem %>
<%= f.submit %>
<% end %>
A screenshot of the data table
Table data screenshot
Any help is appreciated as i am new to rails. Thanks.
One user can only have one business, you need to set relationship between them
In user.rb model
has_one :business
In business.rb model
belongs_to :user
For your second problem, i think you are not passing row id for which you want to update columns, so everytime it is creating a new entry for that column
It looks like your code snippet is from your new template (i.e. "app/views/businesses/new.html.erb"). This should only be used when you want a new object created. Your controller probably says something like
def new
#new_business = Business.new
end
When the form is submitted, the :create action in your controller gets called, which should create a new record in the database.
When you want to edit that object, you use an edit action in your controller that corresponds to an edit.html.erb view. Something like this in the controller:
def edit
#business = Business.find(params[:id])
end
with a similar form to the one you listed above in the view. When that form is submitted, it should route to an :update action in your controller, that updates the existing record in the database.
The tricky part about what you are trying to accomplish is limiting each user to only being able to create one record in the database. There are many ways to go about doing this, but the general idea would be to restrict the user's access to the new and create actions in the controller once they have already created a record. You can do that by using before_action (if using Rails 4.0 or greater) or before_filter (if using Rails < 4.0) to call a method that checks if a user has already created a record.
campaign.rb
class Campaign < ActiveRecord::Base
has_many :items
accepts_nested_attributes_for :item
end
item.rb
class Item < ActiveRecord::Base
belongs_to :campaign
end
Campaign has 2 attributes: title and description
Item has 1 attirubte: name
I'll try explain myself by words, I want to create a nested form where they user insert the campaign's name and description but he can insert more than just 1 item, he can insert a list of items (in particular there will be a "+" button that when clicked a new item row will appear and the user can insert items).
At the end all is send all together clicking just one submit button.
How can I reach my goal with rails?
I answered a question just yesterday. Here's the link: Rails accepts_nested_attributes_for with f.fields_for and AJAX
I'll write out how it works & provide some resources to help give you some more ideas:
How It Works
Loading associative fields into a form is done by using f.fields_for
You'll do it like this:
#app/views/campaigns/new.html.erb
<%= form_for #campaign do |f| %>
<%= f.fields_for :items do |a| %>
<%= a.text_field :information %>
<% end %>
<% end %>
In order to get this to work, you have to build the associated ActiveRecord objects in the backend before you render the view, like this:
#app/controllers/campaigns_controller.rb
def new
#campaign = Campaign.new
#campaign.items.build
end
Adding Extra Fields Via Ajax
Adding extra fields with Ajax requires engineering a new solution to the issue
The way you do this is to take the f.fields_for text & put it into a partial. This partial can be called from the original view, as well as another view (which we can render through Ajax)
The Ajax part works by basically taking a request from your form (the ajax request), and then using another action in your controller to build a new ActiveRecord object & render another partial that will contain another form. This partial will then call the original f.fields_for partial, allowing you to render another field
Your Ajax can then extract the new field & append it to your page. The way you get around the id issue (keeping the IDs sequential & unique) is to employ the child_index method, and use Time.now.to_i to generate a timestamp
If you read my answer referenced at the top of this answer, all of this will make sense :)
Some great resources for this:
RailsCasts Nested Forms
Adding Fields With Ajax
A nice gem along with tutorial is available from ryanbates who is the author of railscasts.com site.You can use this and have a look at tutorial here
And also if you want to try manually use the fields_for while writing in the form like here and manage some jquery code for add or remove.
Some pseudocode from my app:
User has many Products
User has many Projects
Project and Product belong to User
Furthermore:
Project has one Video
Video belongs to Project
I have a multi-step wizard built using the Wicked gem. In step one I create and save a Project. In step two I add a Video to that Project:
= form_for #project do |f|
= f.fields_for :video_attributes do |v|
= v.file_field :file
Everything works fine, but I'd like to add a Product to the Project's User during this same step. I'm a little confused as to how accepts nested attributes works for this sort of thing.
I imagine I need to do something like this in my wicked controller:
#user = current_user
# wicked makes us use :project_id as it hijacks :id
#project = #user.projects.find(params[:project_id])
#user.products.build
But where do I stick the 'nested attributes for' call? Do I need more than one call to accepts_nested_attributes_for? Would this work?
Make Project model accept nested attributes for User
Make User model accept nested attributes for Product
= form_for #product do |f|
= f.fields_for :user_attributes do |u|
= u.fields_for :product_attributes do |p|
= p.file_field :image
I can't try the code out till tomorrow, but i will sleep better knowing i can solve this when i get to it.
You certainly can extend nested attributes through several objects by nesting the fields_for calls... But you can sometimes get into trouble if you jump back and forth between objects like it looks like you're headed towards here. I've had problems with circular save issues resulting from structures like that. For this reason, I recommend keeping the accepts_nested_attributes_for as a one-way street. So, if a user accepts_nested_attributes_for projects then a project should not also accepts_nested_attributes_for users. Given that, your form has to be built based on the root object. I don't know your project but for mine that was the user. Basically, it's more likely the user would be the central relationship. Hope this helps.
Also, I'm not sure why your fields_for calls are using <some object>_attributes. Unless you're doing something special, those should be relation names like f.fields_for :video. This way the fields_for call loops through each object of that type in the collection.
I am making my own custom view that I need to make the process of creating associated models less painful for my users. I want to display all of the models associated pieces in-line, with controls to edit them. This is quite easy to roll my own for the basic fields, but I'd rather use a form_filtering_select partial for the inline model's associations, but I can't find any documentation to do this.
You can use Nested Form
Consider a User class which returns an array of Project instances from the projects reader method and responds to the projects_attributes= writer method:
class User
def projects
[#project1, #project2]
end
def projects_attributes=(attributes)
# Process the attributes hash
end
end
Note that the projects_attributes= writer method is in fact required for fields_for to correctly identify :projects as a collection, and the correct indices to be set in the form markup.
When projects is already an association on User you can use accepts_nested_attributes_for to define the writer method for you:
class User < ActiveRecord::Base
has_many :projects
accepts_nested_attributes_for :projects
end
This model can now be used with a nested fields_for. The block given to the nested fields_for call will be repeated for each instance in the collection:
<%= nested_form_for #user do |user_form| %>
...
<%= user_form.fields_for :projects do |project_fields| %>
<% if project_fields.object.active? %>
Name: <%= project_fields.text_field :name %>
<% end %>
<% end %>
...
<% end %>
Here goes the Reference for details.
There's a cool gem out there that does pretty much what you want. It's called Nested Form Fields. It allows you to edit records (along with their has_many associations) on a single page. The cool thing about it is that it even uses jQuery to dynamically add/remove form fields without a page reload. Checkout out the gems docs for proper usage. Hope that helps!
Here's my Schema
class Menu < ActiveRecord::Base
belongs_to :menuable, :polymorphic => true
end
class Page < ActiveRecord::Base
has_one :menu, :as => :menuable
end
class Links < ActiveRecord::Base
has_one :menu, :as => :menuable
end
I want to link to a polymorphic class in the Menu view using link_to, e.g.
<%= link_to menu.name, menu.menuable %>
This works, but this retrieves the menuable object from the database, when all I wanted is to generate a link. You can imagine if my menu is large, this will really bog down my application.
When I decared the menuable field as polymorphic, Rails created menuable_type and menuable_id. What can I use to generate a link to the polymorphic page, short of writing a helper function with a giant switch statement (e.g. if I have a large number of menuable 'subclasses'?)
It's been long since the question was asked but I had the same problem recently and the solution was to use polymorphic_url. You need to find the name of the route you need to create a link to, for example "fast_car_path" and make it out of your *_type and *_id from polymorphic table. For example, you have a list of comments and want to make the link to the cars that they belong to. So if *_type = FastCar we have
#comments.each do |comment|
link_to polymorphic_url(comment.commentable_type.tableize.singularize, :id => comment.commentable_id)
which will generate "fast_car_path" without downloading the cars from database.
I am a noob in rails and I dont know how good that advice is, but I hope it will be helpful for somebody.
You could do something like this:
def my_menu_url(menu)
"/#{menu.menuable_type.tableize}/#{menu.menuable_id}"
end
if you use the rails convention for naming the controllers that correspondent to your models.
But don't do it. You work around the routing mechanism of rails and that's simply bad practice.
You should use the :include option in your finders to eager load your menuables:
Menu.all :include => :menuable
In the case this isn't enough you may use some sort of caching.
Another approach could be to use url_for[menu.menuable, menu]. So, the link tag would look like so: <%= link_to menu.name, url_for([menu.menuable, menu]) %>.
you could use polymorphic routes for this
https://api.rubyonrails.org/classes/ActionDispatch/Routing/PolymorphicRoutes.html
<%= link_to menu.name, polymorphic_path(menu.menuable) %>
it will generate html like this
menu.name