I am new to Ruby and Rails, trying to fix an error I constantly get. Not sure how to fix it. Please help..
Route.rb
namespace :my do
namespace :account do
resource :details, :only => [:show, :update]
resources :addresses
end
end
AddressesController
class My::Account::AddressesController < MyController
def index
#addresses = current_user.addresses
end
def new
#address = current_user.addresses.new
end
....
end
Error - undefined method `addresses'
NoMethodError in My::Account::AddressesController#index
undefined method `addresses' for #<User:0x007fc955029380>
Schema.rb for customer addresses and users
create_table "customer_addresses", force: true do |t|
t.integer "customer_id"
t.string "name"
t.string "line_1"
t.string "line_2"
t.string "line_3"
t.string "line_4"
t.string "line_5"
t.string "postcode"
t.string "phone"
t.datetime "deleted_at"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "customer_addresses", ["customer_id"], name: "index_customer_addresses_on_customer_id"
create_table "users", force: true do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "password_digest"
t.datetime "created_at"
t.datetime "updated_at"
t.string "password_reset_token"
t.datetime "password_reset_token_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
User.rb
class User < ActiveRecord::Base
validates :email, :presence => true, :uniqueness => true
has_secure_password
...
end
Customer.rb
class Customer < User
has_many :addresses
def self.register(attributes)
customer = create!(attributes)
return customer
end
def full_name
"#{first_name} #{last_name}"
end
end
Address.rb
class Customer::Address < ActiveRecord::Base
belongs_to :customer
self.table_name = 'customer_addresses'
default_scope { where(:deleted_at => nil) }
validates :line_1, :postcode, :phone, :presence => true
end
You need to define the relationship on both models.
class User < ActiveRedord::Base
has_many :addresses, class_name: 'CustomerAddress', foreign_key: 'customer_id'
end
class Address < ActiveRecord::Base
belongs_to :user, foreign_key: 'customer_id'
end
Add a #current_customer method to your ApplicationController that return a Customer-instance instead of a User-instance:
class ApplicationController
#…
private
def current_customer
current_user && Customer.find_by_id(current_user.id)
end
end
then change your code like this:
class My::Account::AddressesController < MyController
def index
#addresses = current_customer.addresses
end
def new
#address = current_customer.addresses.new
end
#…
end
Related
I am doing some refactoring and I have seen this project for a while and it worked from what I last recall. But the issue is, I am trying to create a flight and I keep getting "ActiveModel::MissingAttributeError (can't write unknown attribute flights_count):" when trying create a new flight.
As far my models in place:
My Flight, Pilot models
class Flight < ActiveRecord::Base
has_many :passengers
belongs_to :destination
belongs_to :pilot, counter_cache: true
accepts_nested_attributes_for :passengers
belongs_to :user, class_name: "Flight" ,optional: true
validates_presence_of :flight_number
validates :flight_number, uniqueness: true
scope :order_by_flight_international, -> { order(flight_number: :asc).where("LENGTH(flight_number) > 3") }
scope :order_by_flight_domestic, -> { order(flight_number: :asc).where("LENGTH(flight_number) <= 2 ") }
def dest_name=(name)
self.destination = Destination.find_or_create_by(name: name)
end
def dest_name
self.destination ? self.destination.name : nil
end
def pilot_name=(name)
self.pilot = Pilot.find_or_create_by(name: name)
end
def pilot_name
self.pilot ? self.pilot.name : nil
end
end
class Pilot < ActiveRecord::Base
belongs_to :user, optional: true
has_many :flights
has_many :destinations, through: :flights
validates_presence_of :name, :rank
validates :name, uniqueness: true
scope :top_pilot, -> { order(flight_count: :desc).limit(1)}
end
Edit
Flight Controller
class FlightsController < ApplicationController
before_action :verified_user
layout 'flightlayout'
def index
#flights = Flight.order_by_flight_international
#dom_flights = Flight.order_by_flight_domestic
end
def new
#flight = Flight.new
10.times {#flight.passengers.build}
end
def create
#flight = Flight.new(flight_params)
# byebug
if #flight.save!
redirect_to flight_path(current_user,#flight)
else
flash.now[:danger] = 'Flight Number, Destination, and Pilot have to be selected at least'
render :new
end
end
private
def flight_params
params.require(:flight).permit(:flight_number,:date_of_flight, :flight_time, :flight_id, :destination_id, :pilot_id, :pilot_id =>[], :destination_id =>[], passengers_attributes:[:id, :name])
end
end
Edit
Flights, Pilot Schemas
create_table "flights", force: :cascade do |t|
t.integer "pilot_id"
t.integer "destination_id"
t.string "flight_number"
t.string "date_of_flight"
t.string "flight_time"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "pilots", force: :cascade do |t|
t.string "name"
t.string "rank"
t.integer "user_id"
t.integer "flight_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "flight_count", default: 0
end
As I said before when I last worked on this project everything was working fine, but I am faced with this issue. What am I doing wrong this time.
You have defined a counter_cache in your Flight model for pilots. When you just use counter_cache: true to define it, ActiveRecord will look for a column named flights_count in your pilots table but I see that you have named it as flight_count instead. You can either rename the column to flights_count or pass the custom column name to it by using counter_cache: :flight_count
Source https://guides.rubyonrails.org/association_basics.html#options-for-belongs-to-counter-cache
I have two models: Account and Profile.
I want render json API with attributes of Profile and include there one attribute from Account.
profile_serializer.rb
class ProfileSerializer < ActiveModel::Serializer
attributes :first_name, :middle_name, :last_name, :role
def role
#UserAccountSerializer.new(object.role)
object.account.role
end
end
account_serializer.rb
class UserAccountSerializer < ActiveModel::Serializer
attributes :role
end
profile.rb
class Profile < Grape::API
desc 'Current user profile'
get '/', serializer: ProfileSerializer do
current_user.profile
end
end
accounts_controller.rb
class AccountsController < AdminController
def index
#accounts = Account.all
end
def show
render json: #account
end
def update
#account = Account.find(params[:id])
redirect_to accounts_path if #account.update(role: params[:role])
end
end
account.rb
class Account < ApplicationRecord
belongs_to :profile
enum role: %i[user admin]
after_initialize :set_default_role, if: :new_record?
def set_default_role
self.role ||= :user
end
end
profile.rb
class Profile < ApplicationRecord
has_one :account
end
schema.rb
create_table "accounts", force: :cascade do |t|
t.string "email", default: "", null: false
t.bigint "profile_id", null: false
t.integer "role", default: 0
t.index ["email"], name: "index_accounts_on_email", unique: true
t.index ["profile_id"], name: "index_accounts_on_profile_id"
end
create_table "profiles", force: :cascade do |t|
t.string "last_name", null: false
t.string "first_name", null: false
t.string "middle_name"
end
Grape give me this error: undefined method `role' for nil:NilClass
hey guys im working on a application where a devise user sign ups and logs in, Once the user logs in they can 'create a team' or 'join a team'. I have my associations set up like this
user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
validates_presence_of :phone, :city, :state, :street, :zip, presence: true, on: :create
belongs_to :team
end
team.rb
class Team < ApplicationRecord
has_many :users
end
and my tables are set up
schema.rb
create_table "teams", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "team_name"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "firstname"
t.integer "team_id"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
team_controller.rb
class TeamController < ApplicationController
before_action :authenticate_user!
def index
#team = current_user.team
end
def new_team
end
def create_team
#team = current_user.create_team(sanitize_team)
if #team.save
redirect_to team_root_path
else
render json: #team.errors.full_messages
end
end
def join_team
#teams = Team.all
end
def team
end
private
def sanitize_team
params.require(:team).permit(:team_name, :team_statement)
end
end
I want the users 'team_id' attribute to update with the teams id when they create a team. or when they join a team. Are my associations correct? how would i make this happen in the controller ?
Yes, associations are correct. You can do it better only by adding foreign key to your database schema. It can be done by generator rails g migration AddTeamToUsers team:references
More information about associations can be found here: https://guides.rubyonrails.org/association_basics.html
In controller you have to change only the whitelisting params to allow team_id. And you probably need to add to your form in view something like this:
<%= f.select :team_id, Team.all.map { |t| [t.team_name, t.id] } %>
Let's strip your code example down to the minimum required:
# app/models/team.rb
class Team < ApplicationRecord
has_many :users
end
# app/models/user.rb
class User < ApplicationRecord
belongs_to :team
end
# db/migrate/20181124230131_create_teams.rb
class CreateTeams < ActiveRecord::Migration[5.2]
def change
create_table :teams do |t|
t.string :team_name
t.timestamps
end
end
end
# db/migrate/20181124230136_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.belongs_to :team
t.timestamps
end
end
end
Then in your controller:
team = Team.where(team_name: 'foo').first_or_create!
team.users << current_user
Start by setting the association up as optional:
class User < ApplicationController
belongs_to :team, optional: true
end
Otherwise the validations on the user model will not let the user be saved without a team.
Then setup the teams resource:
# config/routes.rb
resources :teams do
post :join
end
post :join creates an additional POST /teams/:team_id/join route.
Then setup the controller:
class TeamsController
# ...
# GET /teams/new
def new
#team = Team.find
end
# POST /teams
def create
#team = Team.new(team_params)
if #team.save
unless current_user.team
current_user.update(team: #team)
end
redirect_to 'somewhere'
else
render :new
end
end
# ...
def join
#team = Team.find(params[:team_id])
if current_user.update(team: #team)
redirect_to #team, notice: 'Team joined'
else
redirect_to #team, error: 'Could not join team'
end
end
#
private
def team_params
params.require(:team).permit(:team_name, :team_statement)
end
end
Note that prefixing your action names is neither needed nor compatible with the "Rails way". Prefixing column names is also largely superfluous.
I'm trying to create an association between two models in my Rails app (User and Coin) where Coin belongs_to User and User has_many Coins. When I add the belongs_to association in the Coin model, I am no longer able to edit or create Coin pages. Why would it do this? As soon as I remove the association, I'm able to create/edit again. Also, the corresponding has_many association on the User page does not have the same effect. I'd appreciate any help in understanding what is happening here and how I can properly make this association. Thanks.
User.rb
class User < ApplicationRecord
acts_as_votable
has_many :questions, dependent: :destroy
has_many :events, dependent: :destroy
has_many :links, dependent: :destroy
has_many :posts, dependent: :destroy
has_many :moderated_coins, class_name: "Coin"
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:validatable, authentication_keys: [:login]
validates :username, presence: :true, uniqueness: { case_sensitive: false }
validates_format_of :username, with: /^[a-zA-Z0-9_\.]*$/, :multiline => true
validate :validate_username
def validate_username
if User.where(email: username).exists?
errors.add(:username, :invalid)
end
end
def login=(login)
#login = login
end
def login
#login || self.username || self.email
end
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
elsif conditions.has_key?(:username) || conditions.has_key?(:email)
where(conditions.to_h).first
end
end
end
Coin.rb
class Coin < ApplicationRecord
validates :currency_name, presence: true
has_many :questions, dependent: :destroy
has_many :events, dependent: :destroy
has_many :links, dependent: :destroy
mount_uploader :picture, PictureUploader
has_and_belongs_to_many :genres
# belongs_to :moderator, class_name: "User". <--- * The problem is here
validate :picture_size
private
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "Picture must be smalled than 5MB.")
end
end
end
coins_controller.rb
class CoinsController < ApplicationController
load_and_authorize_resource param_method: :question_params
before_action :find_coin, only: [:edit, :update, :destroy ]
before_action :authenticate_user!, except: [:index, :create, :show]
def index
#search = Coin.ransack(params[:q])
#coins = #search.result(distinct: true)
end
def new
#coin = Coin.new
end
def create
#coin = Coin.new(coin_params)
if #coin.save
flash[:success] = "Coin created"
redirect_to #coin
else
render 'new'
end
end
def show
#coin = Coin.find(params[:id])
end
def edit
authorize! :update, #coin
end
def update
if #coin.update(coin_params)
redirect_to #coin
else
render 'edit'
end
end
def destroy
Coin.find(params[:id]).destroy
redirect_to coins_url
end
private
def coin_params
params.require(:coin).permit( :currency_name, :currency_abbrev, :moderator_id, :accepted, :picture, :question1, :question2, :question3, :question4, genre_ids:[])
end
def find_coin
#coin = Coin.find(params[:id])
end
end
user_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #user }
end
end
end
schema.rb
create_table "coins", force: :cascade do |t|
t.string "link_name"
t.string "currency_name"
t.string "currency_abbrev"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "permalink"
t.boolean "accepted", default: false
t.datetime "accepted_at"
t.string "genre"
t.integer "genre_id"
t.integer "moderator_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "username"
t.string "wallet"
t.boolean "admin", default: false
t.boolean "moderator", default: false
t.decimal "currentbalance", precision: 8, scale: 2
t.decimal "payout_to_date", precision: 8, scale: 2
t.text "bio"
t.string "link1"
t.string "link2"
t.string "link3"
t.string "link4"
t.string "link5"
t.string "name"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["username"], name: "index_users_on_username", unique: true
end
Use:
belongs_to :moderator, class_name: "User", optional: true
In rails 5, belongs_to enforces existence of the associated record by default. You need to use optional: true in order to allow moderator_id to be nil.
My models
CAR BRANDS MODEL
class CarBrand < ActiveRecord::Base
has_many :car_ads
end
CAR ADVERTISEMENTS MODEL
class CarAd < ActiveRecord::Base
has_one :car_brand
end
my controller:
def index
#car_ads = CarAd.all.order("car_ads.created_at DESC")
end
car ads migrations:
class CreateCarAds < ActiveRecord::Migration
def up
create_table :car_ads do |t|
t.integer "user_id"
t.integer "car_brand_id"
t.integer "car_model_id"
t.integer "state_id", :limit => 2
t.integer "vin_id"
t.integer "year_manufac", :precision => 4
t.integer "km_age"
t.integer "price_usd", :limit => 7
t.integer "car_tel_number", :precision => 8
t.float "motor_volume", :limit => 10
t.string "transmission"
t.integer "horse_power", :limit => 3
t.text "description"
t.boolean "visible", :default => true
t.boolean "active", :default => true
t.string "photo_file_name"
t.string "photo_content_type"
t.integer "photo_file_size"
t.datetime "photo_updated_at"
t.timestamps null: false
end
add_index :car_ads, :user_id
add_index :car_ads, :car_brand_id
add_index :car_ads, :car_model_id
add_index :car_ads, :state_id
add_index :car_ads, :vin_id
end
def down
drop_table :car_ads
end
end
Car brands migratiions
class CreateCarBrands < ActiveRecord::Migration
def up
create_table :car_brands do |t|
t.string "brand", :limit => 20
t.timestamps null: false
end
end
def down
drop_table :car_brands
end
end
so the problem is that i cant get car brand form car ads, please help,
i wanted to get that like
iterating
<% #car_ads.each do |carad|%>
<%= carad.car_brand %>
<%end%>
Modify CAR ADVERTISEMENTS MODEL
class CarAd < ActiveRecord::Base
belongs_to :car_brand
end
Modify your controller:
def index
#car_ads = CarAd.all.order("created_at DESC")
end
You didn't add any reference to CarBrand in CarAd table, just add the car_ad_id column with a migration like this
class AddCarBradIdToCarAd < ActiveRecord::Migration
def change
add_column :car_ads, :car_brand_id, :integer
end
end
So rails would be able to get the corresponding CarBrand from a CarAd