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
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.
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])
I am trying to link the location and the operating tables so that I can display some data in location table in the operatings views. However, I am stuck and don't know what to do. Any help would be appreciated.
#below are models#
class Location < ApplicationRecord
has_many :operatings
end
class Operating < ApplicationRecord
belongs_to :location
end
##below are my tables##
enable_extension "plpgsql"
create_table "locations", force: :cascade do |t|
t.string "country"
t.string "supra_region"
t.string "region"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "operatings", force: :cascade do |t|
t.string "operating_company_name"
t.string "address"
t.date "year_formed"
t.string "other_operational_countries"
t.string "about_company"
t.string "current_focus"
t.string "incumbent_irm_contractor"
t.string "irm_frame_agreements"
t.text "estimated_irm_budgets"
t.integer "location_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["location_id"], name: "index_operatings_on_location_id", using: :btree
end
add_foreign_key "operatings", "locations"
###below is my operating controller###
def create
#operating = Operating.new(op_company)
if #operating.save
flash[:success] = "A recorded has been successfully Saved"
redirect_to operatings_path
else
render 'new'
end
end
####routes####
resources :offshores, :index, :show, :new, :create, :destroy
resources :locations, :index, :show, :new, :create, :destroy
Since your Location and Operating models are linked together using has_many and belongs_to, if you have an operating object in your template, you can easily access the attributes of its location:
<% #operatings.each do |operating| %>
<div>The name of its location: <%= operating.location.name %></div>
<% end %>
You need to be careful with this though. If you only fetch the operatings from the database, accessing each operating's location attribute in that each loop will trigger a separate database query for every operating item. This is called an N+1 query, and it is very inefficient. To fix the problem, make sure to pre-fetch the associated location as well when loading operatings using includes:
# in the controller
#operatings = Operating.all.includes(:location)
This way the associated locations of every operating will be fetched using just a single query.
I have just made a new model named Comments and now I'm trying to make a view so that user can leave comments from the UI(very basic stuff here). But, I'm running into this error uninitialized constant CommentsController::Comments for the life of me I can't figure out why it's throwing this error and not just rendering the page when it's clicked?
I'm going to post all the applicable code and error for clarity.
MODEL:
class Comment < ActiveRecord::Base
belongs_to :subscriber
end
CONTROLLER:
class CommentsController < ApplicationController
def new
#comments = Comments.new
end
end
ROUTES
Rails.application.routes.draw do
devise_for :users
resources :subscribers, except: :show
resources :comments
SCHEMA:
create_table "comments", force: :cascade do |t|
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "fav_drink"
t.string "visit_time"
end
VIEW:
<h2>Let us get to know you more <%= link_to 'Comment', new_comment_path %> </h2>
This is the link I've set up to click the the page where the user will leave a comment.
ERROR:
Let me know if you need to see anymore code. Thank you!
I just made a silly mistake and called Comments.new instead of Comment.new
Models are referenced singularly as they are classes. You have made a mistake of Comments.new but it should be Comment.new.
I have the following models:
class Office < ActiveRecord::Base
belongs_to :city
belongs_to :company
end
class Company < ActiveRecord::Base
has_one :acquirer
has_many :offices
has_many :cities, through: :offices
end
class City < ActiveRecord::Base
has_many :offices
end
My Offices controller is set up this way:
class OfficesController < ApplicationController
before_action :set_office, only: [:show, :edit, :update, :destroy]
respond_to :html, :json
def index
respond_with(#offices = Office.all(:include => [:company, :city]))
end
...
And my schema.rb:
create_table "cities", id: false, force: true do |t|
t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "companies", id: false, force: true do |t|
t.string "name", null: false
t.string "website"
t.datetime "created_at"
t.datetime "updated_at"
t.string "acquirer_id"
end
create_table "offices", force: true do |t|
t.boolean "headquarters"
t.string "city_id"
t.string "company_id"
t.datetime "created_at"
t.datetime "updated_at"
end
I'm not really sure what's wrong.
All I really want is to show the company_id and city_id columns. I have an Acquisitions Controller that shows these columns in JSON even without the respond_with method. So I don't understand why it works by default in that case and not in this. I'm using Rails 4.0.0 with Ruby 2.0.0.
You should always use integer for defining foreign keys.
Coming back to your question, you can use
respond_with Office.all(:include => [:company, :city])).as_json(:include => [:company,:city])
However, if you have complex JSON responses I would suggest you to look at RABL or jbuilder
I got it to work. Leaving controller as it is, I changed index.json.jbuilder from:
json.array!(#offices) do |office|
json.extract! office, :headquarters
json.url office_url(office, format: :json)
end
to:
json.array!(#offices) do |office|
json.extract! office, :headquarters, :company_id, :city_id
json.url office_url(office, format: :json)
end
Not sure what you guys think of this fix?