I have 3 models that are basically nested.
class User < ActiveRecord::Base
attr_accessible :birthday, :name
has_one :advancement, :dependent => :destroy
accepts_nested_attributes_for :advancement
attr_accessible :advancement_attributes
end
class Advancement < ActiveRecord::Base
attr_accessible :user_id, :rank_name
belongs_to :user
has_one :rank, :dependent => :destroy
accepts_nested_attributes_for :_rank
attr_accessible :rank_attributes
end
class Rank < ActiveRecord::Base
attr_accessible :advancement_id, :one_li, :one_pi, :one_date, ...
belongs_to :advancement
end
Here is the controller code I use to create my models.
class UsersController < ApplicationController
def new
#user = User.new
#user.advancement = Advancement.new
#user.advancement.rank = Rank.new
respond_to do |format|
format.html # new.html.erb
end
end
def create
#user = User.new(params[:user])
#user.advancement = Advancement.new
#user.advancement.rank = Rank.new
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
else
format.html { render action: "new" }
end
end
end
end
In the console I can create relations that work like I would expect. When I try to do this through the browser all of my objects are instantiated, the foreign keys are correct, but if I try to find user.advancement, I get a method missing error. Am I using new or create wrong and if so what should I do? Sorry about the amount of code, but I didnt know any other way to explain it.
To start, in most of your code you reference a "rank" model but the model itself appears to be called "BoyScoutRank". Could this be the issue?
As Thanh pointed out, you have accepts_nested_attributes_for :_rank instead of :rank. You also should not need to create new Advancement and Rank models in your create. The accepts_nested_attributes should handle that for you.
Does the following work (once you fix the :_rank issue):
#user = User.new(params[:user])
Related
I am studying Belongs_to association, I have used following models, in that every order belongs to the customer, so I have used belongs_to in order model it giving error while creating order
undefined method `orders' for #
when I use has_many :orders in customer model it works fine, why it
does not work with only belongs_to
Its work with has_many :orders in customer model but not with
has_one : order in customer controller it giving same above error.
thanks in advance.
Model :- order.rb
class Order < ActiveRecord::Base
belongs_to :customer
attr_accessible :order_date, :customer_id
end
Model :- customer.rb
class Customer < ActiveRecord::Base
attr_accessible :name
end
controller :- orders.rb
def create
#customer = Customer.find_by_name(params[:name])
#order = #customer.orders.new(:order_date => params[:orderdate] )
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render json: #order, status: :created, location: #order }
else
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
Technically, belongs_to will work without a matching has_many or has_one. If, for instance, you say that Order belongs_to :customer, you can call .customer on an Order object, and get a Customer object.
What you can't do is call .orders on a Customer without telling it that it has_many :orders (or .order, in the case of has_one), because that method is created by the has_many declaration.
That said, I can't think of any reason you would ever want to only specify half of a relation. It's a terrible design choice, and you should not do it.
Edit: has_one doesn't create the .collection methods that has_many does. Per the guide:
4.2.1 Methods Added by has_one
When you declare a has_one association, the declaring class
automatically gains four methods related to the association:
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
You'll note that there's no .new on that list. If you want to add an associated object, you can use customer.build_order(), or customer.order = Order.new().
I have a many to many relationship between two models as follows:
#users.rb
has_many :users_to_roles
has_many :roles, through: :users_to_roles
#users_to_roles.rb
belongs_to :user
belongs_to :role
#roles.rb
has_many :users_to_roles
has_many :users, through: :users_to_roles
I want to disable the deletion of roles if there are users who are "in this role". Here I have found two options who should do the work:
:restrict_with_exception causes an exception to be raised if there are
any associated records :restrict_with_error causes an error to be
added to the owner if there are any associated objects
but there is no example with the syntax of this and how it should work.
Could you help to make this valid:
#roles.rb
has_many :users_to_roles
has_many :users, through: :users_to_roles, dependent: restrict_with_exception
Such operations can be easily do using Callbacks. In my case, I have added the following method in my model:
# callbacks
before_destroy :check_for_users_in_this_role
def check_for_users_in_this_role
status = true
if self.security_users.count > 0
self.errors[:deletion_status] = 'Cannot delete security role with active users in it.'
status = false
else
self.errors[:deletion_status] = 'OK.'
end
status
end
Alternatively, you can rescue the exception in your controller. In this example, a contact may own interest, i.e.
class Interest < ActiveRecord::Base
belongs_to :contact
end
class Contact < ActiveRecord::Base
has_many :interests, :dependent => :restrict
end
Then in the controller:
def destroy
#contact = Contact.find(params[:id])
begin
#contact.destroy
rescue
flash[:msg] = "Can't delete - owns interest"
end
respond_to do |format|
format.html { redirect_to(:back) }
format.xml { head :ok }
end
end
The flash message will be displayed in the calling page.
The correct rails way is to do the following:
users.rb:
has_many :users_to_roles, dependant: :destroy # don't keep the join table entry if the user is gone
has_many :roles, through: :users_to_roles
Make sure that your join does not have redundant entries (in which either column is null or orphaned).
users_to_roles.rb:
belongs_to :user
belongs_to :role
# add validations presence of both user and role
# in both model and database.
Bonus, from rails 4.2 you can add forigen_key: true in your migration for referential integrity
Now in your role (I am assuming you name your models singularly and made a typo in the question), you add this:
role.rb:
has_many :users_to_roles, dependant: :restrict_with_error
has_many :users, through: :users_to_roles
I made it with my classes like this:
app/models/guest_chat_token.rb
class GuestChatToken < ApplicationRecord
has_many :chat_messages, as: :sendable, dependent: :restrict_with_exception
end
app/controllers/admin/application_controller.rb
class Admin::ApplicationController < ApplicationController
....
rescue_from ActiveRecord::DeleteRestrictionError do |exception|
redirect_to :back, notice:
"Be aware: #{exception.message}."
end
end
I am currently building very simple Comment system on Rails. The primary models are User, Albumpost, and Comment. Users can post Albumposts. For each Albumpost, Users can add Comments to the Albumpost. As a result, a Comment belongs to a User and belongs to an Albumpost.
The problem I'm having is that even with the proper associations in my models (see below), I can't get
#comment.user.name
when I'm trying to render the comments in the albumpost 'show' page (/views/albumposts/show.html.erb). When I go to the page, I can't get #comment.user.name (doesn't understand the association) and get a
"undefined method `name' for nil:NilClass"
Oddly I can get
#comment.albumpost.content
I've double-checked my models and also added the proper foreign keys to the models. Am I doing something wrong in the controllers?
Here are my models:
class Comment < ActiveRecord::Base
attr_accessible :body, :albumpost_id, :user_id
belongs_to :albumpost
belongs_to :user
end
class Albumpost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
has_many :comments, dependent: :destroy
end
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_many :albumposts, dependent: :destroy
has_many :comments, dependent: :destroy
end
Here are the relevant parts of my Albumpost and Comments controllers:
class AlbumpostsController < ApplicationController
def show
#albumpost = Albumpost.find(params[:id])
#comments = #albumpost.comments
#comment = Comment.new
#comment.albumpost_id = #albumpost.id
#comment.user_id = current_user.id
end
end
class CommentsController < ApplicationController
def create
albumpost_id = params[:comment].delete(:albumpost_id)
#comment = Comment.new(params[:comment])
#comment.albumpost_id = albumpost_id
#comment.user_id = current_user.id
#comment.save
redirect_to albumpost_path(#comment.albumpost)
end
end
I think you should prefer setting objects to relations instead of setting their ids. For example, you should do this:
#comment.user = current_user
instead of
#comment.user_id = current_user.id
ActiveRecord will take care of setting corresponding *_id fields. I'm not sure how it handles the reverse. (it should autoload though, if I understand correctly)
Just started playing with Ruby (no IT background) and until now went it quite well. But since two days I'm stuck and don't understand what's going wrong... so many thanks already for helping me out with this problem!! The situation is as described below:
I created a currencymaster table with the following columns: currmasdesc:string, currmasiso:string.
I created a currencyrate table with the following columns: currratemasteridd:integer, currratemasteridc:integer, currraterate:decimal, currratedate:date. Whereby the column currratemasteridd reflects the Dominant Currency and the currratemasteridc reflects the Converted Currency to generate combined a currency-pair.
The models/currencymaster.rb looks like this:
class Currencymaster < ActiveRecord::Base
has_many :CurrencyRateDom, :class_name => "Currencyrate", :foreign_key => "CurrRateMasterIDD"
has_many :CurrencyRateConv, :class_name => "Currencrate", :foreign_key => "CurrRateMasterIDC"
end
The models/currencyrate.rb looks like this:
class Currencyrate < ActiveRecord::Base
belongs_to :CurrencyDominant, :class_name => 'Currencymaster' , :foreign_key => "CurrRateMasterIDD", :validate => true
belongs_to :CurrencyConverted, :class_name => 'Currencymaster' , :foreign_key => "CurrRateMasterIDC", :validate => true
end
The controllers/currencyrates_controller.rb looks like this:
class CurrencyratesController < ApplicationController
# GET /currencyrates
# GET /currencyrates.json
def index
#currencyrates = Currencyrate.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #currencyrates }
end
end
# GET /currencyrates/1
# GET /currencyrates/1.json
def show
#currencyrate = Currencyrate.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #currencyrate }
end
end
end
Now is my problem that I can't show, in the view/currencyrates/index.html.erb , the related currencymaster.currmasiso instead of the currratemasteridd & currratemasteridc stored in the table currencyrate.
I hope all information is avaibale in this question, else please let me know when other information is needed. Thanks again!
Why you don't follow conventions?
You should create a CurrencyMaster class, don't repeat "currmas" or "currrate" in your column's name. Don't override foreign keys... You code should be like this :
class CurrencyMaster < ActiveRecord::Base
has_many :currency_rate_doms
has_many :currency_rate_convs
end
It's the same in your other classes. Rails use the "convention over configuration principe. It would be better after that.
Welcome to wonderful ruby/rails world.
I have a model named User . I am using Devise for authentication.
The 'User' has many 'Profiles' and one default profile . So I have added a column called 'default' to the User model. I want to store the id of the default profile.
However the code fails at the current_user.default = statement.
Error - undefined method `default=' for #User:0x402c480
class User < ActiveRecord::Base
..........
has_many :profiles
...........
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :default
end
.......
class ProfilesController < ApplicationController
before_filter :authenticate_user! ,:except => [:show]
def create
#profile = current_user.profiles.new(params[:profile])
#profile.user = current_user
respond_to do |format|
if #profile.save
#current_user.default= #profile.id
............
end
How do I go about this ? Adding 'default' to the User model doesnt solve the problem.
I suggest you to use STI, it means add "type" column into 'profiles' table
class User < ActiveRecord::Base
has_many :profiles
has_one :profile_default
after_create do
create_profile_default
end
end
class Profile < ActiveRecord::Base
end
class ProfileDefault < Profile
end
def create
#profile = current_user.profiles.new(params[:profile])
#profile.user = current_user
respond_to do |format|
if #profile.save
...
end