I have three tables: users, observative_sessions and observations.
In each respective model there's
users.rb
has_many :observative_sessions, dependent: :destroy
has_many :observations, through: :observative_sessions, dependent: :destroy
observative_sessions.rb
belongs_to :user
has_many :observations, dependent: :destroy
observations.rb
belongs_to :observative_session
belongs_to :user
In routes.rb I nested observations in observative_sessions
resources :observative_sessions do
resources :observations
end
I show the list of possible observations for each observative_session so I created the partial (observations/_index.html.erb) to render in the show page of observative session.
<%= render 'observations/index' %>
and a button to create new observations
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#new-observation-modal">Create</button>
observative_sessions_controller.rb
def show
#q = Observation.ransack([:observative_session])
#observations = #s.result.order(params[:order]).paginate(page: params[:page]) if params[:q].present?
#observations = #s.result.order(params[:order]).paginate(page: params[:page]) unless params[:q].present?
#observation = Observation.new
end
Now I have a problem, when I try to create a new observation the button calls the new_modal which renders the form
<%= render 'observations/form' %>
observations/_form.html.erb
<%= bootstrap_form_for ([#observative_session, #observation]) do |f| %>
And here I get the error Undefined method 'observations_path'
This is how I defined the creation method in observations_controller.rb
def create
#observation = Observation.new(observation_params)
respond_to do |format|
if #observation.save
format.html { redirect_to [#observative_session, #observation], notice: 'Observation was successfully created.' }
format.json { render :show, status: :created, location: #observation }
else
format.html { render :new }
format.json { render json: #observation.errors, status: :unprocessable_entity }
end
end
end
Thank you so much for any help.
My bad the error was on new.html.erb I passed
<%= render 'form', observation: #observation %>
instead of
<%= render 'form', observation: ([#observative_session, #observation]) %>
and in observations_controller.rb I had to add
#observative_session = ObservativeSession.find(params[:observative_session_id])
#observation = #observative_session.observations.build(observation_params)
By the way before I was trying to use thinking of passing the id of the session
ObservativeSession.find(params[:id])
instead it should be
ObservativeSession.find(params[:observative_session_id])
Related
I have two models: Game and Assignment. When I create a Game, I want to automaticall create an Assignment to go along with that game, hence the association between the two. In my Game controller I have:
def create
#game = Game.new(game_params)
#assignment = Assignment.new(assignment_params)
respond_to do |format|
if #game.save
format.html { redirect_to #game, notice: "Game was successfully created." }
format.json { render :show, status: :created, location: #game }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
private
def game_params
params.require(:game).permit(:home_team, :away_team)
end
def assignment_params
params.require(:assignment).permit(#game.game_id)
end
end
How do I pass in the game_id to the Assignment params when the Game is created? My models below incase they're needed. There is a game_id column in my Assignment model.
class Game < ApplicationRecord
has_one :assignment, dependent: :destroy
has_many :users, through: :assignments
end
class Assignment < ApplicationRecord
belongs_to :game
belongs_to :center_referee, class_name: 'User', foreign_key: "user_id"
belongs_to :assistant_referee_1, class_name: 'User', foreign_key: "user_id"
belongs_to :assistant_referee_2, class_name: 'User', foreign_key: "user_id"
end
Game Form
<%= simple_form_for(#game) do |f| %>
<%= f.error_notification %>
<%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<div class="form-inputs">
<%= f.input :home_team %>
<%= f.input :away_team %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
Game Controller
def new
#game = Game.new
end
# POST /games or /games.json
def create
#game = Game.new(game_params)
respond_to do |format|
if #game.save
format.html { redirect_to #game, notice: "Game was successfully created." }
format.json { render :show, status: :created, location: #game }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #game.errors, status: :unprocessable_entity }
end
end
end
Right of the top of my head, you could simply run a simple callback inside the Game model as follows:
after_create :create_assignment
def create_assignment
Assignment.create(game_id: id, center_referee_id: , assistant_referee_1_id:, assistant_referee_2_id:)
end
This way you handle it once at the model level. Every game created automatically creates an assignment.
Also if the referees are not required, you may pass an optional: true flag to the belongs_to in the assignment model. that way you can safely create the games. because currently, it is not clear how you're getting the referee details from.
Don't know how to save a question form that will have 2 ids, event id and user id
User.rb
class User < ApplicationRecord
has_many :questions
has_many :answers
end
Event.rb
class Event < ApplicationRecord
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions
end
Question.rb
class Question < ApplicationRecord
belongs_to :user
belongs_to :event
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers
end
Answer.rb
class Answer < ApplicationRecord
belongs_to :user
belongs_to :question
scope :sorted, ->{ order(created_at: :asc) }
end
questions_controller.rb
def new
#question = current_user.questions.build
end
def create
#question = Question.new(question_params)
#question["user_id"] = current_user.id
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
I have the standard form generated with the scaffold, but I cant piece together what I am missing from my limited knowledge and experience in rails on how to get every question that a user makes to be linked to a specific created event and show which user created that particular question (I would assume that each question entry will need a user_id and a event_id column)
<%= form_with(model: question, local: true) do |form| %>
<%= form.label :body %>
<%= form.rich_text_area :body %>
<%= form.submit 'Create Question' %>
<% end %>
Updated for Error:
When I try to create a question, each entry requires an event_id (an event has many questions) and a user_id (I want to show who created that question).
Are my models and controllers setup correctly? When I try to create a question for an event, the error "Event must exist" occurs
Updated with ERD pic ( Not sure if I should just have users or seperate into creators and users )
ERD after reading up on last update
I begun reading up more on data modelling and I came up with this ERD...I am still not very sure on achieving 3NF and setting up the relationships, and how to translate it to rails models but would be great to have comments on my ERD so I can learn.
Creator creates events that users can join. Creator creates questions that users can post answers. Each event has many questions and each question can have many answers.
If I understood you correctly, you need nested resources to achieve your goal. It means, that questions are nested inside events, like 'parent' event has 'children' questions. At first, change your routes:
resources :events do
resources :questions
end
Run rake routes in terminal and you will see new routes with :event_id parameter. Now on events#index you can add link_to 'Questions about this event', event_questions_path(event) near each event, the link will lead you to events/1/questions (1 is id of the event). In QuestionsController you have new parameter, event_id, which you can use to find needed Event or to assign as foreign key.
Change you form
<%= form_with(model: [#event, #question], local: true) do |form| %>
<%= form.label :body %>
<%= form.rich_text_area :body %>
<%= form.submit 'Create Question' %>
<% end %>
and controller a bit to work with nested routes
def new
#event = Event.find(params[:event_id])
#question = event.questions.build
end
def create
#event = Event.find(params[:event_id])
#question = event.questions.build(question_params)
#question.user_id = current_user.id
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
Also, you should remove the line accepts_nested_attributes_for from Event and Question models, since you never use it
I am trying to understand Rails' field_for, specifically what should go into the controller for nested resources. My issue is that when I create a comic with comic pages through the Comic form, the page's image are not saved.
I have Users, Comics, and ComicPages. Here are the models:
class User < ActiveRecord::Base
has_many :comics
has_many :comic_pages, through: :comics
end
class Comic < ActiveRecord::Base
belongs_to :user
has_many :comic_pages, :dependent => :destroy
accepts_nested_attributes_for :comic_pages
end
class ComicPage < ActiveRecord::Base
belongs_to :comic
end
Here is the form for Comic, where I also want to add comic_pages:
<%= form_for ([#user, #comic]) do |f| %>
<%= f.text_field :title %>
<%= f.fields_for :comic_pages do |comic_page| %>
<%= comic_page.file_field :comic_page_image %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I am confused about the comics_controller (new and create actions). How can I pass comic_page params to this controller???
def new
#user = current_user
#comic = #user.comics.new
#comic.comic_pages.build
end
def create
#user = current_user
#comic = #user.comics.new(comic_params)
#comic.comic_pages.build
respond_to do |format|
if #comic.save
format.html { redirect_to #user, notice: 'Comic was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #comic.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comic
#comic = Comic.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comic_params
params.require(:comic).permit(:title, :synopsis)
end
def comic_page_params
params.require(:comic_page).permit(:comic_page_image, :comic_image_file_name)
end
Many thanks!
--- EDIT ---
After the answer for the params, I used it to create the following create action:
def create
#user = current_user
#comic = #user.comics.new(comic_params)
i = 0
until i = 1
#comic_page = #comic.comic_pages.new(comic_params[:comic_pages_attributes]["#{i}"])
#comic_page.save
i += 1
end
respond_to do |format|
if #comic.save
...
end
end
end
You need to permit those fields from comic_pages that you want to save through in the comic_params section of your controller
params.require(:comic).permit(:title, :synopsis, comic_pages_attributes: [:comic_page_image])
I'm new to Rails and making application where college members (teachers and students) can create posts and comment on them. Later on I wish to add nesting (ancestry) and points system in it.
I have Post, Comment and Member model. The Post model was made via Scaffolding, Member model was made with help of Devise, and Comment is just a model.
In my show page of Post, I'd like to have comments beneath the posts, I've made some progress (thanks to SO I came to know quite a bit) but now I am stuck with a problem that whenever I attempt to post a blank comment, rails was redirecting to the edit page. How to change this so that rails stays only on the show page and display errors?
For this I searched a bit, created a new method 'update_comments' in post_controller.rb and tried modifying the forms_for tag attributes, as in the code below, but now I get routing error on submitting.
app/models/member.rb
class Member < ActiveRecord::Base
#Associations
belongs_to :department
has_one :student, :dependent => :destroy
accepts_nested_attributes_for :student
has_one :nstudent, :dependent => :destroy
accepts_nested_attributes_for :nstudent
has_many :posts, :dependent => :destroy
has_many :comments, :dependent => :destroy
end
app/models/post.rb
class Post < ActiveRecord::Base
#Associations
belongs_to :member
has_many :comments, :dependent => :destroy
accepts_nested_attributes_for :comments
end
app/models/comment.rb
class Comment < ActiveRecord::Base
# Associations
belongs_to :member
belongs_to :post
validates_presence_of :content
end
config/routes.rb
Urdxxx::Application.routes.draw do
devise_for :members
resources :posts do
member do
get 'update_comment'
end
end
root :to => 'posts#index'
app/controllers/posts_controller.rb
class PostsController < ApplicationController
# Devise filter that checks for an authenticated member
before_filter :authenticate_member!
# GET /posts
# GET /posts.json
def index
#posts = Post.find(:all, :order => 'points DESC')
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
...
# GET /posts/1/edit
def edit
#post = Post.find(params[:id])
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(params[:post])
#post.member_id = current_member.id if #post.member_id.nil?
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PUT /posts/1
# PUT /posts/1.json
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post = Post.find(params[:id])
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
# Not made by scaffold
def update_comment
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, notice: 'Comment was successfully created.' }
format.json { head :no_content }
else
format.html { render action: "show" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
end
app/views/posts/show.html.erb
<p> Have your say </p>
<%= form_for #post, :url => {:action => 'update_comment'} do |p| %>
<%= p.fields_for :comments do |c| %>
<!-- Following 3 lines saved my life -->
<% if c.object.new_record? %>
<%= c.text_area :content, :rows => 4 %>
<%= c.hidden_field :member_id, value: current_member.id %>
<% end %>
<% end %>
<%= p.submit "Reply" %>
<% end %>
image of my show page:
http://i.stack.imgur.com/TBgKy.png
on making a comment:
http://i.stack.imgur.com/JlWeR.png
Update:
Looked back and made changes here, following what Ken said. I don't know how but it works for now.
app/controllers/posts_controller.rb
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
elsif :comments
format.html { render action: "show" }
format.json { render json: #post.errors, status: :unprocessable_entity }
else
format.html { render action: "edit" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
You don't need a custom method. It is not very RESTful. See, e.g., http://www.sitepoint.com/restful-rails-part-i/ for info on REST. This is not a case where there is justification to use a custom method.
Whenever you find yourself adding custom methods you should think long and hard about whether it's necessary. Usually if you need custom methods what you actually need is another controller (or a different set of controllers).
The update method here is all you need. If you really want to go to the show method after a failed update (though I don't know why) then change the render edit call in the block in the update method after the update fails.
It seems like your real problem is the edit view isn't showing errors. Although the scaffold generated view should do that so maybe you changed it.
In case you missed it you may also benefit from this screencast:
http://railscasts.com/episodes/196-nested-model-form-part-1
You need to update the method type in route and also needs to sets the form post method to your new action, also when you submit a form its an post request not a get request.
Urdxxx::Application.routes.draw do
devise_for :members
resources :posts do
collection do
post :update_comment
end
end
root :to => 'posts#index'
<p> Have your say </p>
<%= form_for :post, :url => {:action => 'update_comment'} do |p| %>
<%= p.fields_for :comments do |c| %>
<!-- Following 3 lines saved my life -->
<% if c.object.new_record? %>
<%= c.text_area :content, :rows => 4 %>
<%= c.hidden_field :member_id, value: current_member.id %>
<% end %>
<% end %>
<%= p.submit "Reply" %>
<% end %>
I'm saving nested objects within the objects they belong to, but when i do that they do not use the controller im saving but the parents controller.
class Project < ActiveRecord::Base
belongs_to :company
belongs_to :user
has_many :tasks
accepts_nested_attributes_for :tasks, :allow_destroy => true
end
in the views i have something like this
<% form_for #project do |c| %>
<% c.fields_for :tasks, #project.tasks.last do |p| %>
<%= p.text_field :name %>
<% end %>
<%= submit_tag '+' %>
<% end %>
so what i'm trying to do, is update the user field with the fields for, that last field is specified in the controller.
def show
#project = Project.find(params[:id])
#project.tasks.build
#project.tasks.last.user = current_user # this should pass to the show.html.erb, to be saved back
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #project }
end
end
I'm thinking maybe the solution would be to check if the username is set in the nested objects, and if not to populate it with the current user in:
def update
#project = Project.find(params[:id])
#project.user = current_user
#find anything #project.....user blank and set to current user
respond_to do |format|
if #project.update_attributes(params[:project])
format.html { redirect_to(#project, :notice => 'Project was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #project.errors, :status => :unprocessable_entity }
end
end
end
I'm hoping that is the solution, and how do it do it?
an example of it running currently is at http://severe-fire-37.heroku.com