Rails keep and display model method errors - ruby-on-rails

I have a model in rails for movies and within this model I have a method that parses JSON and adds each movie from that to a database.
My controller so far has an index to list the movies and it has a create action that triggers the model method that parses the JSON.
def create
#movie = Movie.parse_json
if #movie
redirect to root_path
else
render new_movie_path
end
the parse json method just parses each json element and adds it as a new movie.
My main predicament is I think I'm doing things a little wrong and so it's becoming difficult. I want to have it so if for whatever reason a movie can't save - it adds an error and moves to the next. After the JSON has been fully parsed I can then see which movies encountered errors. I've tried doing errors.add(:base, "Movie #{movie.title} can't be saved" .. and then in my movie/new.html.erb having a loop of the errors but #movie is undefined there.
So far to trigger my create action I have a button on my new.html.erb that sends a post request to trigger the create action and so parse the JSON.
<%= link_to "Get New Movies", movies_path, method: :post %>
Which parts am I doing wrong that is making it difficult. I want it so from my new movie page I can click a button and this triggers movie parsing - at the end of parsing if there are errors I can display them - else just redirect to the index page. I could do this if my controller just made a single object but the whole model method parsing json and creating multiple records is the thing that is throwing me off.

Related

How does Rails keep the form data when validations fail?

I have an 'create' action method in Rails and do:
def create
#movie = Movie.new(movie_params)
if #movie.save
redirect_to #movie, notice: "Movie successfully created"
else
render :new
end
end
Now, I have a few validations in place for the Movie model. In case those validations fail, and #movie.save returns false, I simply invoke the new template (without touching the new action, since render :new is the same as render template: 'new'.
I don't understand how Rails can keep the form data I already entered when it again renders that new view. What's going on behind the hood that allows it to do this?
Let's try to understand this whole process point-wise
Instance variables defined in the controller action are shared with the rendered views.
In your case I'm assuming that there's a new action something like
def new
#movie = Movie.new
end
And you have a corresponding view new.html.erb where you have created a form like this
= form_for #movie do |f|
Now, as you know the #movie object that you are passing in form_for method is defined in new action. Most of the times we don't pass any parameters to the new method in new action. The form fields are blank when you load the form because the attributes of the object(in your case #movie) are by default blank because we just initialize an empty object(Movie.new).
Let's assume your Movie model has a name attribute, Try doing this in your new action
def new
#movie = Movie.new(name: 'Hello World!')
end
Now when you will load the new action, you will see Hello World! populated in your name text field because your #movie object is initialized with this value.
Also, keep in mind that Rails Convention-Over-Configuration automatically generates the form URL in this case, by default it points to the create action. When you submit the form the request is made to the create action. This takes me to the next point.
When we submit the form all the filled in form values are sent to the action whose route matches with the form URL(in your case URL points to the create action)
In create action you are receiving parameters in the form of a hash with model attributes(Movie attributes) as keys and the filled in information as their values. The first line in your create action is
#movie = Movie.new(movie_params)
This is a very important line of code, try to understand this. Let's assume your form had only one text field, i.e., name. Now movie_params is a method that looks like this
def movie_params
params.require(:movie).permit(:name)
end
Now, the movie_params method will return a hash something like { 'name' => 'Hello World!' }, now you are passing this hash as a parameter to Movie.new method.
So now, after breaking up the code, the first line of your create action looks like
#movie = Movie.new({ name: 'Hello World!' })
That means your #movie instance variable contains an object of Movie class with name attribute set to Hello World!. Here, when after initialization, if you do #movie.name it will return Hello World!.
Now, in the second line you are calling #movie.save that returned false due to failed validation in your case as you have already mentioned in the question. As it returned false the execution will go to the else part. Now this takes me to the next point.
Calling render :action(in your case render :new) in the controller renders only the view that belongs to that action and does not execute that action code.
In your case, you called render :new, so there you are actually rendering the new.html.erb view in create action. In other words, you are just using the code in new.html.erb and not in new action. Here, render :new does not actually invoke the new action, it's still in the create action but rendering the new.html.erb view.
Now, in new.html.erb you have created a form that looks like
= form_for #movie do |f|
Now as my explained under my first point, the instance variables that are declared in the action are shared by the rendered view, in this case #movie object that you have defined in create action is shared by the rendered new.html.erb in create action. In our case, in create action the #movie object was initialized with some values that were received in the parameters(movie_params), now when new.html.erb is rendered in the else, the same #movie object is used in the form by default. You got the point right, you see the magic here?
This is how Rails works and that's why its awesome when we follow the convention! :)
https://gist.github.com/jcasimir/1210155
http://guides.rubyonrails.org/v4.2/layouts_and_rendering.html
Hope the above examples cleared your doubts, if not, feel free to drop your queries in the comment box below.
form_for helper takes data from #movie variable. In create action forms data assigns to #movie variable. When you call render :new form_for takes column's data from #movie variable.
I'm not sure how deep under the hood you want to go, but basically when you POST to the create method the data is passed to the params, the params being just a key:value pairs where the key and the value are strings, but rails has a special syntax and methods for turning into hashes. params data is passed the Movie model to be processed and the result stored in #movie. When the form is rendered the #movie date is passed back to the form - that data is used to repopulate the form.
I would recommend this blog post and the rails guidefor further reading.
I will try to explain little bit:
in method create first of all we set instance variable
#movie = Movie.new(movie_params)
#movie at this moment has fields filled with movie_params
and after validates brakes we say to Rails 'render :new' with variable #movie.
This is the same if we assign attributes into form:
= form_for Movie.new(movie_params) do ...
When you submit your form. You call create method where all values of movie_params are initializes in #movie. Now due to any reason code break then you call render new for same object (#movie). So form come up with values.
Means in whole process your #movie object persisted.

using rails how do i only show the id submitted via a text box from a table

I've got a table full of information at the moment, Ideally i need the information from a database table to be viewed via a link.
I only have the controller and some standard html (the html is just a h1 tag at the moment)
The HTML will be standard throughout like a template.
The way i'm seeing what i want in my head is the users will get a link which would be events_prev/{{id from DB here}} and depending on the ID the information on the page will be populated from the corrisponsing DB Row
Heres my controller
class Events::EventsPrevController < ApplicationController
def index
#events = Event.where(id: id)
end
def show
render :nothing => true
end
end
Sorry if its super confusing.
Welcome to rails.
Ok, there's a couple of things that will get you in the right directions. Firstly, you REALLY need to do a little reading to understand how the controller and the routes and the views are linked together in rails, that'll help you tons.
But moving on to your specific issues:
Parameters:
All data passed via a url (get, post, put, doesn't matter the method) is available in the controller in an array object called params - So that means when want to access the data the user submitted, you'll use something like
#event = Event.where(id: params[:id])
Routes:
It looks like you're trying to use the index page. In rails index is a RESTful route which generally points to a collection of model objects. The show route will point to an individual object so you should instead make your link point to the show path instead of the index path.
You can view the routes available on a model on a command line using:
bundle exec rake routes
An example of what your routes might look like:
prev_events GET /prev_events(.:format) prev_events#index
POST /prev_events(.:format) prev_events#create
new_prev_event GET /prev_events/new(.:format) prev_events#new
edit_prev_event GET /prev_events/:id/edit(.:format) prev_events#edit
prev_event GET /prev_events/:id(.:format) prev_events#show
PATCH /prev_events/:id(.:format) prev_events#update
PUT /prev_events/:id(.:format) prev_events#update
DELETE /prev_events/:id(.:format) prev_events#destroy
Link
Based on the routing table, you now should see that the link you need your users to click on might look like this (given that event is your object:
<%= link_to event.title, event_path(event.id) %>
or shortcutted
<%= link_to event.title, event %>
View
For the view this is entirely dependent on the data in the Event model. Since the data is stored in #event you'll simple use the attributes on the event model to render the html however use like, e.g.
<h3><%= #event.title %></h3>
<span><%= #event.start_time %></span>
You should read up on Rails controllers: by default the action index is used to show all of the records and what you're talking about should belong to the show action. The default routes take care of the id passing to your show action.
Index action is mean to show list of items in view and Show action is used to show a single item.
what you are doing in index is actually mean to be in show action.
Reason:
#events = Event.where(id: id)
this line will give u a single record in every case it means it should be in Show action.
your code should look like:
def show
#event = Event.find(params[:id])
[your logic: what you want to do with that #event]
end

How to ask two queries from a single controller

I am trying to do everything on a single page. The page is called users/index.html.erb.
On this page I return a simple data base query in json format.
I would like to also return a seperate query in non json format from the same database.
I'm trying to avoid partials for the moment as they make variable handling a bit more complicated that I want for my beginner level.
This is what I have:
class UsersController < ApplicationController
def index
#users = User.including_relationships
#followas= User.find_by name: params[:name]
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
end
but it doesnt work unsurprisingly. How can I get it to work in one action?
Not sure about your purpose of returning two formats of query from a single controller call. It usually happens when you use Javascript heavily (eg. Angular.js). Controller first returns the HTML page including all the HTML template and javascript, then data in JSON is returned in the second round.
So in your users/index.html.erb, you might have something like:
<p ng-repeat='user in users'>{{user.name}}</p>
<script>
// do a AJAX call to retrieve users in JSON format
</script>
Then you will call users/index.json again to retrieve users information in JSON.
In short, your users/index is called twice, once for html, once for JSON.
It might seem unwise to call the same controller method twice just to render different format. In such case, you might consider to embed the JSON data in HTML. In such way, you will only render users/index.html.erb once and parse the data in JSON through Javascript on the client side.

Retriving data from form params is not possible in ruby on rails

In My Ruby on Rails Application
in my controller i have two action methods,first one is for load data and second one for saving data.
at first i wrote two different views for both action methods as follows
def index
-------
end
and corresponding view as follows..
<%= form_tag :action=>:save do %>
-------
<%= text_field_tag templateitem.name %>
------
------
<%= submit_tag "save"%>
<%end%>
like above it consists some input items like radiobutton, checkbox ..etc
The other action method as follows ,in this all data read from view and saved into database
def save
--here i read the parameter values as follows
templateItem.value=params[templateItem.name]
end
then i wrote a view for saved data its works fine.
but my requirement is the saved data load in same index page for that i included all the code i written in save view is included in index page then the save action method is changed as follows
def save
--here i read the parameter values as follows
templateItem.value=params[templateItem.name]
--- saved into database
return redirect_to :action => 'index',:encounterid=>1
end
then its not worked , i found the problem here is there is no data reading from view but it redirects to next view as i mentioned above action method, i didn't find solution for this problem please help me to solve this..
params are only passed. After redirecting they are destroyed. So they are not accessible in your index action

Rails 3.1 nested forms validation

I'm using Formtastic 2 for a nested form - I have a menus and a meals model, menus have many meals, every meal belongs to one menu.
I added the meal form to the menu show action, right below a list of already associated meals.
Creating meals works fine if validation succeeds, I forward to the menu show action again listing the created meal in the list.
But when the meals doesn't get validated and I forward to the menu show action again with an appropriate flash message, I would really like to fill the form with the data that was submitted before and rendering the errors next to it.
I tried with this redirect:
redirect_to(menu_path(menu,#meal), :alert => 'The meal was not created')
But I can't get at the meal variable and passing it back to the form this way, the request itself is a GET request with only the menu id.
You shouldn't redirect after validation errors, because you'll lose all state. Just the old template directly after a failed validation. A little gotcha is that you need to use flash.now[:alert], so it won't carry over to the next page.
Usually you'll have this structure:
def new
#meal = Meal.new
end
def create
#meal = Meal.new(params[:meal])
if #meal.save
flash[:notice] = "Meal was created"
redirect_to menu_path(menu, #meal)
else
flash.now[:alert] = "The meal was not created"
render :new
end
end

Resources