I'm working through Code4Startup airbnb course (https://code4startup.com/projects/build-airbnb-with-ruby-on-rails-level-1) but I'm trying to adapt it for my own idea - an airbnb for education courses for my local colleges. I'm new to ruby and stackoverflow, I've tried searching for the solution for ages but my issue is that I don't know how to correctly describe it, so please explain it like I'm 5!
The error I'm receiving:
The error seems to be suggesting that my course model isn't being built correctly or that it is reverting back to the Listing model somehow (perhaps because my course_params aren't correct?)
NoMethodError in Courses#new
Showing /Users/Jack_R/code/rails/planet_study/app/views/courses/new.html.erb where line #22 raised:
undefined method `type' for #<Listing:0x007fc25da72ea8>
Extracted source (around line #22):
20
21
22
<div class="form-group">
<label>Course Type</label>
<%= f.select :type, [["English Language", "English Language"], ["Culture", "Culture"], ["Sports", "Sports"], ["Tech/Science", "Tech/Science"], ["Adventure", "Adventure"], ["Mixture", "Mixture"]],
id: "type", prompt: "Select...", class: "form-control" %>
</div>
</div>
My Models
I have a User model, a Listing model and a Course model (User>Listing>Course):
class User < ApplicationRecord
has_many :listings
has_many :courses, :through => :listings
end
Listing model:
class Listing < ApplicationRecord
belongs_to :user
has_many :courses
end
Course model:
class Course < ApplicationRecord
belongs_to :listing
end
Courses Controller
class CoursesController < ApplicationController
before_action :set_course, except: [:index, :new, :create]
before_action :authenticate_user!, except: [:show]
def index
#courses = current_user.listings.courses
end
def new
#course = current_user.listings.build
end
def create
#course = listings.build(course_params)
if #course.save
redirect_to listing_course_path(#course), notice: "Saved..."
else
render :new, notice: "Something went wrong..."
end
end
def show
def listing
end
def pricing
end
def description
end
def photo_upload
end
def amenities
end
def location
end
def update
if #course.update(course_params)
flash[:notice] = "Saved..."
else
flash[:notice] = "Something went wrong..."
end
redirect_back(fallback_location: request.referer)
end
private
def set_course
#course = Course.find(params[:id])
end
def room_params
params.require(:course).permit(:name, :type, :summary, :address, :places, :start_date, :finish_date, :price)
end
end
end
Course new.html.erb
<div class="panel panel-default">
<div class="panel-heading">
Create your course listing
</div>
<div class="panel-body">
<div class="devise-container">
<%= form_for #course do |f| %>
<div class="row">
</div>
<div class="col_md_4 select">
<div class="form-group">
<label>Course Type</label>
<%= f.select :type, [["English Language", "English Language"], ["Culture", "Culture"], ["Sports", "Sports"], ["Tech/Science", "Tech/Science"], ["Adventure", "Adventure"], ["Mixture", "Mixture"]],
id: "type", prompt: "Select...", class: "form-control" %>
</div>
</div>
<div class="col_md_4 select">
<div class="form-group">
<label>Places</label>
<%= f.select :places, [["1", 1], ["2", 2], ["3", 3], ["4", 4], ["5", 5], ["6", 6], ["7", 7]],
id: "places", prompt: "Select...", class: "form-control" %>
</div>
</div>
</div>
<div><%= f.submit "Create My Course", class: "btn btn-primary-green" %></div>
<% end %>
</div>
</div>
</div>
type is a reserved word in Rails. You can create a model with a type attribute, but you can't perform actions on this model until you rename the type attribute.
If you try to create a new record, through the rails console, you'll see a message like:
$ rails console
[1] pry(main)> Course.new(name: 'first', type: 'some type')
ActiveRecord::SubclassNotFound: The single-table inheritance mechanism
failed to locate the subclass: 'some type'. This error is raised because
the column 'type' is reserved for storing the class in case of
inheritance. Please rename this column if you didn't intend it to be
used for storing the inheritance class or overwrite
Course.inheritance_column to use another column for that information.
from
/Users/***/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/activerecord-5.1.4/lib/active_record/inheritance.rb:196:in
`rescue in find_sti_class'
As the message says, you must rename the type attribute to be able to work with it, to do it just run a migration to change this name and edit the file created, like:
$ rails generate migration rename_type_to_type_of
In the generated file use the rename_colum method, and specify first the model, then the old attribute name, and then the new one, like:
class RenameTypeToTypeOf < ActiveRecord::Migration[5.1]
def change
rename_column :courses, :type, :type_of
end
end
After that you can run rails db:migrate. Note type_of is just a poor suggestion of mine, you can adapt it as you want.
Related
I have 2 models in my rails application. User and Goal.
I have set them like this:
User Model
class User < ApplicationRecord
has_one :goal, dependent: :destroy
end
Goal Model
class Goal < ApplicationRecord
belongs_to :user, optional: true
end
Whenever I try to make a new record of the Goal model I get this error:
undefined method `new' for nil:NilClass
Here's my controller and view for the Goal model
Goals Controller
class GoalsController < ApplicationController
def index
end
def new
#goal = Goal.new
end
def create
#goal = current_user.goal.new(goal_params)
if #goal.save
redirect_to #goal
else
render 'new'
end
end
private
def goal_params
params.require(:goal).permit(:user_id, :goal_type)
end
end
Goals View (new action)
<%= form_for(#goal) do |f| %>
<div class="field">
<%= f.label :goal_type, "Would you like to..." %>
<%= f.select :goal_type, ["Loose weight", "Gain weight", "Keep current weight"] %>
</div>
<div class="field submit">
<%= f.submit "Submit", class: "button button-highlight button-block" %>
</div>
<% end %>
In my goals table I have a column called goal_type and user_id.
I need to make it so that when creating a new record the user_id field gets automatically filled with the current_user id (using devise of course).
Thanks in advance!
I just changed the controller from:
#goal = current_user.goal.new(goal_params)
to:
#goal = current_user.build_goal(goal_params)
I'm completing this airbnb clone course (https://code4startup.com/projects/build-airbnb-with-ruby-on-rails-level-1) but have diverted a bit in order to complete my own project; a marketplace for education camps. Therefore I've added an additional model. It now has User>Listing>Course.
Since adding this course I keep receiving the following error upon running the server and going to localhost:3000/courses/new I've tried searching for the problem on stackoverflow but I'm not experienced enough to describe and therefore find the issue, I'd appreciate any ideas.
Error Message
undefined method `curriculum_type' for #<Listing:0x007fb776ac0f50>
Extracted source (around line #15):
<div class="form-group">
<label>Course Type</label>
<%= f.select :curriculum_type, [["English Language", "English Language"], ["Culture", "Culture"], ["Sports", "Sports"], ["Tech/Science", "Tech/Science"], ["Adventure", "Adventure"], ["Mixture", "Mixture"]],
id: "type", prompt: "Select...", class: "form-control" %>
</div>
</div>
Models
class User < ApplicationRecord
has_many :listings
has_many :courses, :through => :listings
end
class Listing < ApplicationRecord
belongs_to :user
has_many :courses
end
class Course < ApplicationRecord
belongs_to :listing
end
Courses Controller
class CoursesController < ApplicationController
before_action :set_course, except: [:index, :new, :create]
before_action :authenticate_user!, except: [:show]
def index
#courses = current_user.listings.courses
end
def new
#course = current_user.listings.build
end
def create
#course = listings.build(course_params)
if #course.save
redirect_to listing_course_path(#course), notice: "Saved..."
else
render :new, notice: "Something went wrong..."
end
end
def show
def listing
end
def pricing
end
def description
end
def photo_upload
end
def amenities
end
def location
end
def update
if #course.update(course_params)
flash[:notice] = "Saved..."
else
flash[:notice] = "Something went wrong..."
end
redirect_back(fallback_location: request.referer)
end
private
def set_course
#course = Course.find(params[:id])
end
def room_params
params.require(:course).permit(:name, :course_type, :summary, :address, :course_places, :start_date, :finish_date, :price)
end
end
end
Courses new.html.erb
<div class="panel panel-default">
<div class="panel-heading">
Create your course listing
</div>
<div class="panel-body">
<div class="devise-container">
<%= form_for #course do |f| %>
<div class="row">
</div>
<div class="col_md_4 select">
<div class="form-group">
<label>Course Type</label>
<%= f.select :curriculum_type, [["English Language", "English Language"], ["Culture", "Culture"], ["Sports", "Sports"], ["Tech/Science", "Tech/Science"], ["Adventure", "Adventure"], ["Mixture", "Mixture"]],
id: "type", prompt: "Select...", class: "form-control" %>
</div>
</div>
<div class="col_md_4 select">
<div class="form-group">
<label>Places</label>
<%= f.select :course_places, [["1", 1], ["2", 2], ["3", 3], ["4", 4], ["5", 5], ["6", 6], ["7", 7]],
id: "places", prompt: "Select...", class: "form-control" %> -->
</div>
</div>
</div>
<div><%= f.submit "Create My Course", class: "btn btn-primary-green" %></div>
<% end %>
</div>
</div>
</div>
In Rails Console I can create all models, but it seems that it is not recognising the Courses Model when I start the server
Your controller is building a Listing object, but your curriculum_type attribute is an attribute for a Course. You'll want to modify your controller to be building a Course object if that's the case, or add the attribute to Listing.
I am having a weird issue that I can't figure out.
It is very basic rails programming : I want to create an association between a user model and a goal model.
goal.rb
class Goal < ActiveRecord::Base
belongs_to :user
end
user.rb
class User < ActiveRecord::Base
has_many :goals, dependent: :destroy
has_many :records
has_many :orders
end
When I am making the association from the console, it is working well, lets say :
$ goal = Goal.first
$ goal.user_id = 1
$ goal.save
$ goal.inspect
#<Goal id: 1, description: "loremipsum", created_at: "2016-11-26 12:39:34", updated_at: "2016-11-26 12:43:41", name: "ipsumlorem", user_id: 1>
But then, when I am creating a goal from my views, the association is not made, and the user_id of the goal user_id remain : nil.
Any ideas ?
EDIT AS REQUIRED :
_form.html.erb
<%= form_for(#goal) do |f| %>
<%= f.text_field :name, class: "form-control" %>
<%= f.text_area :description, class: "form-control" %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
goal_controller.rb
def new
#users = User.all
#goal = current_user.goals.build
end
def edit
end
def create
#goal = Goal.new(goal_params)
#goal.save
redirect_to root_path, notice: "Objectif sauvegardé"
end
def create
# Here!!!!
#goal = current_user.goals.new(goal_params)
#goal.save
redirect_to root_path, notice: "Objectif sauvegardé"
end
I'm rather new to rails and I'm stuck with this has_one and belongs_to form. I'm trying to create a team that has two speakers (from class 'User') through a form ,in the following manner:
class Team<ActiveRecord::Base
belongs_to :league
belongs_to :seed
has_many :speakers do
def user(level="1")
find_by(level: level).user
end
end
end
my user model looks like this :
class User < ActiveRecord::Base
belongs_to :team
end
user model:
class User
speaker model:
class Speaker < ActiveRecord::Base
belongs_to :team
belongs_to :user
end
my issue is (i think ) primarily in my controllers and form.controller looks like:
class TeamsController<ApplicationController
def new
#seed=Seed.find_by_id(params[:seed_id])
#league=current_admin.league
#team=current_admin.league.teams.build(:seed_id=>#seed,:approved=>false)
#usernames= #mca.connections.connected.each do |x| x.user end
end
def create
#league=current_admin.league
#team = #league.teams.build(team_params)
if #team.save
flash[:notice] = "Team Request Sent!."
redirect_to '/'
else
flash[:error] = "Unable to request team."
redirect_to :back
end
end
form looks like:
<div class="panel-body">
<div class="container">
<%= form_for #team do |f| %>
<%= f.hidden_field :seed_id, :value => #seed.id %>
<%= f.hidden_field :league_id, :value => #league.id %>
<div class="row">
<!-- <div class="col-md-8"> -->
<div class="form-group">
<%= f.collection_select :speaker, #usernames,:user,:fullname, multiple:true %>
</div>
<!-- </div> -->
</div>
<div class="actions">
<%= f.submit "Create" , class:"btn btn-primary" %>
</div>
<% end %>
</div>
</div>
I would really appreciate some help because it keeps throwing the following error:
NoMethodError in TeamsController#create
undefined method `each' for "2":String
The surface issue you have is that you're passing a string when Rails is expecting an object:
User(#69980837338020) expected, got String(#69980808947560)
This means you should be sending #user rather than "username" etc.
The error will likely be on this line:
#team = #league.teams.build team_params
... which means that you're passing :speaker (which Rails needs as an object) when you should be passing the speaker_id foreign key. Yury Lebedev's answer explains how to do this.
There is a deeper issue.
I don't see how each User can only belong to a Team:
class AddFieldsToUser < ActiveRecord::Migration
def change
add_column :users, :speaker_id, :integer
add_column :users, :speaker2_id, :integer
end
end
For this to work, your users can only be a member of one team.
Whilst this might work for a smaller scale product, I personally feel it to be an incorrect schema setup.
If anything, you'd expect the team to have speaker_1 and speaker_2, which would mean those two options being stored in the teams database (not user).
I think this is the cause of your problem (you're trying to set the speaker_1 and speaker_2 params when they don't exist in the teams db).
-
I would recommend the following:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :speaking_engagements, class_name: "Speaker"
has_many :teams, through: :speaking_engagements
end
#app/models/speaker.rb
class Speaker < ActiveRecord::Base
#columns team_id | user_id | level | created_at | updated_at
belongs_to :team
belongs_to :user
end
#app/models/team.rb
class Team < ActiveRecord::Base
has_many :speakers do
def user(level="1")
find_by(level: level).user
end
end
end
This will give you the ability to call:
#team = Team.find params[:id]
#speakers = #team.speakers
#user.speaking_engagements.where(team: #team)
To save it, you'll be able to use the following:
#app/controllers/teams_controller.rb
class TeamsController < ApplicationController
def new
...
#team = current_admin.league.teams.build seed: #seed, approved: false
end
def create
#league = current_admin.league
#team = #league.teams.build team_params
if #team.save
...
end
private
def team_params
params.require(:team).permit(:name, :speakers) #-> not sure about "speakers"
end
end
This should allow you to define the following:
#app/views/teams/new.html.erb
<%= form_for #team do |f| %>
<%= f.collection_select :speakers, #usernames, :id, :name, multiple: true %>
<%= f.submit %>
<% end %>
I have Users and each user sets a Goal. So a goal belongs_to a user and a user has_one: goal. I am trying to use form_for in the view to allow the user to set their goal up.
I figured it would be like microposts (which is straight from Hartl's tutorial) just with singular instead of plural. I've looked at tutorials and questions on here for this issue and tried numerous different things and I just can't get it working.
I got the form_for actually working and apparently pointing to the correct route, but I'm getting the below error and I have no clue what it means:
undefined method `stringify_keys' for "Lose Weight":String
GoalsController
class GoalsController < ApplicationController
before_filter :signed_in_user
def create
#goal = current_user.build_goal(params[:goal])
if #goal.save
redirect_to #user
end
end
def destroy
#goal.destroy
end
def new
Goal.new
end
end
Goal Model
class Goal < ActiveRecord::Base
attr_accessible :goal, :pounds, :distance, :lift_weight
belongs_to :user
validates :user_id, presence: true
end
User Model
class User < ActiveRecord::Base
has_one :goal, :class_name => "Goal"
end
_goal_form (which is a modal on the Users#show)
<div class="modal hide fade in" id="goal" >
<%= form_for(#goal, :url => goal_path) do |f| %>
<div class="modal-header">
<%= render 'shared/error_messages', object: f.object %>
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>What's Your Health Goal This Month?</h3>
</div>
<div class="modal-body">
<center>I want to <%= select(:goal, ['Lose Weight'], ['Exercise More'], ['Eat Better']) %> </center>
</div>
<div class="modal-body">
<center>I will lose <%= select_tag(:pounds, options_for_select([['1', 1], ['2', 1], ['3', 1], ['4', 1], ['5', 1], ['6', 1], ['7', 1], ['8', 1], ['9', 1], ['10', 1]])) %> lbs. this month!</center>
</div>
<div class="modal-footer" align="center">
Close
<%= f.submit "Set Goal", :class => "btn btn-primary" %>
</div>
<% end %>
</div>
Routes.rb
resource :goal, only: [:create, :destroy, :new]
I expect the stringify_keys error is down to the way you've listed your options in the goal field, they need to be grouped up to be treated as one argument.
Along with using accepts_nested_attributes_for :goal as suggested by Dipak, you will want to have a nested form.
form_for #user do |form|
fields_for #goal do |fields|
fields.select :goal, ['Lose Weight', 'Exercise More', 'Eat Better']
The save action is now in the context of a user, so the attributes that come through will include a portion for the goal fields:
user =>{goal_attributes => {:goal => 'Eat Better'}}
You can save these attributes by updating the user:
#user.update_attributes(params[:user])
As an aside, your "new" action should be #goal = Goal.new, just creating a new goal does nothing, you need to assign it to a variable for it to make it to the page.
Good luck!
try this
# in your user model
accepts_nested_attributes_for :goal
write above code in your user model, and
for select tag try to use from this link
http://shiningthrough.co.uk/Select-helper-methods-in-Ruby-on-Rails