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.
Related
I am trying to create a question and answer thread in Rails 6 where a User can answer on a question, and then other users can comment on the answer - similar to a reddit or even stackoverflow.
I created a polymorphic association on my Answer model with a 'parent_id' and I am able to post answers on the initial question. However the nested answers do not render below the initial answer, but rather below the main question. I think I have isolated the problem to the corresponding partial view seen below:
Answer View
<li>
<%= answer.body %></br>
<%= link_to answer.user.first_name, answer.user %>
<%= link_to answer.user.last_name, answer.user %>
answered <%= time_ago_in_words(answer.created_at) %> ago.
<div class="comments-container">
<%= render partial: "answers/reply", locals: {commentable: answer.commentable, parent_id: answer.parent.id} %>
</div>
<ul> <%= render partial: "answers/answer", collection: answer.answers %> </ul>
</li>
From my understanding, the last line should render the answers to the answer, however the answers render underneath the initial question, and not the answer. Any ideas on what im doing wrong?
Should I be using a gem like Ancestry to do this? If so how would that work?
For completeness, here are the other components
Question View
<h3><%= #question.title %></h3>
<p> Asked by <%= link_to #question.user.email, #question.user %> <%= time_ago_in_words(#question.created_at) %> ago. </p>
</br>
<span class="body"> <%= #question.body %> </span>
</br>
<h5><strong><%= #question.answers.count %> Answers</strong></h5>
<%= render #answers %></br>
<%= render partial: "answers/form", locals: {commentable: #question} %> </br>
<%= paginate #answers %>
Answer model
belongs_to :user
belongs_to :parent, optional: true, class_name: 'Answer'
belongs_to :commentable, polymorphic: true
has_many :answers, as: :commentable, dependent: :destroy
validates :body, presence: true
validates :user, presence: true
Question model
belongs_to :user
has_many :answers, as: :commentable, dependent: :destroy
validates :body, presence: true
validates :title, presence: true
validates :user, presence: true
AnswerController
class AnswersController < ApplicationController
before_action :set_answer, only: [:edit, :update, :destroy, :upvote, :downvote]
before_action :find_commentable, only: [:create]
def new
#answer = Answer.new
end
def create
#answer = #commentable.answers.new(answer_params)
respond_to do |format|
if #answer.save
format.html { redirect_to #commentable }
format.json { render :show, status: :created, location: #commentable }
else
format.html { render :new }
format.json { render json: #answer.errors, status: :unprocessable_entity }
end
end
end
def destroy
#answer = #commentable.answers.find(params[:id])
#answer.discard
respond_to do |format|
format.html { redirect_to #commentable, notice: 'Answer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_answer
#answer = Answer.find(params[:id])
end
def answer_params
params.require(:answer).permit(:body).merge(user_id: current_user.id, parent_id: params[:parent_id])
end
def find_commentable
#commentable = Answer.find(params[:answer_id]) if params[:answer_id]
#commentable = Question.find(params[:question_id]) if params[:question_id]
end
end
Question Controller
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy, :upvote, :downvote]
def index
#questions = Question.order('created_at desc').page(params[:page])
end
def show
#answer = #question.answers.new
#answers = if params[:answer]
#question.answers.where(id: params[:answer])
else
#question.answers.where(parent_id: nil)
end
#answers = #answers.page(params[:page]).per(5)
end
def new
#question = Question.new
end
def edit
end
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'You have successfully asked a question!' }
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
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to #question, notice: 'Question successfully updated.' }
format.json { render :show, status: :ok, location: #question }
else
format.html { render :edit }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
def destroy
#question.discard
respond_to do |format|
format.html { redirect_to #questions_url, notice: 'Question successfully deleted.' }
format.json { head :no_content }
end
end
private
def set_question
#question = Question.find(params[:id])
end
def question_params
params.require(:question).permit(:title, :body, :tag_list).merge(user_id: current_user.id)
end
end
You kind of failed at modeling polymorphism. If you want a true polymorphic association you would model it as so:
class Question
has_many :answers, as: :answerable
end
class Answer
belongs_to :answerable, polymorphic: true
has_many :answers, as: :answerable
end
This lets the "parent" of a question be either a Question or a Answer and you don't need to do ridiculous stuff like #question.answers.where(parent_id: nil). You can just do #answers = #question.answers and this will only include the first generation children.
However polymorphism isn't all its cracked up to be and that will be especially apparent when building a tree hierarchy. Since we actually have to pull the rows out of the database to know where to join you can't eager load the tree effectively. Polymorphism is mainly useful if the number of parent classes in large or unknown or you're just prototyping.
Instead you can use Single Table Inheritance to setup the associations:
class CreateAnswers < ActiveRecord::Migration[6.0]
def change
create_table :answers do |t|
t.string :type
t.belongs_to :question, null: true, foreign_key: true
t.belongs_to :answer, null: true, foreign_key: true
# ... more columns
t.timestamps
end
end
end
Note the nullable foreign key columns. Unlike with polymophism these are real foreign keys so the db will ensure referential integrity. Also note the type column which has a special significance in ActiveRecord.
Then lets setup the models:
class Question < ApplicationRecord
has_many :answers, class_name: 'Questions::Answer'
end
class Answer < ApplicationRecord
has_many :answers, class_name: 'Answers::Answer'
end
And the subclasses of Answer:
# app/models/answers/answer.rb
module Answer
class Answer < ::Answer
belongs_to :answer
has_one :question, through: :answer
end
end
# app/models/questions/answer.rb
module Questions
class Answer < ::Answer
belongs_to :question
end
end
Pretty cool. Now we can eager load to the first and second generation with:
Question.eager_load(answers: :anser)
And we can keep going:
Question.eager_load(answers: { answers: :answer })
Question.eager_load(answers: { answers: { answers: :answers }})
But at some point you'll want to call it quits and start using ajax like reddit does.
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])
I have a question.
I have this model:
class Project < ApplicationRecord
has_many :documents
belongs_to :course_unit
belongs_to :user
has_and_belongs_to_many :people
has_one :presentation
has_and_belongs_to_many :supervisors, :class_name => "Person", :join_table => :projects_supervisors
end
and this model:
class Presentation < ApplicationRecord
belongs_to :project
has_and_belongs_to_many :juries, :class_name => "Person", :join_table => :juries_presentations
end
When I create a new project, I have many attributes of the model Project and two attributes (room and date) from Presentation model, so I don't know how to send data from room and date attributes to the presentation model.
So my question is: How can I create a new project that saves data in project table and presentation table?
UPDATE #1
My project controller:
def new
#project = Project.new
end
def edit
end
def create
#project = Project.new(project_params)
#project.build_presentation
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Project was successfully updated.'}
format.json { render :show, status: :ok, location: #project }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project).permit(:title, :resume, :github, :grade, :project_url, :date, :featured, :finished, :user_id, :course_unit_id, presentation_attributes: [ :date , :room ])
end
My index view for Projects is:
<%= form_for #project do |f| %>
<%= f.fields_for :presentations do |ff| %>
<%= ff.label :"Dia de Apresentação" %>
<%= ff.date_field :date %>
<%= ff.label :"Sala de Apresentação" %>
<%= ff.text_area :room %>
<% end
<%= f.submit %>
<% end %>
You can try something like this:
project = Project.new(name: 'project 1')
project.build_presentation(room: 'room 1', date: Time.current)
project.save
It will save project with name project 1 and presentation belongs to that project, with room room 1 and date is Time.current.
And you need to update your models to avoid presence validation.
class Project < ApplicationRecord
has_one :presentation, inverse_of: :project
end
class Presentation < ApplicationRecord
belongs_to :project, inverse_of: :presentation
end
Having set up a has_many through relationship, I'm trying to iterate through associated B objects in the view of an object A. I.e. something like
<% for q in #survey.questions do %>
<%= q.name %> <br/>
<% end %>
yields nothing, while
<%= #survey.questions %>
yields
#<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_Question:0x007f9859f221e8>
How could (should) I access these?
Here's the Controller
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
#surveys = Survey.all
end
def show
end
def new
#survey = Survey.new
end
def edit
end
def create
#survey = Survey.new(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render action: 'show', status: :created, location: #survey }
else
format.html { render action: 'new' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url }
format.json { head :no_content }
end
end
private
def set_survey
#survey = Survey.find(params[:id])
end
end
And here's the Models
class Survey < ActiveRecord::Base
has_many :assignments
has_many :questions, through: :assignments
end
.
class Question < ActiveRecord::Base
has_many :assignments
has_many :surveys, through: :assignments
end
.
class Assignment < ActiveRecord::Base
belongs_to :survey
belongs_to :question
end
As far as I can tell Powers answer should work. Try checking if the #survey actually has any questions. Add this in the page somewhere <%= #survey.questions.count %>
Are you sure the #survey in question really has assignments associated to it? Can you see them on the Rails console?
Try this:
<% #survey.questions.each do |q| %>
<%= q.name %> <br/>
<% end %>
The for loop should be avoided in Ruby. From this question, it looks like for loops operate on arrays, so something like this might also work (again, this is not the recommended solution):
<% for q in #survey.questions.to_a do %>
<%= q.name %> <br/>
<% end %>
Update
I think you need to make your associations singular in the Assignment model:
class Assignment < ActiveRecord::Base
belongs_to :survey
belongs_to :question
end
I created a quiz on many to many relationships that you might find helpful.
Hello guys I've a a 2 model client and meal.
client.rb
class Client < ActiveRecord::Base
has_many :meals
accepts_nested_attributes_for :meals
end
meal.rb
class Meal < ActiveRecord::Base
belongs_to :client
end
class Lunch < Meal
end
class Dessert < Meal
end
views/clients/_form.html.erb
<%= simple_form_for #client do |f| %>
<%=f.input :name %>
<%=f.input :adress %>
<%=f.input :telephone %>
<%= f.simple_fields_for :meal do |m| %>
<%=m.input :type %>
<%end%>
<% end %>
When I save the meal type it doesn't appear on client' index.html.erb(it's blank).
What the problem is?
How can I create a client by giving him a meal type(eg."Lunch") with the following cotroller:
def create
#client = Client.new(params[:client])
respond_to do |format|
if #client.save
format.html { redirect_to #client, notice: 'Operation was successfully created.' }
format.json { render json: #client, status: :created, location: #client }
else
format.html { render action: "new" }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
Matter i simply have to set the column inheritance in meal.rb like this:
class Meal < ActiveRecord::Base
set_inheritance_column do
"type" + "_id"
end
belongs_to :client
end
class Lunch < Meal
end
class Dessert < Meal
end
So now I can select the type of meal when I create a client.
Thanks to Anan, the solution comes from him.