I have created a tutorials section and categories section. now i want to create association between these two sections.i have added has_many in my tutorials model.
this is my tutorial models definition:
class Tutorial < ActiveRecord::Base
has_many :tutorialcategories
I got an error "undefined method tutorial category" in show method.
this is my tutorialcategories controller's definition
class TutorialcategoriesController < ApplicationController
respond_to :json
def show
#tutorials=Tutorial.order("id ASC").all
#category = Tutorialcategory.find(params[:id])
Related
I am studying Belongs_to association, I have used following models, in that every order belongs to the customer, so I have used belongs_to in order model it giving error while creating order
undefined method `orders' for #
when I use has_many :orders in customer model it works fine, why it
does not work with only belongs_to
Its work with has_many :orders in customer model but not with
has_one : order in customer controller it giving same above error.
thanks in advance.
Model :- order.rb
class Order < ActiveRecord::Base
belongs_to :customer
attr_accessible :order_date, :customer_id
end
Model :- customer.rb
class Customer < ActiveRecord::Base
attr_accessible :name
end
controller :- orders.rb
def create
#customer = Customer.find_by_name(params[:name])
#order = #customer.orders.new(:order_date => params[:orderdate] )
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render json: #order, status: :created, location: #order }
else
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
Technically, belongs_to will work without a matching has_many or has_one. If, for instance, you say that Order belongs_to :customer, you can call .customer on an Order object, and get a Customer object.
What you can't do is call .orders on a Customer without telling it that it has_many :orders (or .order, in the case of has_one), because that method is created by the has_many declaration.
That said, I can't think of any reason you would ever want to only specify half of a relation. It's a terrible design choice, and you should not do it.
Edit: has_one doesn't create the .collection methods that has_many does. Per the guide:
4.2.1 Methods Added by has_one
When you declare a has_one association, the declaring class
automatically gains four methods related to the association:
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
You'll note that there's no .new on that list. If you want to add an associated object, you can use customer.build_order(), or customer.order = Order.new().
I want to be able to do something like
#foo = MyClass.new
5.times do
#foo.things.build
end
But my #foo needs to have a primary key for this to work, Soo what is the best way to generate primary keys without creating the object?
The purpose for this is to be able to use nested forms more easely
form_builder.fields_for :things do ...
I believe the OP is asking for how to initialize a view action property for use in new action for a standard Rails resource. At this point, there is no ID for the main parent. The solution is simple:
The model:
class ParentObject < ActiveRecord::Base
# the child model in this example is called child_objects
has_many :child_objects, :dependent => :destroy
accepts_nested_attributes_for :child_objects
The controller action for new:
#object = Object.new :example_field => "my field"
#object.child_objects.build :name => "value_1" # pretending that name is a field
#object.child_objects.build :name => "value_2"
Then, in the view:
= form_for(#object) do |f| # top level Object
= f.label :example_field
= f.text_field :example_field
=# the next line loops twice in this example
= f.fields_for :child_objects do |child|
= child.label :name
= child.text_field :name
There is also a good gem called nested_form written by Ryan Bates (https://github.com/ryanb/nested_form) which may help you with the rest of the CRUD operations.
What you probable want is NestedAttributes
Nested attributes allow you to save attributes on associated records through the parent. By default nested attribute updating is turned off, you can enable it using the accepts_nested_attributes_for class method. When you enable nested attributes an attribute writer is defined on the model.
The implementation is different between each ORM, here is for sequel and ActiveRecord
NOTE: Full tutorial also available at Nerdgem
Sequel impementation
Imagine there is a Project class that has many tasks
class Project < Sequel::Model
one_to_many :tasks
end
class Task < Sequel::Model
many_to_one :project
end
To enable the nested attributes you will need include two plugins for the Project class
Sequel::Plugins::NestedAttributes: allows you to create, update, and delete associated objects directly by calling a method on the current object. Nested attributes are defined using the nested_attributes class method:
Sequel::Plugins::InstanceHooks: which is a dependency of NestedAttributes
You can find really good doc on the plugin site
Project.plugin :instance_hooks
Project.plugin :nested_attributes
After that is done you can call the nested_attributes method on the desired class
Project.nested_attributes :tasks
Now you can do this
p = Project.new(:title=>'Project')
p.tasks_attributes = [{:title=>'First Task'}, {:title=>'Second Task'}]
puts p.tasks.inspect
# It will output this
# [#<Task #values={:title=>"First Task"}>, #<Task #values={:title=>"Second Task"}>]
When you save the project it will save both the project and the tasks.
If you can even to edit many tasks at the same.
ActiveRecord implementation
Here is how to use it.
Imagine there is a Project class that has many tasks
Project.rb
class Project < ActiveRecord::Base
attr_accessible :title
has_many :tasks
accepts_nested_attributes_for :tasks
end
Task.rb
class Tasks < ActiveRecord::Base
attr_accessible :title, :project_id
belongs_to :project
end
Now you can do this.
p = Project.new
p.tasks_attributes=[{title: "First Task"}]
p.things
# Would output this
#=> [#<Thing id: nil, title: "First Task", created_at: nil, updated_at: nil, bar_id: nil>]
p.save
When you save the project it will save both the project and the tasks.
If you want to edit many project tasks at the same time you can to this
p.tasks_attributes=[{title: "First Task"},{title: "Second Task"}]
NOTE: there is also a Railscasts that can help you out with nested forms. Orginal Railscast, Revised Railscast
I've got a model setup where a user can create a quiz with many questions and many answers on each question
The models look like this:
model Page < AR::Base
end
model Quiz < Page
has_many :questions
accepts_nested_attributes_for :questions, :allow_destroy => true
end
model Question < AR::Base
belongs_to :quiz
has_many :answers
accepts_nested_attributes_for :answers, :allow_destroy => true
end
model Answer < AR::Base
belongs_to :question
end
And my form looks like this:
= form_for #quiz do |f|
f.fields_for :questions do |qf|
# fields omitted, have fields for id, content, etc
qf.fields_for :answers do |af|
# fields omitted, have fields for id, answer, etc
f.submit 'save'
Everything works wonderfully when I edit just the quiz or when I add new questions and answers, but when I edit existing questions and answers, the changes aren't persisted in the DB. I can see the correct nested parameters being sent into the controller and when inspected the #quiz after calling update_attributes it shows the updated questions and answers but they aren't being persisted after the page is updating.
I've never had this sort of issue before and am having trouble spotting the cause, can anyone share some insight?
Thanks!
As requested, controller code: (Quiz is an STI subclass of Page)
PagesController < ApplicationController
def update
#page = #section.pages.find(params[:id])
if #page.update_attributes(params[#page.type.downcase.underscore])
redirect_to online_course_section_pages_path(#online_course, #section), :notice => "Your page has been updated"
else
render :edit
end
end
end
EDIT:
Found the problem was because of using #page.type.downcase.underscore instead of #page.type.underscore.downcase so update attributes was being passed nil instead of the actual data
Found the problem was because of using #page.type.downcase.underscore instead of #page.type.underscore.downcase so update attributes was being passed nil instead of the actual data
I have nested my resources (see below) and when I try to create a new entity, I get the following error. Does anyone know why I'm getting this error and how to solve it?
undefined method `applications' for nil:NilClass
resources careers do
resources applications
end
Within the 'Applications' controller I have:
before_filter [[:authenticate, :except => :new], :load_career]
def create
# The following line is where the error originates
#application = #career.applications.new(params[:application])
respond_to do |format|
...
end
end
private
def load_career
#career = Career.find(params[:career_id])
end
The Career and Application models have has_many :applications and belongs_to :career respectively.
And the '*_create_applications' migration has a career_id field.
I have never seen before_filters defined that way. I just tried it in Rails 3 and it doesn't seem to do anything. I would give each callback it's own before_filter call:
before_filter :authenticate, :except => :new
before_filter :load_career
I'm trying to write an app in rails 3 and I'm having some trouble figuring out the routes and controllers for a test that I want the user to take. The basic requirements for this app are:
Users, Tests and Questions are all in separate models.
A User has_many Tests. A Test has_many Questions
Provide a link on the user_profile page to /test/new to create the test record.
Provide a link on /test/new to /test/:id/part1 (where :id is the test_id) so that the user can complete the first part of the test. Questions will be retrieved from the db and presented on this page.
Provide a link on /test/:id/part1 to /test/:id/part2 so that the user can complete the second part of the test. Again, questions are retrieved from the db.
Provide a link on /test/:id/part2 to submit the test and return to the user's profile.
I've completed the models, which even pass their tests, so I think I have finished parts 1 and 2.
user.rb
Class User < ActiveRecord::Base
has_many :tests
end
test.rb
Class Test < ActiveRecord::Base
belongs_to :user
has_many :questions
end
question.rb
Class Question < ActiveRecrod::Base
belongs_to :test
end
My issues start when I try to put these models together using routes and controllers.
routes.rb
resources :users
resources :tests do
member do
post 'part1'
post 'part2'
end
end
users/show.html.erb
<%= link_to "Start The Test", new_test_path %>
tests/new.html.erb
<%= link_to "Part 1", part1_test_path(#test) %>
tests_controler.rb
class TestsController < ApplicationController
def new
#test = Test.new(current_user)
end
def part1
# still just a stub
end
end
I'm getting this error when I click on the link to take Part 1 of the test:
No route matches {:action=>"part1", :controller=>"tests", :id=>#<Test id: nil, taken_at: nil, user_id: nil, created_at: nil, updated_at: nil>}
Any help on this would be greatly appreciated.
By defining a member of the routes it's expecting an existent test, ie. one which is saved and has an id.
e.g.
part1_test_path = /test/123/part1
What you need is a collection route.
resources :tests do
collection do
post 'part1'
end
member do
post 'part2'
end
end
e.g.
part1_test_path = /test/part1
edit
Suggested solution:
resources :test, :path_names => { :new => 'part_1', :edit => 'part_2' } *1
def new
#test = Test.new
#new view
form_for #test do
...
def create
#test = Test.new params[:test]
if #test.save
redirect_to edit_test_path #test
def edit
#test = Test.find params[:id]
#edit view
form_for #test do
def update
#test = Test.find params[:id]
if #test.update_attributes params[:test]
redirect_to test_path #test
def show # test results
#test = Test.find params[:id]
if #test.incomplete *2
redirect_to edit_test_path #test
*1 See rails guide on routing. This will give you urls like this
test/part1
test/123/part2
You should put all of your validation in the model; your requirements of test data. Conditional validation will be required, depending on whether it's a new_record? or not ie if you're at part 1 or 2.
*2
add a method to your model which checks test completeness.
def incomplete
self.some_test_field.blank?
Let me know if you don't understand anything.