The title may be too general, but I hope someone can help me.
The scenario is the following:
- I have a model Types, TypesActivity and Activity (and the relation is many to many)
- After create the type (that only consists of a name), I then have to assign activities to that type and in order to do that I have in another view (form) 2 sections:
1) With a select_tag of the types (that shows the name)
2) A list of checkboxes that display all of the activities
I'm showing the checkboxes using this on the form view
_form.html.erb
<% #activities.each do |a|%>
<li><%= check_box_tag "act[]", a.id, false%> | <%= a.name%></li>
<%end%>
and in my controller
def create
params[:act].each do |a|
TypeActivity.create({:type_id => params[:resource][:type_id], :activity_id=>a})
end
redirect_to "somewhere"
end
And everything works fine, but I have a question about... how to update it?
Is this correct? (or is there another better way?)
def update
_v = TypeActivity.find(params[:id]).type_id
params[:act] do |a|
TypeActivity.update_attributes(:type_id=>_v,:activity_id=>a)
end
end
Also if I want to edit one TypeActivity (type_activities/edit/3) I wanted to show the checkboxes that were already selected, and I've done this
def edit
#t = TypeActivity.find(params[:id]).type_id
#activities = Activity.all
end
and in my view
_form.html.erb
<% #activities.each do |a|%>
<li><%= check_box_tag "act[]", a.id, !TypeActivity.where(:type_id=>#t,:activity_id=>a.id).empty?%> | <%= a.name%></li>
<%end%>
Is that a good way?
Thanks in advance to everyone that read all of it =)
JavierQQ
If the type_activity table only contains type_id and activity_id then you are better off not creating a TypeActivty model at all.
Treat type_activity as a link table and configure a has_and_belongs_to_many association in the Type and Activity models. Then use the TypesController to manage the assignment of Activities to a Type.
Reading Materials
Rails Guide on associations:
http://guides.rubyonrails.org/association_basics.html#the-has_and_belongs_to_many-association
Railscast #17 - HABTM Checkboxes:
http://railscasts.com/episodes/17-habtm-checkboxes
Related
I'm attempting to make an invoice application. Here are my models which are related to my question:
UPDATE: Model information has changed due to recent suggestions
Invoice
> id
> created_at
> sales_person_id
LineItem
> id
> invoice_id
> item_id
> qty_commit (inventory only)
> qty_sold
> price (because prices change)
> ...etc
Item
> barcode
> name
> price
> ...etc
Invoice has_many items, :through => :line_items. Ditto for Item. What I want to do is that when I create a new invoice, I'd like the form to be populated with all available Items. The only time I don't want all items to be populated is when I'm viewing the invoice (so only items which exist in the LineItems table should be retrieved). Currently - and obviously - a new Invoice has no items. How do I get them listed when there is nothing currently in the collection, and how do I populate the form? Also I'd like all products to be available when creation fails (along with what the user selected through the form).
UPDATE: I can create items through the controller via the following:
#invoice = Invoice.new
# Populate the invoice with all products so that they can be selected
Item.where("stock > ?", 0).each do |i|
#invoice.items.new(i.attributes)
end
This is of course my crude attempt at doing what I want. Visually it works out great, but as predicted my form id's and such are not playing well when I actually attempt to save the model.
LineItem(#37338684) expected, got Array(#2250012)
An example of the form:
# f is form_for
<% #invoice.items.group_by{|p| p.category}.each do |category, products| %>
<%= category.name %>
<%= f.fields_for :line_items do |line_item| %>
<% for p in products %>
<%= line_item.hidden_field :tax_included, :value => p.tax_included %>
<%= p.name %>
$<%= p.price %>
<% end %>
<% end %>
<% end %>
First of all, if you explicitly want to have a join model with additional attributes in it, you should use has_many :through instead of has_and_belongs_to_many. See the RoR Guide to the differences of the two.
Second, there is no single solution for what you want to reach. I see there two typical usages, depending on the mass of possible instances, one is better than the other:
Use radio buttons to select (and deselect) where a relation should be created or deleted. See the railscast #165 how to do part of that.
You could use select menus with a button to add a relation. See railscast #88. The added relation could be shown in a list, with a delete button nearby.
Use token fields (see railscast #258) to autocomplete multiple entries in one single text entry field.
In all the situations, you normally have to check at the end, if
a relation should be deleted
kept
or created
I hope some of the ideas may show you the right solution for your problem.
I want to show fields from the database dynamically.i.e, if suppose
in one case my #user object reads from users table
#user = User.new
in another case #user object reads from posts table
#user = Post.new
Then dynamically my view(new page) shows the fields based on the model selected.
<% for column in #object.class.column_names %>
<%= #object.send(column) %>
<% end %>
EDITED (To exclude some columns from the view)
exclude_columns = ['id', 'created_at', 'updated_at']
<% for column in #object.class.column_names
next if exclude_columns.include?(column) %>
<%= #object.send(column) %>
<% end %>
This might be more than you are asking for, but perhaps you want to have a look at the presenter pattern. This will let your view make use of one single interface while hiding away the view logic.
I think this post will give you a nice introduction to the topic: http://mikepackdev.com/blog_posts/31-exhibit-vs-presenter
I'm going to disagree with Salil. you should never do assigns in the view (ERB). If you're doing that, you need to move towards a helper or a presenter depending.
Why would you not just walk the tree? #users should, by name, return a collection of users. #user.posts would walk the relationship tree. I'm curious why you're fighting the rails default way of working. Can you share more code to help us understand?
Im trying to create a system which allows athletes to respond to their coaches traing plan, to do this i have allowed the coach to create contentsm however i am using a blogging based system to create it... at the moment the page displays like so
CONTENT TITLE
Content info 1...
Content info 2...
Content info 3...
COMMENTS...
comment 1
comment 2
comment 3
.etc
However i want to set it so that there can only be 7 Comments Max per post as well as set out like this per post...
CONTENT TITLE
Content info 1...
comment 1
Content info 2...
comment 2
Content info 3...
comment 3
.etc
I realise this is probably not the best way to do want i want, but it works (just dosnt appear in the place i want it to)
I did do experiments with creating more models, but kept getting errors whenever i tryed to run more than 1 comment system per post. I was wondering if i could have some help in sorting this out, or any methods i could do to make this easier, or even better if the models would work and if i was just doing something wrong?? tell me if this isn't enough information to go off, and ill try provide some more! Thankyou
.
.
EDIT:
The models i have used are
Program - As in the training plan set for the week
Coaches - The coach that is inputing the data to the rider
Riders - To comment on the coaches data with their own data.
I am unsure what files are need exactly so i have included the link to the github page i am pushing to ( https://github.com/effectonedesign/coacheasy1 ), if there is any other info needed, please let me know!
I like what "mind" has said however, i have done everything have said, in my def show (program controller) it is saying there is an error and i keep getting this message undefined method `coaches' for nil:NilClass everything is identical to his but im getting issues, i really do appreciate the help! Thanks
I would probably create 3 models for the above, TrainingPlan, Section (or content, text_block etc.) and Comment.
Then do the following
TrainingPlan has_many :sections
Section belongs_to :training_plan
Section has_one :comment (if you allow only 1 comment per section, otherwise use has_many)
Comment belongs_to :section
Now, to achieve the formatting you wanted do the following in your views:
<% #training_plan.sections.each do |section| %>
<%= section.text %>
<%= section.comment.text %>
<% end %>
If you allow multiple comments:
<% #training_plan.sections.each do |section| %>
<%= section.text %>
<% section.comments.each do |comment| %>
<%= comment.text %>
<% end %>
<% end %>
Form for comments
I haven't tested the following, so you might need to tweak some parts.The training plan controller:
def show
# using includes will query the database 3 times only (once for each table) rather than
# querying it 1 + N + N (in this case 7 sections, 7 comments possibly, so 15 times)
#training_plan = TrainingPlan.includes(:sections, sections: :comment).find(params[:id])
#sections = #training_plan.sections
#sections.each do |section|
# only build a new comment if there is no comment for that section already
section.build_comment unless section.comment
end
end
In your view views/training_plans/show.html.erb
<%= #training_plan.title %> # or whatever
<% #sections.each do |section|
<%= #section.content %>
<% if section.comment %>
<%= section.comment.content %>
<% else %>
<%= render 'comments/form', comment: section.comment %> # or wherever you have the form
<% end %>
<% end %>
views/comments/_form.html.erb
# This might break if you have a separate comment action somewhere which passes an
# instance variable #comment to the form
<%= form_for comment do |f| %>
# normal form stuff
<% end %>
If that all works then on your training plan show page you should see each section, and if it has a comment then that comment will be rendered, otherwise a form will be shown.
Depending on your routes you might need to run rake routes and see where your comment create action is, and then pass that to the form <%= form for comment, url: some_url_helper_here do |comment| %>
If it was me I would create the add comment part through JavaScript, sort of like in this railscast, but since you're new to RoR I've tried to keep it simple.
I'm attempting to make an invoice application. Here are my models which are related to my question:
UPDATE: Model information has changed due to recent suggestions
Invoice
> id
> created_at
> sales_person_id
LineItem
> id
> invoice_id
> item_id
> qty_commit (inventory only)
> qty_sold
> price (because prices change)
> ...etc
Item
> barcode
> name
> price
> ...etc
Invoice has_many items, :through => :line_items. Ditto for Item. What I want to do is that when I create a new invoice, I'd like the form to be populated with all available Items. The only time I don't want all items to be populated is when I'm viewing the invoice (so only items which exist in the LineItems table should be retrieved). Currently - and obviously - a new Invoice has no items. How do I get them listed when there is nothing currently in the collection, and how do I populate the form? Also I'd like all products to be available when creation fails (along with what the user selected through the form).
UPDATE: I can create items through the controller via the following:
#invoice = Invoice.new
# Populate the invoice with all products so that they can be selected
Item.where("stock > ?", 0).each do |i|
#invoice.items.new(i.attributes)
end
This is of course my crude attempt at doing what I want. Visually it works out great, but as predicted my form id's and such are not playing well when I actually attempt to save the model.
LineItem(#37338684) expected, got Array(#2250012)
An example of the form:
# f is form_for
<% #invoice.items.group_by{|p| p.category}.each do |category, products| %>
<%= category.name %>
<%= f.fields_for :line_items do |line_item| %>
<% for p in products %>
<%= line_item.hidden_field :tax_included, :value => p.tax_included %>
<%= p.name %>
$<%= p.price %>
<% end %>
<% end %>
<% end %>
First of all, if you explicitly want to have a join model with additional attributes in it, you should use has_many :through instead of has_and_belongs_to_many. See the RoR Guide to the differences of the two.
Second, there is no single solution for what you want to reach. I see there two typical usages, depending on the mass of possible instances, one is better than the other:
Use radio buttons to select (and deselect) where a relation should be created or deleted. See the railscast #165 how to do part of that.
You could use select menus with a button to add a relation. See railscast #88. The added relation could be shown in a list, with a delete button nearby.
Use token fields (see railscast #258) to autocomplete multiple entries in one single text entry field.
In all the situations, you normally have to check at the end, if
a relation should be deleted
kept
or created
I hope some of the ideas may show you the right solution for your problem.
I have a form where I'd like to create a parent record and a child record at the same time. For a simple example let's say its a Company with the first Employee.
in my controller I do something like:
def new
#company = Company.new
#company.employees.new
end
and in my view this:
<%= form_for(#company) do |form| %>
<div>
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<%= form.fields_for :employees do |employee_form| %>
<div>
<%= employee_form.label :name %>
<%= employee_form.text_field :name %>
</div>
<% end %>
<% end %>
and back in my controller again:
def create
#company = Company.new(params[:company])
#company.employees << Employee.new(params[:company][:employees_attributes]["0"])
# save stuff
end
Question 1:
I couldn't get the employee collection on the company to be populated with the single employee created in the form. When I looked at the params I found the [:employees_attributes]["0"] stuff.
What I have works, but is there a cleaner way to do this?
Question 2:
If the validation doesn't pass for the employee I get a generic "Employees is invalid" instead of the Name required validator message. I get I am calling save on the collection and rails is doing its best to bubble a validation error up, but is there a cleaner way to do this so I can get the errors specific to the employee?
In Short
How can I clean this up so the related models are created automatically from the params, and so that I get the validation messages for a single employee.
Thanks for looking.
1) fields_for arranges for the child objects attributes to be nested inside the parent objects attributes in the params hash that gets sent back to the controller action. To get Rails to automatically update the child objects tell the parent model to accept nested attributes using the accepts_nested_attributes_for declaration.
2) There is an errors object for every ActiveRecord object. Loop through the errors list and display the messages.
Best way to achieve this is to create a partial and a view helper method that will take render the errors for you. then replace the generated errors messages in the forms with a call to your render_error_messages method. You have all the code to do this already in the generated forms. You just need to refactor that code into a partial, create the helper - which should accept an array of model names as a parameter then do what you want with the info. Wither render a partial for each model or render a partial that will deal with child objects as well as the parent object. Totally your call.
3) Change your new action to build rather that create a new child object so instead of
def new
#company = Company.new
#company.employees.new
end
do this
def new
#company = Company.new
#company.employees.build
end
4) Watch those Railscasts to see how accepts_nested_attributes works
http://railscasts.com/episodes/196-nested-model-form-part-1
and
http://railscasts.com/episodes/197-nested-model-form-part-2
Update
So how does the above information leave you in relation to your questions.
1) What I have works, but is there a cleaner way to do this?
You've fixed the new action as per point 3 above right? Now your create action can look like this
def create
#company = Company.new(params[:company])
# save stuff
end
Which is much cleaner as it has reverted to the original generated create action.
You may not think that's much of an update and therefore not that much cleaner. Well in isolation you'd be right. But consider that you could add as many relationships as you like ad add as many fields_for declarations as you like nd you could turn the user -> employee relationship into a has_many (I know that you wouldn't). You could do all that and your create and update actions stay EXACTLY the same and that's why it's cleaner.
2) is there a cleaner way to do this so I can get the errors specific to the employee?
Given my response in point 2 above you know that there is an errors object on the employee object as well as on the user object right? You also know now that you can loop through that errors object to get the messages right?
So you could do this
<% if #user.employee.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.employee.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.employee.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
At the risk of repeating myself I'll just say that you should refactor your error messages view code into a partial that will take any object as a parameter then you can call it from any view thus enabling you to change the styling and the functionality for all your forms.
Hope that's clearer