Accessing one-to-many association from console - ruby-on-rails

I have a one-to-many association between 2 resources: Discovery and Matter
class Discovery < ActiveRecord::Base
belongs_to :matter
end
class Matter < ActiveRecord::Base
has_many :discoveries
end
My routes file has this:
resources :matters do
resources :discoveries
end
My migration files look like:
class CreateDiscoveries < ActiveRecord::Migration
def change
create_table :discoveries do |t|
t.string :aws_url
t.string :upload_file_path
t.attachment :upload
t.integer :matter_id
t.string :direct_upload_url
t.boolean :processed
t.timestamps
end
end
end
class AddMatterIdToDiscoveries < ActiveRecord::Migration
def change
add_index :discoveries, :matter_id
add_index :discoveries, :processed
end
end
discoveries_controller.rb
def create
#matter = Matter.find(params[:matter_id])
if(params[:url])
#discovery = Discovery.new
render "new" and return
end
if(params[:discovery][:upload_file_path])
#discovery = Discovery.new(discovery_params)
respond_to do |format|
if #discovery.save
#discovery.matter = current_user.matters.find(params[:matter_id])
format.html { render action: :show, notice: 'Discovery was successfully created.' } # matter_url(#discovery.matter_id)
format.json { render action: 'show', status: :created, location: #discovery }
else
format.html { render action: 'new' }
format.json { render json: #discovery.errors, status: :unprocessable_entity }
end
# redirect_to new_document and return
end
else
#discovery = Discovery.new
render action: 'new', notice: "No file"
end
end
When I create a new discovery in the matters model matters/3/discoveries/new the discovery gets created, but in the console, I thought I should be able to access Discovery.last.matter, but instead I get the error NoMethodError: undefined method 'matter' for #<Discovery:0x0000000495dc98>
How would I go about showing the matter that the discovery belongs to? Thanks

Call reload! in the console after changing your models (schema changes, running migrations, adding methods).

Related

Rails PG::ForeignKeyViolation: ERROR: insert or update on table

I've got two models with simple belongs_to has_many relation, as follows:
class Property < ApplicationRecord
belongs_to :portfolio, optional: true
end
class Portfolio < ApplicationRecord
has_many :properties, dependent: :nullify
end
Which were created by migrations:
class CreateProperties < ActiveRecord::Migration[6.0]
def change
create_table :properties do |t|
t.string :name, null: false
t.string :status, null: false
t.references :portfolio, foreign_key: true
t.timestamps
end
end
end
class CreatePortfolios < ActiveRecord::Migration[6.0]
def change
create_table :portfolios do |t|
t.string :name, null: false
t.timestamps
end
end
end
When I want to create new Property with portfolio_id: 1 which doesn't exist I'm getting an error:
PG::ForeignKeyViolation: ERROR: insert or update on table "properties" violates foreign key constraint "fk_rails_760fb8258a" DETAIL: Key (portfolio_id)=(1) is not present in table "portfolios".
How to handle such error and display to user information such as "portfolio_id doesn't exist" ?
Create action is pretty standard:
property_controller.rb
def create
#property = Property.new(property_params)
respond_to do |format|
if #property.save
format.html { redirect_to #property, notice: 'Property was successfully created.' }
format.json { render :show, status: :created, location: #property }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #property.errors, status: :unprocessable_entity }
end
end
end
The error makes sense, since you're trying to create a property with a portfolio_id that doesn't exist. Since you added the optional: true to belongs_to :portfolio in the Property class, you removed the validation that normally comes with the belongs_to.
The best way of avoiding that would be to display a dropwdown in the form for a new portfolio that contains all the possible properties. That would also be better in terms of UX because the user would not have to remember the id of a property.

How to assign a user id to a record in the backend?

I want to make sure that when I create a record in the front end, the user id of the user who created it is automatically assigned to this record. What should I do in the backend if I want the id of the authorized user to be automatically assigned to the record when creating the record? For authorization I use gem 'devise_token_auth'.
notebooks_controller.rb:
class NotebooksController < ApplicationController
before_action :set_notebook, only: [:show, :update, :destroy]
def index
#notebooks = Notebook.all
render json: #notebooks
end
def show
render json: #notebook
end
def create
#notebook = Notebook.new(notebooks_params)
if #notebook.save
render json: #notebook, status: :created, location: #notebook
else
render json: #notebook.errors, status: :unprocessable_entity
end
end
def update
if #notebook.update(notebooks_params)
render json: #notebook
else
render json: #notebook.errors, status: :unprocessable_entity
end
end
def destroy
#spr_contract_solution.destroy
end
private
def set_notebook
#notebook = Notebook.find(params[:id])
end
def notebooks_params
params.require(:notebook).permit!
end
end
notebook.rb:
class Notebook < ApplicationRecord
belongs_to :user
end
...._create_notebooks.rb
class CreateNotebooks < ActiveRecord::Migration[5.1]
def change
create_table :notebooks do |t|
t.string :title
t.text :description
t.boolean :is_active
t.references :user, foreign_key: true
t.timestamps
end
end
end
First, #Subash is right, you need to pass the list of parameters to the permit method in notebook_params (note that maybe you would want to use permit instead of permit!), for example:
params.require(:notebook).permit :name, :text, :any_other_attribute_you_are_saving
Then, to answer your question, you could do something like this in the create action:
#notebook = Notebook.new(notebooks_params)
#notebook.user = current_user #Assuming you have this method available
Assuming you have has_many :notebooks in your user model, this is a popular idiom for doing what you want:
#notebook = current_user.notebooks.build(notebook_params)
add
#notebook.user = current_user
after
#notebook = Notebook.new(notebooks_params)

No such column for dependent :destroy on has_many, belongs_to relationship

I have a simple has_many, belongs_to association.
class Actor < ActiveRecord::Base
belongs_to :movie
end
and
class Movie < ActiveRecord::Base
has_many :actors, dependent: :destroy
after_save :fill_actors_table
validates_presence_of :title
def fill_actors_table
movie_list = Imdb::Search.new("Lion King")
new_movie = movie_list.movies.first
id = new_movie.id
i = Imdb::Movie.new("#{id}")
i.cast_members.each do |actor_name|
actor_image = Google::Search::Image.new(:query => actor_name).first
actor_image_url = actor_image.uri
Actor.create(:name => actor_name, :file => actor_image_url)
end
end
My schema looks like this:
ActiveRecord::Schema.define(version: 20150821182841) do
create_table "actors", force: true do |t|
t.string "name"
t.string "file"
t.integer "actor_id", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "movies", force: true do |t|
t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
end
end
But I keep getting an error
SQLite3::SQLException: no such column: actors.movie_id: SELECT "actors".* FROM "actors" WHERE "actors"."movie_id" = ?
I don't use movie_id anywhere!!!
Movie Controller Code:
class MoviesController < ApplicationController
before_action :set_movie, only: [:show, :edit, :update, :destroy]
# GET /movies
# GET /movies.json
def index
#movies = Movie.all
end
# GET /movies/1
# GET /movies/1.json
def show
end
# GET /movies/new
def new
#movie = Movie.new
end
# GET /movies/1/edit
def edit
end
# POST /movies
# POST /movies.json
def create
#movie = Movie.find_or_create_by(movie_params)
respond_to do |format|
if #movie.save
format.html { redirect_to #movie, notice: 'Movie was successfully created.' }
format.json { render :show, status: :created, location: #movie }
else
format.html { render :new }
format.json { render json: #movie.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /movies/1
# PATCH/PUT /movies/1.json
def update
respond_to do |format|
if #movie.update(movie_params)
format.html { redirect_to #movie, notice: 'Movie was successfully updated.' }
format.json { render :show, status: :ok, location: #movie }
else
format.html { render :edit }
format.json { render json: #movie.errors, status: :unprocessable_entity }
end
end
end
# DELETE /movies/1
# DELETE /movies/1.json
def destroy
#movie.destroy
respond_to do |format|
format.html { redirect_to movies_url, notice: 'Movie was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_movie
#movie = Movie.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def movie_params
params.require(:movie).permit(:title)
end
end
Actors controller was just generated and really is the same as the movie controller for the most part.
What I'm trying to accomplish:
A movie is searched. The movie name is saved in the movie database. Then it pulls a list of actors in the movie using the imdb gem and searches for their images using the google-search gem. The image url's and actor names are saved in the actor database.
I have noticed that when I put in movies, it sometimes seems to list the actors names twice (as if there are two for loops). I can't figure out where I have code that could possibly make it run twice.
This is really the only code I've written in the whole project other than a basic form.
You need the foreign key in the model with the belongs_to.
When you use:
belongs_to :some_model
rails assumes :some_model_key is in the model. You have no :movie_id in your actors model, so when you try to reference a movie's actors rails looks for the :movie_id in your actor model and can't find it. You can add this column with an active migration.

ActiveRecord::StatementInvalid in EventsController#create

After adding new column events_count to table users, I get this error:
SQLite3::SQLException: no such column: events_count: UPDATE "users" SET "events_count" = COALESCE("events_count", 0) + 1 WHERE "users"."id" = 2
This is extracted source with a line with bug:
#event.user_id = current_user.id
respond_to do |format|
**if #event.save**
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: #event }
else
This is my users table:
create_table "users", force: true do |t|
.
.
.
.
t.integer "events_count", default: 0
end
This is html with ranking from users_path:
<tr>
<% User.all.each do |user| %>
<td> <%= user.name %></td>
<td> <%= user.events_count %></br></td>
<% end %>
</tr>
This is Event model:
class Event < ActiveRecord::Base
acts_as_commontable
mount_uploader :picture, PictureUploader
acts_as_votable
belongs_to :user, dependent: :destroy,counter_cache: true
end
Anyone know what may be wrong?
EDIT:
This is migration file:
class AddEventsCountToUsers < ActiveRecord::Migration
def change
add_column :users, :events_count, :integer, :default => 0
end
end
This is Event controller:
# POST /events
# POST /events.json
def create
#event = Event.new(event_params)
#event.user_id = current_user.id
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: #event }
else
format.html { render :new }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
# Never trust parameters from the scary internet, only allow the white list through.
def event_params
params.require(:event).permit(:title, :description, :picture, :start_date, :start_time, :end_time, :location, :user_id, :city)
end
end
Seems like you just added event_counts instead of events_count that to an already existing create_users.rb migration file.
you have to perform a query which generates a migration file for an extra column events_count
rails g migration AddEventsCountToUsers events_count:integer
Do rake db:migrate after that
OR
Do rake db:rollback,add that column in the create_users.rb migration file and do rake db:migrate
As mentioned in the comments,try running rake db:migrate:up VERSION=20140511122817
Edit user table as:
create_table "users", force: true do |t|
.
.
.
.
t.integer :events_count, default: 0
end
And run these commands:-
rake db:drop
rake db:create
rake db:migrate
Note :- It will delete all data from your datasase

Rails associated model id on create

I have 2 models, a sport model and a team model. The team model belongs_to :sport and the sport model has_many :teams.
Sport model:
class Sport < ActiveRecord::Base
has_many :teams
has_many :competitions
has_many :games
end
Team Model:
class Team < ActiveRecord::Base
belongs_to :sport
has_many :competition_teams
has_many :competitions, :through => :competition_teams
has_many :home_games, :foreign_key => "home_team_id", :class_name => "Game"
has_many :visiting_games, :foreign_key => "visiting_team_id", :class_name => "Game"
end
When a new team is created it must always associate with a sport. So for example if Hockey has an ID of 1, the team that is created under hockey must contain the sport ID. Below is the current schema:
create_table "sports", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "teams", force: true do |t|
t.string "name"
t.integer "sport_id"
t.datetime "created_at"
t.datetime "updated_at"
end
This is the teams controller:
class TeamsController < ApplicationController
before_action :set_team, only: [:show, :edit, :update, :destroy]
# GET /games
# GET /games.json
def index
#teams = Team.all
end
# GET /games/1
# GET /games/1.json
def show
end
# GET /games/new
def new
#team = Team.new
end
# GET /games/1/edit
def edit
end
# POST /games
# POST /games.json
def create
#team = Team.new(team_params)
respond_to do |format|
if #team.save
format.html { redirect_to #team, notice: 'team was successfully created.' }
format.json { render action: 'show', status: :created, location: #team }
else
format.html { render action: 'new' }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /games/1
# PATCH/PUT /games/1.json
def update
respond_to do |format|
if #team.update(team_params)
format.html { redirect_to #team, notice: 'team was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
# DELETE /games/1
# DELETE /games/1.json
def destroy
#team.destroy
respond_to do |format|
format.html { redirect_to sports_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_team
#team = Team.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def team_params
params[:team].permit(:name, :sport_id)
end
end
I tried to do the following in the routes:
resources :sports do
resources :teams
end
But get an error when trying to create a team from the the following URL: /sports/1/teams/new
The error is: undefined method `teams_path' for #<#:0x007fafb4b9b0c0>
app/views/teams/_form.html.erb where line #1 raised:
For your route setup:
resources :sports do
resources :teams
end
You will need to use new_sport_team_path which will map to sports/:sport_id/teams/:id/new.
And in your app/view/teams/_form.html.erb, since your route is sports/:sport_id/teams, your form_for declaration should be:
<%= form_for #comment, url: sport_teams_path ... %>
...
<% end %>
In this case sport_teams_path will route to /sports/:sport_id/teams with post method which will execute the create action in your TeamsController.
The form_for declaration above can also be written as:
<%= form_for([#sport, #team]) ... %>
...
<% end %>
In this case you'd need to define #sport and #team in your controller as follows:
# app/controllers/teams_controller.rb
def new
#sport = Sport.find(params[:sport_id])
#team = #sport.teams.build
...
end
For a list of routes defined in your application, you could run rake routes from within your application directory in the terminal.

Resources