I am in the process of creating a forum using Ruby on Rails (I'm pretty new at this) and have managed to get myself utterly stuck.
**Version Ruby on Rails 4.0 **
A forum software can have many Categories, and within these Categories you can have multiple forums.
The main page would look similar to this:
Category 1
Forum 1
Forum 2
Category 2
Forum 3
Forum 4
Forum 5
When you create a forum, you should have a drop down menu that allows you to select which category you wish to place it in.
At first I created two different scaffolds- One for Categories and one for Forums. I used a foreign key to connect the two. I do not know if this is the best method, but I could not get them to interact at all. I ended up screwing up my code so badly I have very little to show for it.
I tried using Adding Sub-categories in Rails4 and categories and sub-categories model rails for solutions but both ended up causing errors.
Here is some of my code. It's not much, but maybe you can tell me where to even begin. If there is a better way of doing this (not using two tables), let me know. I would love to hear the best possible way of doing this without using gems
WARNING: my code is an utter mess.
Migration
class AddForeignToForums < ActiveRecord::Migration
def change
add_column :forums, :category_id, :integer
end
end
Forum Controller (I know I am missing something that will allow me to connect to the Category, I just don't know what)
class ForumsController < ApplicationController
before_action :set_forum, only: [:show, :edit, :update, :destroy]
# GET function. view/forums/index.html.erb
def index
#forums = Forum.all
end
# GET /forums/1. view/forums/show.html.erb
def show
#forum = Forum.find(params[:id])
end
# GET /forums/new. view/forums/new.html.erb
# Be able to list all the Categories.
def new
#forum = Forum.new
#categories = Category.all
end
# GET /forums/1/edit
# Be able to list all the categories.
def edit
#forum = Forum.find(params[:id])
#categories = Category.all
end
# POST /forums
# Allows the creation of a new forum
# Lindsey note: how to save category_idea. Assign to category.
def create
#forum = Forum.new(forum_params)
respond_to do |format|
if #forum.save
#forum = Forum.new(:name => params[:forum][:name],
:category_id => params[:forum][:category_id])
format.html { redirect_to #forum, notice: 'Forum was successfully created.' }
format.json { render action: 'show', status: :created, location: #forum }
else
format.html { render action: 'new' }
format.json { render json: #forum.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /forums/1
# Allows the update of forums.
def update
respond_to do |format|
if #forum.update(forum_params)
format.html { redirect_to #forum, notice: 'Forum was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #forum.errors, status: :unprocessable_entity }
end
end
end
# DELETE /forums/1
def destroy
#forum.destroy
respond_to do |format|
format.html { redirect_to forums_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_forum
#forum = Forum.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def forum_params
params.require(:forum).permit(:name, :description, :category_id)
end
end
Forum Model
class Forum < ActiveRecord::Base
belongs_to :category
end
Category Model
class Category < ActiveRecord::Base
has_many :forums, :dependent => :destroy,
end
Category Index.html.erb
<tbody>
<% #categories.each do |category| %>
<tr>
<td><%= link_to category.name, category %></td>
<td><%= link_to ' (Edit', edit_category_path(category) %></td>
<td><%= link_to '| Destroy)', category, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% category.forums.each do |forum| %>
<tr>
<td><li><%= link_to forum.name, forum %></li></td>
</tr>
<% end %>
<% end %>
</tbody>
Forum _form.html.erb
<%= form_for(#forum) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<%= f.label :category_id %><br />
<%= f.select :category_id, Category.all.map{|c| [c.name, c.id]} %>
<div class="actions">
<%= f.submit %>
</div>
You probably want a table for forums, a table for categories, and a join table that includes a forum_id and category_id - name this forum_categories
class Forum < ActiveRecord::Base
has_many :forum_categories
has_many :categories, :through => :forum_categories
end
And, with categories, you'll do the reverse
class Categories < ActiveRecord::Base
has_many :forum_categories
has_many :forums, :through => :forum_categories
end
For adding categories in the view, you can use checkboxes or a multiple select box. The name of this input will be either
f.check_box 'category_ids[]'
or
f.select 'category_ids[]'
This will submit a param in an array format that will allow you to update the forum.category_ids with a simple
forum.create(params[:forum])
In your view, instead of #forums, you'll list category.forums
<% category.forums.each do |forum| %>
<%= forum.name %>
<% end %>
Hopefully this will get you started.
EDIT
For a single category on Forum, you've done well. Just a few smaller changes:
class Category < ActiveRecord::Base
# belongs_to :category - this can be removed
has_many :forums # Do you want to delete the forums if the category is removed? You don't need the classname option.
end
In the drop down - you'll do something like this...
f.select :category_id, Category.all.map{|c| [c.name, c.id]}
Related
I have a Poll app with 3 models.
Poll.rb
class poll < ApplicationRecord
validates_presence_of :user, :title
belongs_to :user
has_many :questions, dependent: :destroy
has_many :options, through: :questions
accepts_nested_attributes_for :questions
end
Question.rb
class Question < ApplicationRecord
validates_presence_of :poll_id, :question_id, :title
belongs_to :poll
has_many :options
accepts_nested_attributes_for :options, reject_if: proc { |attributes| attributes['title'].blank? }
end
Option.rb
class Option < ApplicationRecord
validates_presence_of :question_id, :title
belongs_to :question
belongs_to :poll
end
I want the question form to have a field for adding options so I've added this to the question _form.
<%= form.fields_for :option do |o| %>
<div>
<%= o.label "Option", style: "display: block" %>
<%= o.text_field :title, placeholder: "Enter Option here" %>
</div>
<% end %>
I can now see an option block which is good. Although I wish to have 3 possbile options so in the questions_controller.rb I've added the following:
def new
#question = #poll.questions.build
3.times { #question.options.build } # 3 different options
end
Despite this I'm only seeing one option block instead of the 3. Why is this the case and how do i fix? Additionally I'm not seeing new entries into the options postgresql table.
Full questions_controller.rb
class QuestionsController < ApplicationController
before_action :set_question, only: %i[ show edit update destroy ]
before_action :set_poll
# GET /questions or /questions.json
def index
#questions = Question.all
end
# GET /questions/1 or /questions/1.json
def show
end
# GET /questions/new
def new
# #question = Question.new
#question = #poll.questions.build
3.times { #question.options.build } # 5 different options
end
# GET /questions/1/edit
def edit
end
# POST /questions or /questions.json
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to polls_question_url(#question), notice: "Question was successfully created." }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1 or /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to polls_question_url(#question), notice: "Question was successfully updated." }
format.json { render :show, status: :ok, location: #question }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1 or /questions/1.json
def destroy
poll_id = Question.find_by(params[:poll_id])
session[:return_to] ||= request.referer
#question.destroy
respond_to do |format|
format.html { redirect_to session.delete(:return_to), notice: "Question was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Only allow a list of trusted parameters through.
def question_params
params.require(:question).permit(:poll_id, :question_type, :title, :description, :randomize_selection, :voter_abstain, { option_attributes: [:question_id, :poll_id, :party_id, :title, :description] } )
end
def set_poll
#poll = poll.find_by(params[:poll_id])
end
end
routes.rb
resources :users do
resources :polls
end
resource :polls do
resources :questions
end
resource :questions do
resources :options
end
Edit:
Here is my questions form partial.
_form.html.erb
<%= form_with(model: [#Poll, question] ) do |form| %>
<% if question.errors.any? %>
<div style="color: red">
<h2><%= pluralize(question.errors.count, "error") %> prohibited this question from being saved:</h2>
<ul>
<% question.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div>
<%= form.hidden_field :poll_id %>
</div>
<div>
<%= form.label :question_type, style: "display: block" %>
<%= form.text_field :question_type %>
</div>
<div>
<%= form.label :title, style: "display: block" %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :description, style: "display: block" %>
<%= form.text_area :description %>
</div>
<div>
<%= form.label :randomize_selection, style: "display: block" %>
<%= form.check_box :randomize_selection %>
</div>
<div>
<%= form.label :voter_abstain, style: "display: block" %>
<%= form.check_box :voter_abstain %>
</div>
<div>
<%= form.fields_for :options do |o| %>
<div>
<%= o.label "Option", style: "display: block" %>
<%= o.text_field :title, placeholder: "Enter Option here" %>
</div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
Here is the poll's show where I am rendering the forms.
show.html.erb
<p style="color: green"><%= notice %></p>
<p>
<strong>Poll Title:</strong>
<%= #poll.title %>
<%= render #poll %>
</p>
<div>
<%= link_to "Edit this poll", edit_user_poll_path(#poll) %> |
<%= link_to "Back to polls", user_polls_path %> |
<%= link_to "Destroy this poll", user_poll_path(#poll), method: :delete %>
</div>
<% if #poll.questions.any? %>
<hr>
<h2>Questions:</h2>
<%= render #poll.questions %>
<% end %>
<hr>
<h2>Add a new Question:</h2>
<%= render "questions/form", question: #poll.questions.build %>
The argument you pass to fields_for has to match the name of the assocation on the model:
<%= form.fields_for :options do |o| %>
<div>
<%= o.label "Option", style: "display: block" %>
<%= o.text_field :title, placeholder: "Enter Option here" %>
</div>
<% end %>
Pay very careful attention to plurization in Rails. Its a huge part of getting Convention over Configuration to work for you instead of against you.
However there are a quite a few other problems with this code.
Constants should always be CamelCase or UPPERCASE in Ruby - you need to change class poll to class Poll and fix all the references to the class. This isn't just a matter of style since the interpreter treats identifiers that start with an uppercase letter completely differently.
You're not nesting it properly. You have a nested route but you're still treating it like a non-nested resource in your controller and docstrings.
You're passing the parent id in your params whitelist. :poll_id and :question_id should not be whitelisted. Do not pass the parent id with a hidden input. The question id is assigned by Rails - you should not trust the user to pass it.
The option should not need a poll_id. Use an indirect has_one assocation to go up the tree. This could cause a edge case where a question and its options belong to different polls.
First lets fix the models:
class Poll < ApplicationRecord
# belongs_to assocations are required by default
# adding validations will just cause duplicate error messages
validates_presence_of :title
belongs_to :user
has_many :questions, dependent: :destroy
has_many :options, through: :questions
accepts_nested_attributes_for :questions
end
class Question < ApplicationRecord
validates_presence_of :title
belongs_to :poll
has_many :options
accepts_nested_attributes_for :options, reject_if: proc { |attributes| attributes['title'].blank? }
end
class Option < ApplicationRecord
validates_presence_of :title
belongs_to :question
has_one :poll, through: :question
end
Then I would recommend that you use shallow nesting
resource :polls do
resources :questions, shallow: true
end
This creates the questions member routes (show, edit, delete) without the /polls/:poll_id prefix while the collection routes (index, create, new) are nested.
And that you set controller up as:
class QuestionsController < ApplicationController
before_action :set_question, only: %i[ show edit update destroy ]
before_action :set_poll, only: %i[ new create index ]
# GET /polls/1/questions or /polls/1/questions.json
def index
#questions = #poll.questions.all
end
# GET /questions/1 or /polls/1/questions/1.json
def show
end
# GET /polls/1/questions/new
def new
# build is just an alias of new for legacy compatibility with Rails 2...
# its about time that we ditch it
#question = #poll.questions.new
3.times { #question.options.new } # 5 different options
end
# GET /questions/1/edit
def edit
end
# POST /polls/1/questions or /polls/1/questions.json
def create
#question = #poll.questions.new(question_params)
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, status: :unprocessable_entity }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1 or /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to #question, notice: "Question was successfully updated." }
format.json { render :show, status: :ok, location: #question }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1 or /questions/2.json
def destroy
session[:return_to] ||= request.referer
#question.destroy
respond_to do |format|
format.html { redirect_to session.delete(:return_to), notice: "Question was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Questions.find(params[:id])
end
# Only allow a list of trusted parameters through.
def question_params
# do not write this in a single unreadable line
params.require(:question).permit(
:question_type,
:title,
:description,
:randomize_selection,
:voter_abstain,
# do not wrap hash arguments in brackets
# as it will break if/when the `permit` method is changed to use real keyword arguments
# for has_many assocations the key naming convention is also plural_attributes
options_attributes: [
:party_id,
:title,
:description
]
)
end
def set_poll
#poll = Poll.find_by(params[:poll_id])
end
end
The key difference here is that you should look up the poll by the parameter in the URL for the nested routes and create the question off the poll instance (which sets poll_id).
Added:
You're not actually using the model you initialized in your controller. If you want to render the form from a completely different action you need to initialize the instance variable there:
class PollsController < ApplicationController
def show
#question = #poll.questions.new
3.times { #question.options.new } # 5 different options ???
end
# ...
end
<%= render "questions/form", question: #question %>
And in your partial you have a sneaky little bug. Ruby is case sensitive so #poll and #Poll are actually different variables.
irb(main):049:0> #foo = "bar" => "bar"
irb(main):050:0> #Foo
=> nil
Since instance variables are auto-vivified you're just get an unexpected nil instead of an error. What you actually want is:
<%= form_with(model: [#poll, question] ) do |form| %>
For my project I am trying to build a dashboard whereby an Agent can view submissions posted by a user and add a Status & Notes to each submission in order to log their own personal activity i.e they would not be changing the actual record, just leaving private notes against it. In order to do this I have created a join table with both Agent id and Submission id as well as Status and Notes columns.
I have managed to create an index view that shows submissions data with 2 form fields at the end of each line from my join table which are called Status and Notes... the problem is when I update these fields they do not get saved to my jointable.
Form on index view
<%= form_with(model: submission, local: true) do |form| %>
<% form.fields_for :agent_activities do |act| %>
<td> <div class="field">
<%= act.text_field :Status %>
</div>
</td>
<td> <div class="field">
<%= act.text_field :Notes %>
</div>
</td>
<td>
<div class="actions">
<%= form.submit %>
</div>
</td>
<% end %>
<% end %>
Model associations in rb files
class Submission < ApplicationRecord
belongs_to :user, :optional => true
belongs_to :location, :optional => true
has_many :agent_activities
end
class AgentActivity < ApplicationRecord
belongs_to :submission, :optional => true #has submission_id
foreign key in table
belongs_to :agent, :optional => true #has agent_id foreign key in
table
end
Controller:
class SubmissionsController < ApplicationController
before_action :set_submission, only: [:show, :edit, :update, :destroy]
def index
#submissions = Submission.where(:user_id => current_user.id)
end
def show
end
def new
#submission = Submission.new
end
def edit
end
# POST /submissions
# POST /submissions.json
def create
#submission = Submission.new(submission_params.merge(user_id: current_user.id))
respond_to do |format|
if #submission.save
# Tell the UserMailer to send a welcome email after save
NewSubmissionMailer.submission_email(#submission).deliver_now
NewSubmissionMailer.matching_agents_email(#submission).deliver_now
format.html { redirect_to #submission, notice: 'Submission was successfully created.' }
format.json { render :show, status: :created, location: #submission }
else
format.html { render :new }
format.json { render json: #submission.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /submissions/1
# PATCH/PUT /submissions/1.json
def update
respond_to do |format|
if #submission.update(submission_params)
format.html { redirect_to #submission, notice: 'Submission was successfully updated.' }
format.json { render :show, status: :ok, location: #submission }
else
format.html { render :edit }
format.json { render json: #submission.errors, status: :unprocessable_entity }
end
end
end
# DELETE /submissions/1
# DELETE /submissions/1.json
def destroy
#submission.destroy
respond_to do |format|
format.html { redirect_to submissions_url, notice: 'Submission was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_submission
#submission = Submission.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def submission_params
params.require(:submission).permit(:First_Name, :Last_Name, :Phone, :Email, :Desired_Location, :number_of_beds, :number_of_occupants, :Rent_price_per_month_gbp, :Max_move_in_date, :Tenant_Occupation, :Contact_me_on, :Furnished, :Current_Address, :Property_Requirements)
end
end
Not sure what im missing here :/
UPDATE BASED OFF #TOM ANSWER
New controller params:
def submission_params
params.require(:submission).permit(:First_Name, :Last_Name, :Phone, :Email, :Desired_Location, :number_of_beds, :number_of_occupants, :Rent_price_per_month_gbp, :Max_move_in_date, :Tenant_Occupation, :Contact_me_on, :Furnished, :Current_Address, :Property_Requirements, agent_activities_attributes: [:id, :Status, :Notes, :_destroy])
end
end
New Submission Model rb:
class Submission < ApplicationRecord
belongs_to :user, :optional => true
belongs_to :location, :optional => true
has_many :agent_activities
accepts_nested_attributes_for :agent_activities
end
Index.html.erb
<%= form_with(model: submission, local: true) do |form| %>
<% form.fields_for :agent_activities, #submission.agent_activities.build do |act| %>
<td> <div class="field">
<%= act.text_field :Status %>
</div>
</td>
<td> <div class="field">
<%= act.text_field :Notes %>
</div>
</td>
<td>
<div class="actions">
<%= form.submit %>
</div>
</td>
<% end %>
On your Submission model add: accepts_nested_attributes_for :agent_activities (accepts_nested_attributes_for documentation) This will let Rails know that your form is going to be supplying fields for an associated model.
Once that is added Rails will be supplying a key in params agent_activities_attributes in your strong params we can add: .permit(..., agent_activities_attributes: [:id, :Status, :Notes, :_destroy]. The :_destroy key is only needed if you plan on having allow_destroy: true on the nested attribute call.
One side note: Capitalized names (Status, Notes, etc) are normally reserved for constants in Ruby. You may want to look into changing your attribute column names to lowercase.
I'm new to Rails and working on a sample app. The idea behind the app is that it's a computerized check-in sheet for kids to ride a school bus. There are four models: Family, Kid, SchoolRide, and HomeRide. For Family and Kid, I generated complete scaffolds, but for SchoolRide and HomeRide, they're just models with a boolean field each of whether the kid has checked in to the schoolbus in the morning or checked out in the afternoon when coming home.
I want to be able to have a user check in a kid from a form rendered on the kid show view, but I'm having trouble creating instances of my ride models from the kids controller. How do I set up the views, routing, and controllers? Where/how do I pass in the parameters to the ride models in the kids controller?
Here's my form rendered into the kid's show view. Currently, I'm getting a syntax error.
<%= form_with(model: #school_ride, remote: true), :url => school_rides_path, :html => { :method => :post } do |form| %>
<div><p>
<%= form.label :check_in %><br>
<%= form.check_box :check_in %><br>
</p>
</div>
<div>
<%= form.hidden_field :kid_id, value: #kid.id %>
</div>
<p>
<%= form.submit %>
</p>
<% end %>
Here're my models:
class Kid < ApplicationRecord
belongs_to :family
has_many :school_rides
has_many :home_rides
end
class HomeRide < ApplicationRecord
belongs_to :kid
end
class SchoolRide < ApplicationRecord
belongs_to :kid
end
Here are some relevant parts of my kids controller:
def show
#family = Family.all
#school_ride = SchoolRide.new
end
# GET /kids/new
def new
#kid = Kid.new
end
# GET /kids/1/edit
def edit
end
# POST /kids
# POST /kids.json
def create
#kid = Kid.new(kid_params)
respond_to do |format|
if #kid.save
format.html { redirect_to family_path(id: #kid.family_id), notice: 'Kid was successfully created.' }
format.json { render :show, status: :created, location: #kid }
else
format.html { render :new }
format.json { render json: #kid.errors, status: :unprocessable_entity }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_kid
#kid = Kid.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def kid_params
params.require(:kid).permit(:name, :birthdate, :grade, :family_id)
end
def school_ride_params
params.require(:school_ride).permit(:check_in)
end
Here's some of my routing:
resources :kids
resources :school_rides, only: [:new, :create]
Try this:
<%=form_for #school_rid, remote: true do |form| %>
I'm trying to get the text from a text_area field in a form to save to a database in a different Model with the current Model's ID.
Currently, this works but only will save integers. If I put text into the 'Notes' field, then its saves it as a '0'. I suspect this is working correctly but I'm missing a piece to my puzzle. This is because I only want the 'Ticket' to save the note_id because I will have multiple 'Notes' per 'Ticket.
How can I get the Note to save in the Note Model, with an ID, and associate that note_id with this specific ticket?
Form - /app/views/tickets/_form.html.erb
<%= form_for(#ticket) do |f| %>
<% if #ticket.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#ticket.errors.count, "error") %> prohibited this ticket from being saved:</h2>
<ul>
<% #ticket.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.fields_for :notes do |u|%>
<%= u.label :note %>
<%= u.text_area :note, :size => "101x4", :placeholder => "Leave notes here." %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Tickets_controller.rb
class TicketsController < ApplicationController
# GET /tickets
# GET /tickets.json
def index
#tickets = Ticket.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #tickets }
end
end
# GET /tickets/1
# GET /tickets/1.json
def show
#ticket = Ticket.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #ticket }
end
end
# GET /tickets/new
# GET /tickets/new.json
def new
#ticket = Ticket.new
#ticket.notes.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #ticket }
end
end
# GET /tickets/1/edit
def edit
#ticket = Ticket.find(params[:id])
end
# POST /tickets
# POST /tickets.json
def create
#ticket = Ticket.new(params[:ticket])
respond_to do |format|
if #ticket.save
format.html { redirect_to #ticket, notice: 'Ticket was successfully created.' }
format.json { render json: #ticket, status: :created, location: #ticket }
else
format.html { render action: "new" }
format.json { render json: #ticket.errors, status: :unprocessable_entity }
end
end
end
# PUT /tickets/1
# PUT /tickets/1.json
def update
#ticket = Ticket.find(params[:id])
respond_to do |format|
if #ticket.update_attributes(params[:ticket])
format.html { redirect_to #ticket, notice: 'Ticket was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #ticket.errors, status: :unprocessable_entity }
end
end
end
# DELETE /tickets/1
# DELETE /tickets/1.json
def destroy
#ticket = Ticket.find(params[:id])
#ticket.destroy
respond_to do |format|
format.html { redirect_to tickets_url }
format.json { head :no_content }
end
end
end
Note.rb
class Note < ActiveRecord::Base
belongs_to :ticket
attr_accessible :note, :ticket_id
end
Ticket.rb
class Ticket < ActiveRecord::Base
attr_accessible :notes_attributes
accepts_nested_attributes_for :notes
end
It is because note_id is an integer type.
Use nested models:
Refer this for Nested Models
Model:
class Ticket < ActiveRecord::Base
has_many :notes
attr_accessible :note_id, :notes_attributes
accepts_nested_attributes_for :notes
end
View:
<%= form_for(#ticket) do |f| %>
<%= f.fields_for :notes do |u|%>
<%= u.label :note %>
<%= u.text_area :note %>
<% end %>
<%= f.submit 'Submit' %>
<% end %>
What you have is a nested association, with Ticket as the "parent". The association is governed by the link from note_id in the Note model to the id (primary key) of the Ticket. What you're presently doing right now is manually manipulating that numeric association. Rails, knowing that the note_id column is supposed to be an integer, is taking the text you're trying to insert and turning it in to a number (zero in this case). You've probably got a bunch of orphaned rows right now because of this.
Ultimately, in order to accomplish what you're trying to do, your form will need to provide fields for that associated model. One way you can handle this is by using the accepts_nested_attributes_for in your Ticket model. Like so:
class Ticket < ActiveRecord::Base
has_many :notes
accepts_nested_attributes_for :notes
end
And in your form, you can easily create a nested form like so:
<%= form_for(#ticket) do |f| %>
<div class="field">
<%= f.fields_for :notes do |f_notes|%>
<%= f_notes.label :note %><br />
<%= f_notes.text_area :note, :size => "101x4", :placeholder => "Please leave notes here."%>
<% end %>
</div>
<% end %>
Edit Almost forgot: Check out this Railscast from Ryan Bates dealing with Nested Attributes
Edit 2 As codeit pointed out, you don't need the attr_accessible :note_id in Ticket. Since you've indicated that a Ticket has many Notes, and that Note belongs to Ticket, the foreign key column will appear in the Note model as ticket_id, which you already have. Having note_id in the ticket model is useless, and also nonsensical since has_many describes a plural relationship (which can't be expressed with a single column).
Alright so I'm pretty new to this rails stuff so please bear with me...
I'm trying to make the most simple application ever, a Christmas list, and I need a little bit of help. Let me fill you in:
I scaffolded a person and an item. I modified my models a little bit and here is what they look like.
class Person < ActiveRecord::Base
has_many :item, :dependent => :destroy
end
class Item < ActiveRecord::Base
belongs_to :person
end
class CreateItems < ActiveRecord::Migration
def change
create_table :items do |t|
t.integer :person_id
t.string :description
t.timestamps
end
end
end
class CreatePeople < ActiveRecord::Migration
def change
create_table :people do |t|
t.string :name
t.timestamps
end
end
end
Seems like that's all cool. The index action on the people_controller lists all the people(duh)
<% #people.each do |person| %>
<tr>
<td><%= link_to person.name, "/people/#{person.id}" %></td>
</tr>
<% end %>
and when you click on one, it calls the show action(same controller) which gets all of the items for that person
def show
#person = Person.find(params[:id])
#items = Item.where(:person_id => params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #person }
end
end
and pulls the show view
<table>
<% #items.each do |item| %>
<tr>
<td><%= item.description %></td>
<td><%= link_to 'Edit', edit_item_path %></td>
<td><%= link_to 'Remove', "" %></td>
</tr>
<% end %>
</table>
<br/>
<%= link_to 'Add', :controller => :items, :action => :new, :id => #person.id %>
The link at the bottom is to add a new item for the person who's summary we are viewing. So then in the new action on the items_controller I have:
def new
#item = Item.new
#item.person_id = params[:id]
respond_to do |format|
format.html # new.html.erb
format.json { render json: #item }
end
end
Now I know this doesn't get saved until #item.save is called. I imagine this happens from the _form.html.erb submit button which in turn calls the create action in the controller?
def create
#item = Item.new(params[:item])
respond_to do |format|
if #item.save
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render json: #item, status: :created, location: #item }
else
format.html { render action: "new" }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
I'm just a little confused why this is never getting set, it seems like it should be so easy(I'm sure it is haha). Also while I'm at it, you may have noticed I have no link for my 'Remove' link above. This is because I also couldn't figure out how to destroy action from that link to remove the correct item.
Like I said, this is all new to me. I appreciate any help! Please feel free to critique EVERYTHING I have done here. I don't have feelings :)
As Vibhu said, it's quite likely your issue stem from the fact that you should have has_many :items (note the plural) in your Person controller.
To add a hidden filed in your form specifying the person's id, add this in your creation form:
f.hidden_field :person_id