Sorry if my problem will be silly but Rails are new to me. I made two models and two controllers. My problems were started after I made second model and added reference to the first one.
class SentencesController < ApplicationController
before_action :find_story
def create
#sentence = find_story.sentences.build(sentence_params)
if #sentence.save
flash[:success] = "You wrote the continuation!"
render 'stories/show'
else
render 'stories/show'
end
end
private
def sentence_params
params.require(:sentence).permit(:content)
end
def find_story
#story = Story.find(params[:id])
end
end
and this:
class StoriesController < ApplicationController
........
def show
#story = Story.find(params[:id])
#sentence = #story.sentences.build
end
.........
end
And I have a problem with defining instance variable #story = Story.find(params[:id]). Error: ActiveRecord::RecordNotFound in SentencesController#create. I have tried many combinations.
This is my migrate files:
class CreateStories < ActiveRecord::Migration[5.1]
def change
create_table :stories do |t|
t.string :title
t.text :content
t.timestamps
end
end
end
class CreateSentences < ActiveRecord::Migration[5.1]
def change
create_table :sentences do |t|
t.text :content
t.references :story, foreign_key: true
t.timestamps
end
add_index :sentences, [:story_id, :created_at]
end
end
What did I do wrong?
EDIT (routes):
Rails.application.routes.draw do
root 'stories#index'
get 'stories/show'
get 'stories/new'
resources :stories
resources :sentences, only: [:create]
end
and schema:
ActiveRecord::Schema.define(version: 20180322121215) do
create_table "sentences", force: :cascade do |t|
t.text "content"
t.integer "story_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["story_id"], name: "index_sentences_on_story_id"
end
create_table "stories", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
As stated in the comments, you probably want your routes to look something like:
resources :stories do
resources :sentences, only: [:create]
end
Which will give you:
story_sentences POST /stories/:story_id/sentences(.:format) sentences#create
stories GET /stories(.:format) stories#index
POST /stories(.:format) stories#create
new_story GET /stories/new(.:format) stories#new
edit_story GET /stories/:id/edit(.:format) stories#edit
story GET /stories/:id(.:format) stories#show
PATCH /stories/:id(.:format) stories#update
PUT /stories/:id(.:format) stories#update
DELETE /stories/:id(.:format) stories#destroy
Which you might use something like:
<%= form_tag story_sentences_path(#story) do %>
...
<% end %>
Then, as Matt said, change your find to:
#story = Story.find(params[:story_id])
There are a couple of reasonable ways you can find the story in your sentences controller.
You can add a story_id field to your form and submit it as a param along with the sentence content. Just make sure to add it to sentence_params in the controller so it's not ignored.
def sentence_params
params.require(:sentence).permit(:content, :story_id)
end
And then you'll need to update your find_story method in the controller to:
#story = Story.find(sentence_params[:story_id])
You can set up nested resources in your routes file (where the sentences resource is nested within the stories resource). That will give you access to the story_id from the route itself (ie. you wouldn't need to submit the story_id through the form).
And if you go this way, you'll also need to tweak the find_story method in the controller, but this time it should be:
#story = Story.find(params[:story_id])
Related
I am trying to link from my set index page to card index directly without having to go through the show index. I am also concerned that my routes may be wrong due to a foreign key missing in my schema. I have added card_id manually to my set model. I will put all the relevant files below.
ActiveRecord::Schema[7.0].define(version: 2022_12_04_135913) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "pokemon_cards", force: :cascade do |t|
t.string "name"
t.string "set"
t.string "artist"
t.integer "price"
t.boolean "owned"
t.boolean "needed"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "pokemon_sets", force: :cascade do |t|
t.string "name"
t.string "series"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "card_id"
t.string "image_url"
t.string "symbol_url"
end
end
The below line which has a comment has the following error :
No route matches {:action=>"index", :controller=>"pokemon_cards", :pokemon_set_id=>nil}, missing required keys: [:pokemon_set_id]
<h1>Pokemon Sets</h1>
<ul>
<% #pokemon_sets.each do |set| %>
<li><%=link_to(set.name, pokemon_set_pokemon_cards_path(#pokemon_set)) %></li> # this throws an error
<li><%= set.series %></li>
<li><%= image_tag(set.image_url, size: "250x150") %></li>
<% end %>
</ul>
I will put below my nested resources in my routes file and also my routes in the terminal.
Rails.application.routes.draw do
root to: "pages#home"
resources :pokemon_sets, only: [:index, :show], path: 'sets' do
resources :pokemon_cards, only: [:index, :show], path: 'cards'
end
end
Prefix Verb URI Pattern Controller#Action
root GET / pages#home
pokemon_set_pokemon_cards GET /sets/:pokemon_set_id/cards(.:format) pokemon_cards#index
pokemon_set_pokemon_card GET /sets/:pokemon_set_id/cards/:id(.:format) pokemon_cards#show
pokemon_sets GET /sets(.:format) pokemon_sets#index
pokemon_set GET /sets/:id(.:format) pokemon_sets#show
class PokemonSetsController < ApplicationController
before_action :find_pokemon_set, only: [:show]
def index
#pokemon_sets = PokemonSet.all
end
def show
end
private
def find_pokemon_card
#pokemon_card = PokemonCard.find(params[:card_id])
end
def find_pokemon_set
#pokemon_set = PokemonSet.find(params[:id])
end
def pokemon_sets_params
params.require(:pokemon_cards).permit(:name, :series, :card_id)
end
end
I have tried a few things, like specifiying the controller in the link to to use the pokemon_cards controller but that did not work.
Is not a problem with your schema, the routes does not know what is going on with schema.
Are there some problems:
Models: In your problem pokemon set needs to have many cards, so you need pokemon_set_id inside pokemon_card model no, the other way around.
Controllers: You need two controller one PokemonSetsController and other PokemonSets::PokemonCards::Controller. For the second one i suggest you to read this post: https://www.digitalocean.com/community/tutorials/how-to-create-nested-resources-for-a-ruby-on-rails-application
The route error is because you do not set pokemon_set_id, you should use this:
<%=link_to(set.name, pokemon_set_pokemon_cards_path(pokemon_set_id: #pokemon_set.id)) %>
But is better try to re-write some parts of the code following the post in the second topic.
I have a Course model:
class Course < ApplicationRecord
has_many :sub_courses
validates :title, presence: true
# Course associated to SubCourse via 'sub_course_id' on Course table
end
And a SubCourse model:
class SubCourse < ApplicationRecord
belongs_to :course
# SubCourse associated to Course via 'course_id' on SubCourse table
end
On the courses.show.html (specific course page e.g. admin/courses/1) I have a button that links to a new sub course page
%table
%tr
%td= #course.title
%td= #course.description
= button_to "Add New Sub Course", new_admin_sub_course_path(course_id: #course.id), method: :post
The new sub_course page sub_courses.new.html form.
= form_for #sub_course, url: admin_sub_courses_path do |f|
= f.label :title
= f.text_field :title
= f.label :description
= f.text_field :description
= f.submit
When going to the sub course new page I see the error No route matches [POST] "/admin/sub_courses/new"
My sub_course_controller.rb looks like this:
def new
#course = Course.find(params.require(:course_id))
#sub_course = #course.sub_course.new
end
def create
if #sub_course.save
redirect_to admin_sub_courses_path, notice: "saved"
else
render "new"
end
end
And my routes looks like this:
namespace :admin do
resources :courses, { :only => [:index, :new, :create, :edit, :destroy, :update, :show] }
resources :sub_courses
end
How do I successfully create a sub_course thats automatically associated with its course from the original show page I came from?
Schema structure looks like this:
create_table "courses", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "title"
t.string "description"
t.integer "sub_course_id"
end
create_table "sub_courses", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "question"
t.string "possible_answer"
t.string "correct_answer"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "course_id"
end
After running rake routes for sub courses:
admin_sub_courses GET /admin/sub_courses(.:format) admin/sub_courses#index
POST /admin/sub_courses(.:format) admin/sub_courses#create
new_admin_sub_course GET /admin/sub_courses/new(.:format) admin/sub_courses#new
edit_admin_sub_course GET /admin/sub_courses/:id/edit(.:format) admin/sub_courses#edit
admin_sub_course GET /admin/sub_courses/:id(.:format) admin/sub_courses#show
PATCH /admin/sub_courses/:id(.:format) admin/sub_courses#update
PUT /admin/sub_courses/:id(.:format) admin/sub_courses#update
DELETE /admin/sub_courses/:id(.:format) admin/sub_courses#destroy
First of all I would check the result of rake routes |grep sub_courses.
At a first glance, though, it seems to me there is a problem of value assignment in the sub_course controller.
I would try to refactor it as follows:
before_action :set_course, only: [:new, :create]
def new
#sub_course = #course.sub_courses.new
end
def create
#sub_course = #course.sub_courses.new(sub_course_params)
if #sub_course.create!(sub_course_params)
redirect_to admin_sub_courses_path, notice: "saved"
else
render "new"
end
end
private
def sub_course_params
params.require(:sub_course).permit(:title, :description, :question, :possible_answer, :correct_answer, :course_id)
end
def set_course
#course = Course.find(params[:course_id])
end
As you see, I removed the line with Course.find(params.require(:course_id)) with the the correct syntax for finding a record through a params.
The params.require syntax is instead used by strong parameters, which need to be defined, in order to actually persist data from forms into records in the database.
See https://api.rubyonrails.org/classes/ActionController/StrongParameters.html
I then moved the #course assignment into a private method set_course This way the assignment is done just once and is shared by the methods that need it.
I have two models - ProjectSite and Project.
I uploaded a file in project_sites table, now I want to access that file in projects#index. But I'm getting an error
Couldn't find ProjectSite without an ID
There is one to many association between Project and ProjectSite.
projects_controller.rb
before_action :find_project_sites?
def find_project_sites?
#project_sites = ProjectSite.find(params[:project_site_id])
end
in controller view
<% #project_sites.each do |project_site| %>
<% end %>
here is schema.rb
create_table "project_sites", force: :cascade do |t|
t.string "attendance"
t.integer "user_id"
t.integer "project_id"
t.index ["project_id"], name: "index_project_sites_on_project_id"
t.index ["user_id"], name: "index_project_sites_on_user_id"
end
create_table "projects", force: :cascade do |t|
t.string "project_name"
t.datetime "created_at", null: false
t.integer "user_id"
end
routes.rb
resources :project_sites
resources :projects
devise_for :users
project.rb (model)
has_many :projects
has_many :project_sites
When resolving action project#index your params are empty, so params[:project_site_id] will be obviously nil, as you're operating on collection, not a single object.
I'm still not sure what you're trying to reach, but I guess you're trying to set up has_many association on Project model with ProjectSite.
After proper setting up association in models, in #index you can reach project_sites for each of indexed projects:
Project.all.each do |project|
project.project_sites
end
On member actions (#show, #create, #edit, #update etc.) you need to set up project
#project = Project.find(:id)
It might be done in :befor_action but restricted to only: [:show, :edit, :create, :update, :destroy]. Next you can get associated sites by #project.project_sites
I have a nested resource with 'posts' containing many 'comments' and associations set up between these models. But when I create a comment for a post the 'post_id' in the comments table remains empty and no link is established. The comment text itself gets created ok.
I'm using Rails ver 4.2.1 and a postgresql database.
The associations are set up like this:
class Comment < ActiveRecord::Base
belongs_to :post
end
class Post < ActiveRecord::Base
has_many :comments, dependent: :destroy
end
This is the route set up:
resources :posts do
resources :comments
end
I create the comments from the comments/new view with this code:
= form_for [#post, #comment] do |f|
= f.label :comment
= f.text_field :comment
= f.submit "Add Comment"
My comments controller is like this:
class CommentsController < ApplicationController
def new
#post = Post.find(params[:post_id])
#comment = Comment.new
end
def create
#post = Post.find(params[:post_id])
#comment = Comment.create(comment_params)
redirect_to posts_path
end
def comment_params
params.require(:comment).permit(:comment)
end
end
I have the column 'post_id' set up in the comments table and my schema is this:
ActiveRecord::Schema.define(version: 20150404204033) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "comments", force: :cascade do |t|
t.string "comment"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "comments", ["post_id"], name: "index_comments_on_post_id", using: :btree
create_table "posts", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "comments", "posts"
end
Just can't work out what is going on, I've used almost identical code on another project and that worked.
Any help would be great.
In this code:
def create
#post = Post.find(params[:post_id])
#comment = Comment.create(comment_params)
redirect_to posts_path
end
you find the post but never do anything with it. The comment has no knowledge of that post. You need to set comment's post to #post.
I have 2 models: brand and coupon, coupon is nested within brand. On the index page for coupon, I want to show all coupons with the current brand id.
Example on /brand/1/coupons - want to show all the coupons with brand id = 1
Code is below
create_table "brands", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "logo"
end
create_table "coupons", force: true do |t|
t.integer "brand_id", limit: 255
t.string "code"
t.date "expiry"
t.string "link"
t.string "details"
t.datetime "created_at"
t.datetime "updated_at"
end
class Brand < ActiveRecord::Base
has_many :coupons
accepts_nested_attributes_for :coupons
end
class Coupon < ActiveRecord::Base
belongs_to :brand
end
class CouponsController < ApplicationController
before_action :set_coupon, only: [:show, :edit, :update, :destroy]
def index
#coupons = Coupon.find(params[:brand_id]
end
end
Error I get when going to brands/1/coupons ...
NoMethodError in CouponsController#index
undefined method `each' for #<Coupon:0x000001068e8f58>
else
match = match_attribute_method?(method.to_s)
match ? attribute_missing(match, *args, &block) : super
end
end
Assuming your routes are setup correctly, this should work:
#coupons = Coupon.where(brand_id: params[:brand_id])
config/routes.rb
resources :brands do
resources :coupons
end
in controller
#coupons = Coupon.all
or
#coupons = Coupon.where(brand_id: params[:id])
in view
#coupons.each do |coupon|
coupon.brand_id
or
coupon.brand.id # but in this case your will have N+1 problem