i have the following models
class User
has_many :projects, :through => :bids
has_many :bids, :dependent => :destroy
end
class Project
attr_accessible :name, :user_id
has_many :users, :through => :bids
has_many :bids, :dependent => :destroy
belongs_to :projectmanager, :class_name => "User", :foreign_key => "user_id"
end
class Bid
attr_accessible :project_id, user_id
belongs_to :user
belongs_to :project
end
As you can see, my Project class has both *has_many* and *belongs_to* to the same model (User)
In Project controller new I have
def new
#project = Project.new
#project.gencontr = current_user
where current_user is Devise current logged in user.
When I save the project, the column user_id in the Projects table is always null. Can you show me where do I go wrong on this one...Thank you
on create action
def create
#project = Project.new(prams[:project])
#project.projectmanager = current_user
.......
end
Related
My Models:
class Vip < ActiveRecord::Base
belongs_to :organization
has_many :events
has_many :organizations, :through => :events
end
class Organization < ActiveRecord::Base
belongs_to :user
has_many :events
has_many :vips, :through => :events
end
class Event < ActiveRecord::Base
belongs_to :organization
belongs_to :vip
end
My vips Controller:
def create
#organization = Organization.find(params[:organization_id])
#vip = #organization.vips.build(vip_params)
if #vip.save
redirect_to organization_path(#organization)
else
render 'new'
end
end
def vip_params
params.require(:vip).permit(:name, :about, :organization_id)
end
Before I started using the has_many :through associations, the build method would automatically add the foreign key to the new vip. So my vips table would have the organization_id column populated. Since using the has_many associations, the organization_id column is being left NULL on 'vip#create'.
Is there a reason that build wouldn't work the same way anymore with my new associations?
I have a model with associations. How to create/update the associations as CRUD operations are performed on the model.
That is, when I run
#v1_seller = V1::Seller.new(seller_params)
#v1_seller.save
It should save the associations.
Should I create after_create hooks and pass the params (but then I will have to do the same in update)? Or am I missing something? I feel that it should be done automatically in rails.
currently I am doing it explicitly:
#v1_seller = V1::Seller.new(seller_params)
if #v1_seller.save
#v1_seller.assign_categories(params)
my seller model:
class V1::Seller < ActiveRecord::Base
has_many :categories, :class_name => 'V1::Category', dependent: :delete_all
has_many :category_names, :class_name => 'V1::CategoryName', through: :categories
# right now I am manually calling this after a create/update operation in my controller
def assign_categories(params)
params.require(:seller).require(:categories)
params.require(:seller).permit(:categories => []).permit(:name, :brands => [])
self.categories.clear
params[:seller][:categories].each do |c|
if c[:brands].nil? || c[:brands].empty?
next # skip the category if it has no brands associated with it
end
category_name = c[:name]
category = V1::Category.new
category.category_name = V1::CategoryName.find_by(name: category_name)
category.seller = self
category.save
c[:brands].each do |b|
begin
category.brand_names << V1::BrandName.find_by(name: b)
rescue ActiveRecord::RecordInvalid
# skip it. May happen if brand is already added to the particular category
end
end
end
end
end
And V1::Cateogry model:
class V1::Category < ActiveRecord::Base
belongs_to :category_name, :class_name => 'V1::CategoryName', inverse_of: :category
belongs_to :seller, :class_name => 'V1::Seller', inverse_of: :category
has_many :brands, :class_name => 'V1::Brand', dependent: :delete_all, inverse_of: :category
has_many :brand_names, :class_name => 'V1::BrandName', through: :brands, inverse_of: :category
validates :seller, :uniqueness => {:scope => [:category_name, :seller]}
end
Seem like you need nested attributes.
Checkout the docs here: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I've got users and organisations with a join model UsersOrganisation. Users may be admins of Organisations - if so the is_admin boolean is true.
If I set the is_admin boolean by hand in the database, Organisations.admins works as I'd expect.
In the console, I can do Organisation.first.users << User.first and it creates an organisations_users entry as I'd expect.
However if I do Organisation.first.admins << User.last it creates a normal user, not an admin, ie the is_admin boolean on the join table is not set correctly.
Is there a good way of doing this other than creating entries in the join table directly?
class User < ActiveRecord::Base
has_many :organisations_users
has_many :organisations, :through => :organisations_users
end
class Organisation < ActiveRecord::Base
has_many :organisations_users
has_many :users, :through => :organisations_users
has_many :admins, :through => :organisations_users, :class_name => "User",
:source => :user,
:conditions => {:organisations_users => {:is_admin => true}}
end
class OrganisationsUser < ActiveRecord::Base
belongs_to :organisation
belongs_to :user
end
You can always override the << method of the association:
has_many :admins do
def <<(user)
user.is_admin = true
self << user
end
end
(Code has not been checked)
there are some twists with the has_many :through and the << operator. But you could overload it like in #Erez answer.
My approach to this is using scopes (I renamed OrganisationsUsers to Memberships):
class User < ActiveRecord::Base
has_many :memberships
has_many :organisations, :through => :memberships
end
class Organisation < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :class_name => 'User', :source => :user
# response to comment:
def admins
memberships.admin
end
end
class Memberships < ActiveRecord::Base
belongs_to :organisation
belongs_to :user
scope :admin, where(:is_admin => true)
end
Now I create new admins like this:
Organisation.first.memberships.admin.create(:user => User.first)
What I like about the scopes is that you define the "kind of memberships" in the membership class, and the organisation itself doesn't have to care about the kinds of memberships at all.
Update:
Now you can do
Organisation.first.admins.create(:user => User.first)
You can try below code for organization model.
class Organisation < ActiveRecord::Base
has_many :organisations_users
has_many :organisations_admins, :class_name => "OrganisationsUser", :conditions => { :is_admin => true }
has_many :users, :through => :organisations_users
has_many :admins, :through => :organisations_admins, :source => :user
end
i have three models, all for a has_many :through relationship. They look like this:
class Company < ActiveRecord::Base
has_many :company_users, dependent: :destroy
has_many :users, through: :company_users
accepts_nested_attributes_for :company_users, :users
end
class CompanyUser < ActiveRecord::Base
self.table_name = :companies_users #this is because this was originally a habtm relationship
belongs_to :company
belongs_to :user
end
class User < ActiveRecord::Base
# this is a devise model, if that matters
has_many :company_users, dependent: :destroy
has_many :companies, through: :company_users
accepts_nested_attributes_for :company_users, :companies
end
this loads fine, and the joins are built fine for queries. However, whenever i do something like
#company = Company.last
#user = #company.users.build(params[:user])
#user.save #=> true
#company.save #=> true
both the User record and the CompanyUser records get created, but the company_id field in the CompanyUser record is set to NULL
INSERT INTO `companies_users` (`company_id`, `created_at`,`updated_at`, `user_id`)
VALUES (NULL, '2012-02-19 02:09:04', '2012-02-19 02:09:04', 18)
it does the same thing when you #company.users << #user
I'm sure that I'm doing something stupid here, I just don't know what.
You can't use a has_many :through like that, you have to do it like this:
#company = Company.last
#user = User.create( params[:user] )
#company.company_users.create( :user_id => #user.id )
Then you will have the association defined correctly.
update
In the case of the comment below, as you already have accepts_nested_attributes_for, your parameters would have to come like this:
{ :company =>
{ :company_users_attributes =>
[
{ :company_id => 1, :user_id => 1 } ,
{ :company_id => 1, :user_id => 2 },
{ :company_id => 1, :user_id => 3 }
]
}
}
And you would have users being added to companies automatically for you.
If you have a has_many :through association and you want to save an association using build you can accomplish this using the :inverse_of option on the belongs_to association in the Join Model
Here's a modified example from the rails docs where tags has a has_many :through association with posts and the developer is attempting to save tags through the join model (PostTag) using the build method:
#post = Post.first
#tag = #post.tags.build name: "ruby"
#tag.save
The common expectation is that the last line should save the "through" record in the join table (post_tags). However, this will not work by default. This will only work if the :inverse_of is set:
class PostTag < ActiveRecord::Base
belongs_to :post
belongs_to :tag, inverse_of: :post_tags # add inverse_of option
end
class Post < ActiveRecord::Base
has_many :post_tags
has_many :tags, through: :post_tags
end
class Tag < ActiveRecord::Base
has_many :post_tags
has_many :posts, through: :post_tags
end
So for the question above, setting the :inverse_of option on the belongs_to :user association in the Join Model (CompanyUser) like this:
class CompanyUser < ActiveRecord::Base
belongs_to :company
belongs_to :user, inverse_of: :company_users
end
will result in the following code correctly creating a record in the join table (company_users)
company = Company.first
company.users.build(name: "James")
company.save
Source: here & here
I suspect your params[:user] parameter, otherwise your code seems clean. We can use build method with 1..n and n..n associations too, see here.
I suggest you to first make sure that your model associations works fine, for that open the console and try the following,
> company = Company.last
=> #<Tcompany id: 1....>
> company.users
=> []
> company.users.build(:name => "Jake")
=> > #<User id: nil, name: "Jake">
> company.save
=> true
Now if the records are being saved fine, debug the parameters you pass to build method.
Happy debugging :)
In my form for member_profile, I would like to have role checkboxes that are visible for admins. I would like to used some nested form_for, but can't make it work, so I've resorted to manually creating the check_box_tags (see below), and then manually adding them to member_profile.member.
Note that the Member model is Devise, and I don't want to mix those fields in with my MemberProfile data, in case I change auth systems in the future.
class Member < ActiveRecord::Base
has_one :member_profile
has_many :member_roles
has_many :roles, :through => :member_roles
end
class MemberProfile < ActiveRecord::Base
belongs_to :member
has_many :member_roles, :through => :member
#has_many :roles, :through => :member_roles #can't make this work work
end
class Role < ActiveRecord::Base
has_many :member_roles
validates_presence_of :name
end
class MemberRole < ActiveRecord::Base
belongs_to :member
belongs_to :role
end
Form (haml)
= form_section do
- Role.all.each do |x|
=check_box_tag 'member[role_ids][]',
x.id,
begin #resource.member.role_ids.include?(x.id) rescue nil end
=x.name
member_profiles_controller.rb
def update
if #resource.update_attributes params[:member_profile]
#resource.member.role_ids = params[:member][:role_ids]
redirect_to(#resource, :notice => 'Member profile was successfully updated.')
else
render :action => "edit"
end
end
I've decided it only makes sense to do a nested has_many :through on Update, since the join model is what is being 'gone through' to get to the has_many :through model. Before the hmt is created, there is obviously no record in the join model.