render a child & parent objects to a specified template - ruby-on-rails

I have two models, a Charity model and a Milestone model. A charity has_many milestones.
To keep the interface a bit simpler, I'm putting the small milestone form on the show view of the Charity controller. This is fine, but when the charity model fails to save, how do i go back and render those objects with the show action so I get their invalid state?
Here's my create action on the milestones controller. Presently I'm getting a model_name error for nil, meaning that the instance variables are likely not set.
def create
#charity = Charity.find(params[:charity_id])
#charity.milestones.build(params[:milestone])
if #charity.save
redirect_to #charity, notice: "Milestone added"
else
render 'charities/show'
end
end
I know you can normally do just render action :new, but since this is across controllers, it does not work. Ideas?

Forgot to set the #milestone instance variable. Silly mistake. Hope this helps someone down the road.
def create
#charity = Charity.find(params[:charity_id])
#milestone = #charity.milestones.build(params[:milestone])
if #charity.save
redirect_to #charity, notice: "Milestone added"
else
render 'charities/show'
end
end

Related

What is the proper way to make an increment function work?

I'm creating a website that let people read short stories in several chapters.
For this, I nested a chapter scaffold inside a novel scaffold, and linked (novel has_many :chapters, chapters belongs_to :novel) them both together.
However I'm trying to get the chapter's number inside the URL (instead of the id, which never decreases). Setting the chapter as is is not a problem, but I'd like to automate the chapter number without my users needing to add it themselves.
To do so, I figured that all I needed was to get how many chapter the current novel had by checking self.class.where(:novel_id => #novel).count. At this point, I have no issues, however it gets complicated when I try to increment this number, I get the error: undefined method 'anoter_one' for 0:Fixnum
Here is my "another_one" function inside my model (I tried some things)
def another_one
#number = self.class.where(:novel => #novel).count.to_i
#number.increment
end
Here is the controller
def create
#novel = Novel.find(params[:novel_id])
#chapter = Chapter.new(chapter_params)
#chapter.chapter_number.another_one
#chapter.novel = #novel
if #chapter.save
redirect_to novel_chapter_path(#novel, #chapter), notice: 'Chapter was successfully created.'
else
render :new
end
end
What am I doing wrong ?
Thank you in advance
Your calling anoter_one - which is a misspelling of another on the value of #chapter.chapter_number - not the model.
One way to solve this is by using an association callback:
class Novel
has_many :chapters, before_add: :set_chapter_number
def set_chapter_number(chapter)
if chapter.chapter_number.blank?
chapter.chapter_number = self.chapters.size + 1
end
end
end
In order for the callback to be called properly you want to build the associated items off the parent:
def new
#novel = Novel.find(params[:novel_id])
#chapter = #novel.chapters.new
end
def create
#novel = Novel.find(params[:novel_id])
#chapter = #novel.chapters.new(chapter_params)
if #chapter.save
redirect_to [#novel, #chapter], notice: 'Chapter was successfully created.'
else
render :new
end
end

Ruby redirect to same page and show errors if form input is bad

Forgive me I'm new at Ruby. I am trying to create a site to contain information about a zoo.
I have an enclosure model and an animal model.
This is my code for the create method in animal_controller
def create
if params.has_key?(:enclosure_id)
#enclosure = Enclosure.find(params[:enclosure_id])
#animal = Animal.new(animals_params)
#animal.user_id = current_user.id
if #animal.save
#enclosure.animals.push(#animal)
redirect_to enclosure_path(#enclosure)
else
# ????
end
else
#animal = Animal.new(animal_params)
#animal.user_id = current_user.id
if #animal.save
redirect_to #animal
else
render 'new'
end
end
end
I then have two places where a new animal can be created. One is using a form at localhost:3000/animals/new. The other is using a similar form on the show page of a particular enclosure, so for example at localhost:3000/enclosures/1/
In my code above, I check for the presence of enclosure_id to determine where the call is coming from. If the parameter is found, I add the animal to the enclosure there and then. However, if #animal.save fails, I do not understand how I can return to the localhost:3000/enclosures/id page with the validation error messages being passed. In the case of no enclosure_id, render 'new' takes the user back to the ../animal/new page with error messages passed as well.
Thanks
I don't think it's a good idea to go to an other page, because you will have to serialize errors linked to the model and it's gonna be complicated and ugly.
I think you should render the show page of the enclosure and then display the errors
#....
if #animal.save
#enclosure.animals.push(#animal)
redirect_to enclosure_path(#enclosure)
else
render 'enclosures/show'
end
#....

Getting "undefined method for Nil Class" error when calling the reciprocal model method

I currently have two models School and Course where School has_many courses, and Course belongs_to school. Additionally, School and Course are nested resources, where School is the parent resource, and Course the child.
I have created several test records in the Rails Console so that a query such as when the child calls upon the parent Course.first.school successfully executes and returns all the relevant information of the school Course.first is associated with.
However, when put into a controller function, I would instead get an error "undefined method `school' for nil:NilClass" for the following line:
redirect_to school_course_path(#course.school, #course)
.. as if the .school part wasn't recognized (where as it was in the console). Why is this the case, and how do I get past this error? Thanks!
Edit - as suggested, it could be that my #course instance variable isn't passed from method to method in the controller. I have attempted at passing them through via a private method, but its still giving me the same error. Here is my code (background: the model Question belongs_to Course, with Course having many questions. Course isn't part of the nested routes)
class QuestionsController < ApplicationController
def new
#course = Course.find(params[:course]) #confirmed working
self.current_course = #course #I attempt to set current_course, a private method
#question = Question.new
end
def create
#question = Question.new(params[:question]) #also works, in rails console all the questions confirms to have rails id
if #question.save
redirect_to school_course_path(current_course.school, current_course) #source of my frustrations - continues to returns same error message
else
render 'new'
end
end
private
def current_course=(course)
#current_school = course
end
def current_course
#current_course
end
end
Should work if your relationships are set up the way I think they are:
def create
#question = Question.new(params[:question])
#course = #question.course
if #question.save
redirect_to school_course_path(#course.school, #course)
else
render 'new'
end
end
Make sure you have something like this in your create action:
#course = Course.new(params[:course])
your code is okay, it seems there is problem in your redirect.. redirect it to root_path and check whether it is working??

Give a detailed illustration of how the MVC works in Rails, and how the controller communicates with forms etc?

Perhaps this can even become a Community Wiki, but I would love a detailed description of how the controller works - or rather, how I can get it to do what I want it to do.
I understand the general structure of MVC and how the model stores the db structure, and the controller interacts with the db and passes info to the view.
However, I am puzzled (on a fundamental level) about how to accomplish simple tasks using my controller. I know that if I want to create a new record for a model/object, I just do object = Object.new(:name => "Object Name") in the Rails console.
But how on earth would I do that in the CRUD elements of the controller and why?
Please use a simple example - e.g. showing a user the balance of their bank account (I know there are many complexities surrounding this, but ignore them for the sake of this explanation). What would the model look like (just include: Name, Address, Transaction Type (Deposits/Withdrawals), Balance).
What would a view look like? What would the controller look like? Any choices you make (like using a form) please explain them. Why would you use a form, as opposed to a drop down menu and (in layman terms) how does the form or drop down menu interact with the controller? How do I get the info captured there to the db and why am I doing it that way?
I know this sounds like a lot to ask, but I have done RailsTutorial.org, watched many Railscasts, read the Rails guides, and read many other tutorials and still have some basic gaps in my understanding of the way Rails works and why.
Thanks in advance.
I don't know how much more help I can be, but I understand your pain having just come to rails myself. The article recommended by ghoppe, "Skinny Controller, Fat Model" explains the function of Ms Vs & Cs nicely. Seeing as that does not fully answer your question I will try to explain the mechanics of each structure.
Model
class Account < ActiveRecord::Base
belongs_to :user
validates_presence_of :address
def name # Account does not have a name field, but User does so I will make a name method for Account and feed it name of the user it belongs to.
user = self.user # Account gets the user method with the <belongs_to :user> association
# note: Rails expects Accounts to have a user_id field so it can perform the "magic" to associate Accounts with Users
if user.name
return user.name
else
return nil
end
end
end
The model describes your object. Like an object in any OOP language you want to put all of your object logic here. This includes the rails helpers for association(has_one, belongs_to, ...) and validation, as well as any other method or library you want the object to be able use throughout your Models Views and Controllers.
Controller
class AccountsController < ApplicationController
before_filter :name, :only => :edit, :destroy # #account.name will be executed before the edit or destroy method(action) can be invoked on #account. If the user who has the account has a name the action will execute.
def index # This is a RESTful action and is mapped by Rails by default to an HTTP GET request. Rails expects an index.html.erb or index.haml.erb or index.something in the Accounts view to map this action to.
#accounts = Account.all # #accounts is an instance variable and will be accessible in the view this action is mapped to.
end
def show
#account = Account.find(params[:id]) # params[:id] is passed to the controller from the view. The params hash is the primary tool form moving data from a form or URL into a controller. Anytime you click on the link_to the show or edit action of an object Rails will put that objects id in the params hash and call the appropriate action in that objects controller. If you click the show link on an account it will call this action. Now the instance variable in the view show.html.erb will hold a single account instead of an array
end
def new
#account = Account.new # This initializes a new account with all the fields set to blank unless you specified a default in your migration. This account has not been save to the db yet. It is ready for a user to fill in.
respond_to do |format| # Rails can automatically respond differently to different client request. If a client i.e browser wants HTML rails responds with HTML. If a client e.g. an API want XML Rails responds with XML.
format.html # new.html.erb #
format.xml { render :xml => #account }
end
end
def edit
#account = Account.find(params[:id]) # Same as show, but mapped to a different view
end
def create # Finally we have a POST. All the prior actions were GETs, but now we are saving some data to the db.
#account = Account.new(params[:account]) # The :account key is special. It is a hash of hashes. It is populated by the form fields in new.html.erb. To access a specific field such as address we say <params[:account][:address]> and whatever the user entered in the address field in the View is at out fingers in the Controller.
respond_to do |format|
if #account.save # If the validations pass and the account gets saved redirect to the show page of the new record, otherwise refresh/render the new page (hopefully showing what error caused the record to fail to save).
format.html { redirect_to(#account, :notice => 'Account was successfully created.') }
format.xml { render :xml => #account, :status => :created, :location => #account }
else
format.html { render :action => "new" }
format.xml { render :xml => #account.errors, :status => :unprocessable_entity }
end
end
end
def update # This is another of the seven RESTful Rails actions and results in a PUT request because you are updating an existing record
#account = Account.find(params[:id])
respond_to do |format|
if #account.update_attributes(params[:account])
format.js # Rails can also respond with JavaScript. Look up UJS. Rails 3 has made large improvements here.
format.html { redirect_to(#account, :notice => 'Account was successfully updated.') }
format.xml { head :ok }
else
format.js
format.html { render :action => "edit" }
format.xml { render :xml => #account.errors, :status => :unprocessable_entity }
end
end
end
def destroy # This results in a DELETE
#account = Account.find(params[:id])
#account.destroy # destroy is a more thourough delete and will check the options of this records associations and destroy the associated objects as well if they are dependant on this object. The option <:dependant => :destroy> is not set for this object's only association: User. The user this account belongs to will therefore survive the destruction of this account.
respond_to do |format|
format.html { redirect_to(accounts_url) }
format.xml { head :ok }
end
end
end
View
Hopefully you can draw your own logic from here. The view is designed to render information passed as instance vars from a controller to a client: browser, api, smart phone. As well as to pass information from a client to the controller via the params hash. No complicated logic should get performed in a view even though a view with erb has the capability to execute any ruby code.
If an example view would also be helpful I am happy to oblige.
The best description of what the controller is:
http://edgeguides.rubyonrails.org/action_controller_overview.html
http://edgeguides.rubyonrails.org/routing.html
The controller doesn't communicate with the Database. The controller talks to the model, which then communicate with the database.
When I was starting I found very useful to use scaffolding and just looking at what was created.
Do this:
rails generate scaffold Post name:string title:string content:text
Examine all files under the app/ folder. Examine the file config/routes
Then comment here your specific questions.
At first, I thought this question was far too broad, along the lines of "how do I program?" But after reading your comments, I see what you're getting at. You don't quite grasp how MVC works in Rails and are wondering where your code goes.
What you should strive for is a Skinny Controller and a Fat Model. Keep logic out of views. So in your example, you calculate the account balance in the Model, and pass that information along (using the controller) to the view.
For a concise explanation for beginners with sample code, I recommend this article over here.

Writing proper validation errors?

Update : Gutted entire question with more thorough description
Ok same question with different names.
In my model, I do validate the presence of.
class QuickFact < ActiveRecord::Base
belongs_to :organization
validates_presence_of :quick_fact, :content
But if either is blank, it errors out with :
Missing template organizations/_quick_fact_fields.erb
Here's the catch. I have a nested form model with dynamically addable parts to it. As followed from here :
http://railscasts.com/episodes/197-nested-model-form-part-2
That is what generates and calls the _quick_fact_fields.erb . But that works perfectly and is located within quick_facts/_quick_fact_fields.html.haml
Update: My Controller Code
organizations_controller.rb
def update
if #organization.update_attributes(params[:organization])
..
elsif params[:organization][:quick_facts_attributes]
flash[:notice] = 'QuickFacts successfully updated.'
redirect_to organization_quick_facts_url(#organization)
else
flash[:notice] = 'Organization was successfully updated.'
redirect_to :action => 'edit'
end
else
# re-render last form
..
elsif params[:organization][:quick_facts_attributes]
render :template => "quick_facts/index"
else
render :action => 'edit'
end
end
end
It seems that you're trying to render a my_custom_field partial from one of the worker views found in app/views/worker, but apparently there's no such partial there. If you show us the code of the relevant views and controllers, it will be easier to pinpoint the exact problem.
On a side note, you could simply do validates_presence_of :name instead of defining a custom validation method to simplify your model. However, this is likely unrelated to the error you're describing and is just a general improvement suggestion.
Got it. I had two controllers.
quick_facts_controller.rb, and organizations_controller.rb
Once I deleted the update function in quick_facts_controller, it worked properly.

Resources