I have a form that lets users add a new blocked tv show to their list of blocked shows. The form is not taking the param values (:user_id, :title, :image) that I tried to set in the controller. I'm a beginner, so I'm guessing the syntax is the problem.
Also I am getting a Couldn't find Tvshow without Id error when trying to use the #tvshow instance variable to set the param values of :title and :image. Each Blocked show should have the same title and image as the tvshow that the user selects in the collection_select. Is there an easier way to do this?
View
<%= form_for #blockedshow do |b| %>
<%= b.label :tvshow_id, "Add a Blocked TV Show " %><br/>
<%= collection_select(:blockedshow, :tvshow_id, Tvshow.all, :id, :title, prompt: true) %>
<%= submit_tag 'Add' %>
<% end %>
Controller
class BlockedshowsController < ApplicationController
def new
#blockedshow = Blockedshow.new
end
def create
#tvshow = Tvshow.find params[:blockedshow][:id]
#blockedshow = Blockedshow.new(safe_blockedshow)
params[:user_id] = current_user.id
params[:title] = #tvshow.title
params[:image] = #tvshow.image
if #blockedshow.save
flash[:notice] = "New Blocked TV Show added successfully"
redirect_to tv_show_index_path
else
render 'new'
end
end
private
def safe_blockedshow
params.require(:blockedshow).permit(:title, :user_id, :tvshow_id, :image)
end
end
Blockedshow model
class Blockedshow < ActiveRecord::Base
has_many :phrases
has_many :tvshows
belongs_to :user
end
Tvshow model
class Tvshow < ActiveRecord::Base
has_many :phrases
belongs_to :blockedshow
def self.search_for (query)
where('title LIKE :query', query: "%#{query}%")
end
end
Routes
resources :blockedshows
post 'blockedshows', to:'blockedshows#create#[:id]'
you are getting the issue because params[:blockedshow][:id] is not passed, if your trying to access the Tvshow id selected by from the drop-list you can do the following
#tvshow = Tvshow.find params[:blockedshow][:tvshow_id]
Just fixed by changing the controller to this:
def create
#tvshow = Tvshow.find params[:blockedshow][:tvshow_id]
#blockedshow = Blockedshow.new(
:user_id =>current_user.id,
:title=> #tvshow.title,
:image=> #tvshow.image,
:tvshow_id=>#tvshow.id
)
Related
I am having an issue understanding how to use Rails' delegated types when it comes to validations failing on the delegatee.
Having the following code
inbox.rb
class Inbox < ApplicationRecord
delegate :name, to: :inboxable
delegated_type :inboxable, types: %w[ Mailbox Messagebox ], dependent: :destroy
end
class Mailbox < ApplicationRecord
include Inboxable
belongs_to :inbox_domain
validates :alias, presence: true, uniqueness: true
def name
"#{self.alias}##{self.domain.name}"
end
end
messagees_controller.rb
def create
#mailbox = Inbox.create inboxable: Mailbox.new(mailbox_params)
if #mailbox.save
redirect_to #mailbox.inboxable, notice: "<b>#{#mailbox.name}</b> was created."
else
render :new
end
end
private
def mailbox_params
params.require(:mailbox).permit(:alias, :inbox_domain_id)
end
When i want to create a mailbox where the alias is already taken, the following error is thrown because Mailbox.new fails the validation
ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR: null value in column "inboxable_id" violates not-null constraint
DETAIL: Failing row contains (13, 2021-09-26 20:48:53.970799, 2021-09-26 20:48:53.970799, Mailbox, null, f).
):
Tried solution
What is the correct way to handle this scenario? I have tried to check explicitly Mailbox.new first, like this:
mailbox = Mailbox.new(mailbox_params)
if mailbox.valid?
#inbox = Inbox.create inboxable: mailbox
......
While it technically works, it's a mess once you also have to validate attributes on Inbox itself
Use validates_associated to trigger the validations on the associated record:
class Inbox < ApplicationRecord
delegate :name, to: :inboxable
delegated_type :inboxable, types: %w[ Mailbox Messagebox ], dependent: :destroy
validates_associated :inboxable
end
This will add an error ("Inboxable is invalid") to the errors object on this model and prevent saving if the associated mailbox is not valid.
What you want in your controller is:
def create
# .create both instanciates and saves the record - not what you want here
#mailbox = Inbox.new(inboxable: Mailbox.new(mailbox_params))
if #mailbox.save
redirect_to #mailbox.inboxable, notice: "<b>#{#mailbox.name}</b> was created."
else
render :new
end
end
If you want to display the errors for the associated item you need to access and loop through the errors object on it:
# app/views/shared/_errors.html.erb
<ul>
<% object.errors.each do |attribute, message| %>
<li><%= message %>
<% end %>
</ul>
<%= form_with(model: #inbox) do |form| %>
<% if form.object.invalid? %>
<%= render(partial: 'shared/errors', object: form.object) %>
<% if form.object.inboxable.invalid? %>
<%= render(partial: 'shared/errors', object: form.object.inboxable) %>
<% end %>
<% end %>
# ...
<% end %>
I have a survey application that I confused on final side.
here is app models properties:
User
- Survey
user_id
title
- Question
title
survey_id
type: multiple_choice|check_boxes|short_answer
- Option
title
question_id
(Till here is okay. I can create surveys these includes more nested forms) Issue is after created surveys. (users responses)
-Response
user_id
survey_id
-Answer
question_id
response_id
option_id
Creating survey with nested attributes is okay. Problem is at Response side. how should be my response controller and response form on Survey show.html.erb?
There is response controller nested attributes below;
def response_params
params.require(:response).permit(:id, :user_id, :survey_id, answers_attributes:[:question_id, :response_id, :option_id ] )
end
I should tell that survey can includes multiple questions these with only radio_buttons (independent radio buttons are other issue)
This issue made me so tired. I'll be glad if you can help me. Thanks.
For Source code: Click for source codes
updated files:
Response model:
class Response < ApplicationRecord
belongs_to :user
belongs_to :survey
has_many :answers, dependent: :destroy
validates :survey, presence: true
counter_culture :option
accepts_nested_attributes_for :answers
end
Survey_controller:
def new_response
#survey = Survey.find(params[:id])
#response = #survey.responses.build
# now, the tricky part, you have to build the Answer objects so you can use the nested form later
#survey.questions.each do |q|
#response.answers.build question: q
end
end
def create_response
#survey = Survey.find(params[:id])
#response = #survey.build(response_params)
#response.user = current_user
#response.save
end
Routes:
Rails.application.routes.draw do
devise_for :users
resources :surveys do
member do
get :new_response
get :create_response
end
end
root 'surveys#index'
end
form:
- # surveys/new_response.html.haml
- # You need to define a nested route inside survey resource to create the response
= form_for #response, url: create_response_survey_path(#survey) do |f|
- # you can iterate over all the answers already initialized
= f.fields_for :answers do |ff|
- # get the question from the current answer to show the title and options and a hidden_field with the question id
- q = ff.object.question
= q.title
= ff.hidden_field :question_id
- # add the radios for each options for the question
- q.options.each do |option|
= label_tag do
= ff.radio_button :option_id, option.id
= option.title
= f.submit 'Send'
I wouldn't use the Survey's show action to show the form to create a Response, I think it's better to approach it as a new_response action to make it cleaner and leave the show action just to show the actual survey (not to respond it). Something like:
class SurveysController < ApplicationController
def new_response
#survey = Survey.find(params[:id])
#response = #survey.responses.build
# now, the tricky part, you have to build the Answer objects so you can use the nested form later
#survey.questions.each do |q|
#response.anwers.build question: q
end
end
Now, you can have a form for the response:
- # surveys/new_response.html.haml
- # You need to define a nested route inside survey resource to create the response
= form_for #response, url: create_response_survey_path(#survey) do |f|
- # you can iterate over all the answers already initialized
= f.fields_for :answers do |ff|
- # get the question from the current answer to show the title and options and a hidden_field with the question id
- q = ff.object.question
= q.title
= ff.hidden_field :question_id
- # add the radios for each options for the question
- q.options.each do |option|
= label_tag do
= ff.radio_button :choice_id, option.id
= option.title
= f.submit 'Send'
Your response_params should be something like:
def response_params
params.require(:response).permit(answers_attributes: [:question_id, :choice_id])
end
note that I removed the :survey_id and the :user_id, you don't want a user to hack your form, change a survey_id or user_id and add responses to another survey made by another user!
and your create_response action:
def create_response
#survey = Survey.find(params[:id])
#response = #survey.build(response_params)
#response.user = current_user
#response.save
end
Hope it makes sense.
I am learning Ruby on Rails and have a search form set up and its working. On the pins index view I can search for pins(posts) by their title. However if I wanted to search by Username which is not in the pins table and display the results on the Pins index page how would I do this? How do I access an attribute from a different table? (Sorry for the newbie attempt at explaining my issue)
Pins controller
def index
#pins = Pin.search(params[:term])
end
Pin Model
def self.search(term)
if term
where('description LIKE ?', "%#{term}%")
else
order('id DESC')
end
end
_search.html.erb
<%= form_tag(pins_path, method: :get) do %>
<%= text_field_tag :term, params[:term] %>
<%= submit_tag 'Search', description: nil %>
<% end %>
Assuming you have set up something like
class User < ApplicationRecord
has_many :pins
# the username is stored in the attribute 'username'
end
class Pin < ApplicationRecord
belongs_to :user
end
you may do the following
# PinsController
def index
terms = params[:term]
username = params[:username]
#pins = Pin
#pins = #pins.where("description LIKE '%?%'", term) if term
#pins = #pins.includes(:user).where("users.username LIKE '%?%'", username) if username
# you may want to sort by id anyway
#pins = #pins.order('id DESC')
end
Note that I put the code straight to the controller for brevity. You may refactor this to use your search method in pin model.
# _search.html.erb
<%= form_tag(pins_path, method: :get) do %>
<%= text_field_tag :term, params[:term] %>
<%= text_field_tag :username, params[:username] %>
<%= submit_tag 'Search', description: nil %>
<% end %>
In case you want to do some more searching and filtering you may have a look at the ransack gem although I think you're going the right path in trying to figure this out yourself.
Although those railscasts episodes are from the past I think they are applicable to the current rails versions. Anyway one can get the point from them
http://railscasts.com/episodes/37-simple-search-form
http://railscasts.com/episodes/111-advanced-search-form
http://railscasts.com/episodes/240-search-sort-paginate-with-ajax
Another good resource is gorails.com (not affiliate in any way!!). I can highly recommend them as a resource for learning
Provided you have an association between User and Pin
class User
has_many :pins
end
class Pin
belongs_to :user
end
You can join :user from Pin and set conditions on the association:
Pin.joins(:user).where('users.username ? AND awesome = ?', 'Max', true)
# or the preferred method
Pin.joins(:user).where(user: { username: 'Max', awesome: true })
Note that we use users.username and not user.username when writing a SQL string you're specifying the table name - not the association.
To search for pins based on the username you could do:
def self.by_username(term)
joins(:user).where('users.username LIKE ?', "%#{term}%")
end
I'm a beginner building an app for my language students that checks whether the user copied a term correctly into a form. The Word model has a :term attribute, which is what the user will be copying. The Word Exposition model is associated with the Word model; upon enrollment each WordExposition-Word association's :completed attribute is set to false. I'm trying to implement a form in the WordExposition show view, such that if the user types the correct term, the WordExposition completed attribute will change to true from the default false.
In order to check whether the Word.term matches the term that the student copied I have a word_from_student_matches_word method in the WordExposition model, which I want to run before updating. As of now, I'm getting undefined local variable or methodword_from_student_matches_word'` upon submission of the form. How could I go about checking for matching spelling and updating the boolean attribute from the view?
WordExposition model:
class WordExposition < ActiveRecord::Base
belongs_to :enrollment
belongs_to :word
delegate :term, to: :word
delegate :reference, to: :word
delegate :image, to: :word
delegate :sound, to: :word
attr_accessor :term_given_by_student
validate :word_from_student_matches_word, on: :update
def word_from_student_matches_word
return true if word.term == term_given_by_student
errors.add(:term_given_by_student, "Terms don't match")
end
def next_word_exposition
WordExposition.where(["id > ? AND lesson_id = ?", id, lesson_id]).first
end
end
Word Expositions controller:
class WordExpositionsController < ApplicationController
before_action :authenticate_user!
before_action :require_enrollment_in_lesson
def show
#word = current_enrollment.word_expositions.find_by!(word_id: params[:id])
end
def update
current_word_exposition
if word_from_student_matches_word
current_word_exposition.completed = true
current_word_exposition.save
end
end
private
helper_method :current_lesson
def current_lesson
#current_lesson ||= Lesson.find(params[:lesson_id])
end
helper_method :current_enrollment
def current_enrollment
#current_enrollment ||= Enrollment.find_by!(lesson_id: params[:lesson_id], user_id: current_user.id)
end
def require_enrollment_in_lesson
if !(current_user.enrolled_in?(current_lesson))
redirect_to lesson_path(current_lesson), alert: 'You need to enroll in order to view the activities!'
end
end
def word_exposition_params
params.require(:word_exposition).permit(:completed)
end
def current_word_exposition
#current_word_exposition ||= WordExposition.find(params[:id])
end
end
Word Expositions show view:
<h1>Word Exposition</h1>
<!-- display the term to be copied -->
<div class="col-xs-10 col-xs-offset-1">
<h2><%= #word.term %></h2><br>
<!-- Form to check matching spelling and update WordExposition :completed to true if correct -->
<%= simple_form_for #word, url: lesson_word_exposition_path(current_lesson, #word), method: :patch do |f| %>
<%= f.input :term_given_by_student, label: "Enter the term exactly as above:" %><br>
<%= f.button :submit, class: 'btn btn-primary' %>
<% end %>
</div>
Rake routes:
lesson_enrollments POST /lessons/:lesson_id/enrollments(.:format) enrollments#create
lesson_word_exposition GET /lessons/:lesson_id/word_expositions/:id(.:format) word_expositions#show
PATCH /lessons/:lesson_id/word_expositions/:id(.:format) word_expositions#update
PUT /lessons/:lesson_id/word_expositions/:id(.:format) word_expositions#update
lessons GET /lessons(.:format) lessons#index
lesson GET /lessons/:id(.:format) lessons#show
word GET /words/:id(.:format) words#show
teacher_lesson_words POST /teacher/lessons/:lesson_id/words(.:format) teacher/words#create
new_teacher_lesson_word GET /teacher/lessons/:lesson_id/words/new(.:format) teacher/words#new
edit_teacher_lesson_word GET /teacher/lessons/:lesson_id/words/:id/edit(.:format) teacher/words#edit
teacher_lesson_word PATCH /teacher/lessons/:lesson_id/words/:id(.:format) teacher/words#update
PUT /teacher/lessons/:lesson_id/words/:id(.:format) teacher/words#update
DELETE /teacher/lessons/:lesson_id/words/:id(.:format) teacher/words#destroy
teacher_lessons POST /teacher/lessons(.:format) teacher/lessons#create
new_teacher_lesson GET /teacher/lessons/new(.:format) teacher/lessons#new
edit_teacher_lesson GET /teacher/lessons/:id/edit(.:format) teacher/lessons#edit
teacher_lesson GET /teacher/lessons/:id(.:format) teacher/lessons#show
PATCH /teacher/lessons/:id(.:format) teacher/lessons#update
PUT /teacher/lessons/:id(.:format) teacher/lessons#update
DELETE /teacher/lessons/:id(.:format) teacher/lessons#destroy
you are wrong in your controller
def update
current_word_exposition
current_word_exposition.completed = true #this line move to callback in your model changing the flag before save
if current_word_exposition.save
#nothing o something
else
#do something
end
end
Your model is validating the term on update, and return false if something went wrong. Just need to save the model instance and control the result
When I write a message and when pressing the send option,
I want to store student_id, coach_id and message to the database. student_id and coach_id are being saved, but the message field is not being saved. It shows null in the database. How do I fix this?
Any help is appreciated.
Controller file:
class CourseQueriesController <ApplicationController
def index
#course_query = CourseQuery.new
end
def create
# #course_query = CourseQuery.new(course_query_params)
#course_query = CourseQuery.where(student_id: current_student.id, coach_id: "2", message: params[:message]).first_or_create
if #course_query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
private
def set_course_query
#course_query = CourseQuery.find(params[:id])
end
# def course_query_params
# params[:course_query].permit(:message)
# end
end
model/course_query.rb:
class CourseQuery < ActiveRecord::Base
belongs_to :student
belongs_to :coach
end
view/course_query/index.html.erb:
<%= simple_form_for (#course_query) do |f| %>
<%= f.button :submit , "Send or press enter"%>
<%= f.input :message %>
<% end %>
database /course_queries:
It seems you didn't permit :course_query.
Try to permit your params the following way:
def course_query_params
params.require(:course_query).permit(:message)
end
But according to the 2nd way you pass params (params[:message]) I think you have a bit different params structure. So try another one:
def course_query_params
params.permit(:message)
end
When you look into the params generated in the log, you will see that the message inside the course_query hash, so params[:message] should be params[:course_query][:message]
#course_query = CourseQuery.where(student_id: current_student.id, coach_id: "2", message: params[:course_query][:message]).first_or_create