How to create nested model from partial Rails 6 - ruby

Newbie Here!
I'm unsuccessfully trying to submit a form of a nested model.
Aim:
Verify a translation by submitting a boolean to a reviews model to associate whether a translation is verified or not, with translation entry and user model associated references.
# routes.rb
resources :entries do
resources :reviews
end
# entry.rb
belongs_to :user
has_one :review
accepts_nested_attributes_for :review
# user.rb
has_many :entries
has_many :reviews
# review.rb
belongs_to :user
belongs_to :entry
From entry index, pass the entry instance to partial, works perfect
# /entries/index.html.erb
<% #entries.each do |entry| %>
...
<%= render 'reviews/new', entry: entry %>
...
<% end %>
Unsuccessfully setting up new/create form.
What happens is that the entry instance is well received, but I am failing to create a new model instance for review belonging to entry.
entry.review raises an nil error for review, while entry is fine when testing through browser console
First argument in form cannot contain nil or be empty
# reviews/_new.html.erb
<span>
<%= form_for entry.review do |f| %>
<div class="form-check form-switch">
<%= f.check_box :verified, class: "form-check-input" %>
</div>
<%= f.submit class: "btn btn-primary"%>
<% end %>
</span>
Another attempt was also to use just #review from the controller but that doesn't obey nested routes.
My controller looks like this
# reviews_controller.rb
def create
#entry = Entry.find(params[:entry_id])
#review = #entry.review.build(review_params)
#review.user_id = current_user.id
#review.save
end
private
def review_params
params.require(:review).permit(:verified, user: current_user, entry: #entry)
end
Am I suppose to implement my actions in the entries_controller?
I have also found the tutorial here useful but replication was unsuccessful.
Another StackOverflow reference here
I still get the error entry.review.build that review is nil.
First argument in form cannot contain nil or be empty

When building an associated record over a has_one relation, instead of
#review = entry.review.build(review_params)
you need to use the following:
#review = entry.build_review(review_params)
See the documentation for more details.
Am I suppose to implement my actions in the entries_controller?
It depends on what you're after. If you have a dedicated form for adding a new review and it is not embedded in another form for creating or updating an entry then implementing a create action in ReviewsController is the straightforward solution – in this case you should also not need accepts_nested_attributes_for in Entry.
If, however, you want to be able to create or update an entry as well as its review using the same form, then you should nest the review form in the form of the entry, keep accepts_nested_attributes_for, and use actions in EntriesController. The documentation should get you started there.

Related

How to create multiple 'new' pages within a controller that behave the same with different text?

Ok - I have a simple application built that allows you to store action items based on business categories: Business Admin, Business Development, Business Financial, Etc.
Each one of the categories, will have the same exact form:
<div align="center"><h1>What are your important Business Admin Action Items?</h1>
<%= form_for #category do |f| %>
<p>
<p>Store Answer Below:</p>
<%= f.text_field :name, :size => 40, :style => 'height: 40px' %>
</p>
<p>
<%=f.submit 'Save action item' %>
</p>
<% end %> </div>
However, not all of these categories will have the same H1 tag (or other text/attributes within the view). So, how does that work?
Do I create new pages, that are: business_admin.html.erb, business_development.html.erb etc - and if so, I can't see how I'd use the same new action for all of them, especially since the answers to the user input are going to be stored on separate pages (the categories' respective pages: business_admin_storage.html.erb, etc.
I understand that naming the urls in that fashion may not be the best way, nor a conventional way. That's part of my question as well. How should I approach this situation. Will be trying a whole host of stuff in the meantime :) If I need to be more clear, please tell me.
New to rails. Loving every second.
Categories controller
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
#category.save
end
def show
end
def destroy
end
private
def category_params
params.require(:category).permit(:answer)
end
end
Index View
<h1>Select A Business Category To Begin Identifying Action Items</h1>
<ol><li><%= link_to 'Business Admin', new_category_path %></li><br><br>
<li><%= link_to 'Business Development/Marketing', new_category_path %></li><br><br>
<li><%= link_to 'Financial', new_category_path %></li>
</ol>
<%= link_to 'Store random action items', new_facilitate_path %>
Routes.rb
Rails.application.routes.draw do
resources :facilitates
resources :categories
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
root 'categories#index'
get 'show' => 'facilitates#show'
get 'index' => 'categories#index'
get 'business_admin' => 'categories#new'
SHOW
Stored !!
<h1>Listing stored action items for this category... </h1>
I can't see your model for Categories but it seems like you're varying the page served based on the name of the category?
Wouldn't something like this cover you?
<div align="center">
<h1>What are your important <%= #category.name %> Action Items?</h1>
</div>
You would need to pass the relevant #category to the relevant view via the controller, here is the Rails 4 way:
before_action :set_category, only: [:show, :edit, :update, :destroy]
...
def show
end
...
private
def set_category
#category = Category.find(params[:id])
end
And your Categories model would need to have a name property defined, which if you don't have it already could be accomplished by a migration.

Not showing created blog entries

I'm fairly new to Rails and learning to create a blog using this tutorial. On step 10, once I define create and show, after creating a new post in browser I don't see any entries on show with id page. All I see is heading and and blank title and post header.
Following is my controller -
class PostController < ApplicationController
def index
end
def new
end
def create
#post = Post.new(params[:posts])
#post.save
redirect_to #post
end
def show
#post = Post.find(params[:id])
end
end
Show view ---
<h1>Show a post</h1>
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Text:</strong>
<%= #post.text %>
</p>
Route ---
RailsBlog::Application.routes.draw do
resources :post
root :to => "post#index"
end
Form ---
<%= form_for :post, url: {action: 'create'} do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit 'Submit' %>
</p>
<% end %>
May be this is just a spelling mistake, but since I've recently started learning Rails, I'm unable to resolve this.
Update: I can go to particular id using
http://localhost:3000/post/1
but am only seeing blank page with view headers
The problem is here:
#post = Post.new(params[:posts])
It should be params[:post] - singular, not plural.
Also note that the best practice with form_for is to pass an object instead of symbol:
form_for #post do |f|
Then:
You don't need to specify url
You can reuse the same form for an edit action or create action (if object creation failed due to failing validation)
This however requires to initialize new object in your new action:
def new
#post = Post.new
end
UPDATE:
Also your routes are incorrect. When defining plural resources, you need to use plural form (it's more the convention than requirement, but since you're learning stick with it). So change your routes to:
resources :posts
And rename your controller to PostsController (remember to rename file name as well). restart the server and all should work.
ANOTHER UPDATE:
You also need to rename folder app/views/post to app/view/posts.
AND YET ANOTHER UPDATE:
In rails 4, you are not allowed to mass assign any params which has not been whitelisted using strong parameters. You need to tell rails which fields you allow to be assigned first - this is a security thing. You need to make some changes to your controller:
class PostsController < ApplicationController
...
def create
#post = Post.new(post_params)
...
end
...
private
def post_params
params.require(:post).permit(:title, :text)
end
end
This is the way to tell your controller that you are expecting those attributes from your form and they can be safely assigned.
I had just similar problem on the same tutorial.
The code spelling was correct and clearly accorded to examples in tutorial and BroiSatse's answer above.
The mistake was in order of private method definition.
How it was:
...
private
def post_params
params.require(:post).permit(:title, :text)
end
def show
#post = Post.find(params[:id])
end
...
The working order:
def show
#post = Post.find(params[:id])
end
private
...
Anyway, this topic was rather helpful. Thak you for your answers!

RoR, place value from select_tag in params hash

I am confused about the accepted way to update an association. Let's assume there are 2 models, Worker and Factory. When editing a Worker, the user is presented with a select_tag that contains a list of factories. My issue is the fact that the factory_id is not in the params[:worker][:factory_id] hash, but is instead in the params[:factory_id] hash. The end effect is that when my worker.update_attributes is called, the factory_id isn't updated because it isn't in the :worker hash.
My question is should I even be trying to get my factory_id into the params[:worker] hash through modifying my erb file, or should I be manually updating the value at params[:worker][:factory_id] with the value found in params[:factory_id]. Below is my erb code just in case there is something different I should be doing there. Also, the Factory class has_many workers and the Worker class belongs_to the Factory class if that helps.
<div>
<%= label_tag :factory , "Factory" %><br />
<%= select_tag "factory_id",
options_from_collection_for_select(#state_factories... %>
</div>
Any help is greatly appreciated.
Do it in the ERB file:
<%= select_tag "worker[factory_id]", options_from_collection_for_select(#state_factories... %>
Although, I'd use the select helper:
<%= select "worker", "factory_id", options_from_collection_for_select(#state_factories... %>
As that would automatically select the factory on edit.

Rails3 association and accepts_nested_attributes_for failing when validation of user_id on

I am following the Rails Tutorial doing a little project for myself and then try to progress.
Well I am facing a problem for which I found the solution, but I'll really appreciate any thoughts and opinions because to make it work I have to disable a validation in an association.
The context is as follow : the application (big name for what it does actually...) has users to track their weight. I would like that when a user sign up, he/she enters a first measure on the go.
So here are the simplified models:
User.rb
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation, :measures_attributes
has_secure_password
has_many :measures, dependent: :destroy
accepts_nested_attributes_for :measures
# here goes validations before_save, etc. taken from the Rails Tutorial
end
Measure.rb
class Measure < ActiveRecord::Base
attr_accessible :weight
belongs_to :user
# This is kind where the problem is...
# If I deactivate the validation for user_id everyhing goes fine
validates :user_id, presence: true
validates :weight, presence: true, numericality: { greater_than: 0 }
default_scope order: 'measures.created_at ASC'
end
Here is the Users controller
class UsersController < ApplicationController
# GET /users/new
def new
#user = User.new
#user.measures.build
end
# POST /users
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Hi #{#user.name}. Welcome !"
redirect_to #user
else
render :new
end
end
end
Here is the form partial for the user :
<%= form_for(user) do |user_form| %>
<%= render 'shared/error_messages', object: user_form.object %>
<%= render 'users/fields', user_builder: user_form %>
<%= user_form.fields_for :measures do |measure_fields| %>
<%= render 'measures/fields', measure_builder: measure_fields, full: true %>
<% end %>
<%= user_form.submit submit_text, class: 'btn btn-large btn-primary' %>
<% end %>
And even when I fill the form correctly, I get this error :
* Measures user can't be blank
The only way I found to make it work is to get rid of the :user_id validation for presence in the Measure model. I want to emphasize that when this validation is off, the user is saved, the measure is saved and correctly associated with the newly created user.
Am I doing something wrong ? Is the :user_id presence validation in the Measure model really useful (it is in the Rails Tutorial and it makes perfectly sense for me) ? If yes why is the Measure validation failing when it is on ?
Thanks a lot in advance.
You could try validates :user instead of user_id. Then it might work out that the two are associated in memory even though the User hasn't been saved yet.
I think I may have found an error in your code that would cause the error you're seeing.
In your new controller you specifically create an association:
#user.measures.build
In your create controller, you create a new element based on the fields that were passed in.
#user = User.new(params[:user])
There are some cases where the associated element will be dropped. I can't remember if this happens by default if it doesn't validate or if there was an option you need to set to make it drop.
I think you may need to add
#user.measure.build if #user.measures.empty?
in your create controller else clause before render :new.
I don't know if you're actually hitting this problem, but it would explain why the association to your User wasn't set.

Rails finds no route in a on-to-one relation (using Devise)

I'm facing a problem with a nested route in Rails an I can't figure out, what I'm doing wrong.
In a nutshell: I use Devise for authentication an registration. Now I want to have the user enter more detailed information about his contact data. To keep the user model small, I want to use a different model called account. As a user only will have one account, I use a on-to-one association and a nested route. But somehow the routing does not work.
This is my user model:
user.rb
class User < ActiveRecord::Base
has_one :account, :dependent => :destroy
attr_accessible :username, :email, :password, :password_confirmation, :remember_me
and this is my account model:
account.rb
class Account < ActiveRecord::Base
attr_accessible :anrede, :land, :nachname, :plz, :stadt, :strasse, :user_id, :vorname
belongs_to :user
end
and my route file looks like this:
routes.rb
devise_for :users, :path => 'members'
resources :users do
resource :account
end
As a user might not have a account yet, I test this in my view:
<% if current_user.account.try %>
<li><%= link_to "Account", user_account_path %></li>
<% else %>
<li><%= link_to "create Account", new_user_account_path %></li>
<% end %>
but when I enter the root path with a signed in user, Rails tells me
Routing Error
No route matches {:action=>"new", :controller=>"accounts"}
but there is a new action in my accounts_controller.rb as I have scaffolded the whole CRUD set (edited create with current_user.build_account) and it's also the path given by rake routes.
I'm desperately stuck in this! Could anybody help me, please?
EDIT
This is the output of my rake routes:
user_account POST /users/:user_id/account(.:format) accounts#create
new_user_account GET /users/:user_id/account/new(.:format) accounts#new
edit_user_account GET /users/:user_id/account/edit(.:format) accounts#edit
GET /users/:user_id/account(.:format) accounts#show
PUT /users/:user_id/account(.:format) accounts#update
DELETE /users/:user_id/account(.:format) accounts#destroy
EDIT2
this is the error message for the action new form:
NoMethodError in Accounts#new
Showing /home/stonjarks/Work/toytrade_devise/app/views/accounts/_form.html.erb where line #1 raised:
undefined method `accounts_path' for #<#<Class:0xa44d0cc>:0xab6222c>
Extracted source (around line #1):
1: <%= form_for(#account) do |f| %>
2: <% if #account.errors.any? %>
3: <div id="error_explanation">
4: <h2><%= pluralize(#account.errors.count, "error") %> prohibited this account from being saved:</h2>
I solved this using this SO hack, but it's a strange behavior anyway:
Rails Nested Route For Singular Resource
<%= form_for #account,:url=>{:action=>:create}
But I still don't get the point of this routing anyway. Despite this, I can't manage to find a route to show the account:
/users/1/account
ActiveRecord::RecordNotFound in AccountsController#show
Couldn't find Account without an ID
new_user_account_path is missing a user instance which should be provided for example:
new_user_account_path(current_user)
If you look at the following line:
new_user_account GET /users/:user_id/account/new(.:format) accounts#new
You can see that the path demands a :user_id - a user instance.
You can read here for further clarification of your problem.

Resources