Rails has_many through association not updating join model - ruby-on-rails-4

I have the following models:
class Project < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
end
class User < ActiveRecord::Base
has_many :memberships
has_many :projects, through: :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
and the migration for memberships
def change
create_table :memberships do |t|
t.belongs_to :user
t.belongs_to :project
t.boolean :admin
end
end
In my controller, I want to create a project for the current user
def create
#project = current_user.projects.build(project_params)
if #project.save
render json: #project, status: :ok
else
render json: { errors: #project.errors }, status: :unprocessable_entity
end
end
def project_params
params.require(:project).permit(:name)
end
When #project.save is called, the SQL in the log:
INSERT INTO "projects" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", "2014-12-09 17:18:55.298566"], ["name", "Test"], ["updated_at", "2014-12-09 17:18:55.298566"]]
So memberships is never updated, so the project gets made but it is not associated with the current user. I think rails is supposed to do that automatically when using the relationship to build the object, but that doesn't seem to be the case here.
It will work if I do
#project = current_user.projects.build(project_params)
#project.users << current_user
But that feels incorrect. Where am I going wrong?

Related

UnknownAttributeError for nested form

I have two model as:
Customer:
has_many :products, :dependent => :destroy
accepts_nested_attributes_for :products, reject_if: proc { |attributes| attributes['product_id'].blank? }
Product:
belongs_to :customer
products controller:
def product_params
params.require(:product).permit(:name, :first_build)
end
customers controller:
def customer_params
params.require(:customer).permit(:first_build, :name, :product_id,
products_attributes: [:first_build, :customer_id])
end
So in the customers controller I do this
#customer.products.build(:first_build => true)
but I get this error
unknown attribute 'first_build' for Prodcut
but when I do this #customer.products.build(:name => "test product name")
it works perfectly without any error. One thing to note here, first_build is not a column in the products table.
If you want to pass attributes that are not in the table, you can create a "temporary" attribute which will not be stored in the table but will be available while the object is in memory.
class Product < ActiveRecord::Base
attr_accessor :first_build
...
end

Re-named associations working correctly

I have two types of users, one that can create movies and one that can create reviews:
class User < ActiveRecord::Base
has_many :created_movies, foreign_key: 'creator_id', class_name: 'Movie'
has_many :reviewed_movies, foreign_key: 'reviewer_id', :through => 'Review'
end
class Review < ActiveRecord::Base
belongs_to :movie
belongs_to :reviewer, class_name: 'User'
end
class Movie < ActiveRecord::Base
has_many :reviews
belongs_to :creator, class_name: 'User'
end
Whenever I try to run the following in my users/show:
<% if #user.reviewed_movies.any? %>
I get this problem:
ActiveRecord::HasManyThroughAssociationNotFoundError at /users/1
Could not find the association "Review" in model User
I can see that I can successfully populate the reviewer_id with the correct user when the review is created when I go into the command line:
m = Movie.last
m.reviews[1]
# => <Review:0x007fdc4036a938> {
# :id => 2,
# :rating => 2,
# :title => "bye",
# :content => "byeeeee",
# :created_at => Tue, 15 Sep 2015 19:20:25 UTC +00:00,
# :updated_at => Tue, 15 Sep 2015 19:20:25 UTC +00:00,
# :movie_id => 3,
# :reviewer_id => 1
# }
But I can't retrieve it from the other end. If anyone can provide some assistance it would be greatly appreciated, thanks!
ActiveRecord::HasManyThroughAssociationNotFoundError at /users/1 Could
not find the association "Review" in model User
The problem is here in this line
has_many :reviewed_movies, foreign_key: 'reviewer_id', :through => 'Review'
Which should be
has_many :reviewed_movies, foreign_key: 'reviewer_id', class_name: 'Review'
You need to tell your User model it has many reviews through which it has many movies called "reviewed_movies".
class User < ActiveRecord::Base
has_many :created_movies,
foreign_key: :movie_id,
class_name: 'Movie'
has_many :reviews
has_many :reviewed_movies,
foreign_key: :movie_id,
through: :reviews,
class_name: 'Movie'
end
EDIT: The foreign key is there to tell your model which foreign key to use, if your relation isn't called the same as the foreign-key, so I think it should be 'movie_id'... Ok, not sure about this any longer, I guess it depends on your setup ^^

Rails - use collection_select in a simple_form for a has_many_through association

I need help with a create form for a has_many_through association which also specifies a class name:
class Sponsor < ActiveRecord::Base
has_many :member_contacts
has_many :contacts, through: member_contacts, class_name: :member
accepts_nested_attributes_for :address, :contacts
end
class Contact < ActiveRecord::Base
has_many :member_contacts
has_many :sponsors, through: member_contacts
end
class MemberContact < ActiveRecord::Base
belongs_to :contact
belongs_to :sponsor
end
sponsors_controller.rb
def create
#sponsor = Sponsor.create(sponsor_params)
#sponsor.contacts.build
end
def sponsor_params
params.require(:sponsor).permit(:name,:website,:email,
:contact_first_name, :contact_surname, contacts_attributes: [], address_attributes: [:flat, :street, :postal_code, :city])
end
sponsor/_form.html.haml
= simple_form_for #sponsor do |f|
= f.association :contacts, collection: Member.all, label_method: :full_name
This is failing with the error 'unpermitted params, contact_ids', because
"contact_ids"=>["", "4", "5", "6"]
is being passed in the params hash. In the form I'd like a drop down list of all the members and the ability to select multiple members which will be saved against the sponsor as contacts.
How do I set up the contacts_attributes in sponsor_params in the controller and the collection_select simple_form helper in the view?
To get the form working, I added a foreign key to the sponsor class
has_many :contacts, through: member_contacts, class_name: 'Member', foreign_key 'member_id'
changed the strong params in the controller
def sponsor_params
params.require(:sponsor).permit(:name,:website,:email, contact_first_name, :contact_surname, contact_ids: [], address_attributes: [:flat, :street, :postal_code, :city])
end
and removed the association in the view, using collection_select
= f.collection_select :contact_ids, Member.all, :id, :full_name,
{ selected: #sponsor.contacts.map(&:id) }, { multiple: true }
You can set like this:
params.require(:sponsor).permit(:name,:website,:email, contact_ids: []...)
Note that permit(:contact_ids) will fail, but permit(contact_ids: []) works.

Rails 4: how to use form_for (has_many :through with extra attributes)

I have a Store model, Product model, and a StoreProduct model to form a many-to-many relationship between stores and products.
The StoreProduct model, on top of store_id and product_id, has an attribute price:float.
I'm struggling to figure out how I can design a form to create a new product given the store. Is it better off making a form for a new StoreProduct object? What is the conventional way of doing this?
store.rb
class Store < ActiveRecord::Base
validates :name, presence: true
has_many :store_products
has_many :products, through: :store_products
end
product.rb
class Product < ActiveRecord::Base
validates :name, presence: true
validates_numericality_of :price, on: :create
has_many :reviews
has_many :users, through: :reviews
has_many :store_products
has_many :stores, through: :store_products
end
store_product.rb
class StoreProduct < ActiveRecord::Base
belongs_to :store
belongs_to :product
end

Rails model namespacing dependent destroy causes mysql error unknown field

I have several models in a separate folder called jira (instance.rb, generic_field.rb, etc.). They are are all namespaced under JIRA, for example JIRA::Instance < ActiveRecord::Base, JIRA::GenericField < ActiveRecord::Base. Here are the two models:
class JIRA::GenericField < ActiveRecord::Base
self.table_name = "jira_generic_fields"
belongs_to :jira_instance, class_name: JIRA::Instance
end
class JIRA::Instance < ActiveRecord::Base
self.table_name = "jira_instances"
has_many :jira_generic_fields, dependent: :destroy, class_name: JIRA::GenericField
end
The DB schema for the tables:
create_table "jira_generic_fields", force: true do |t|
t.string "jira_id"
t.string "name"
t.integer "jira_instance_id", null: false
end
create_table "jira_instances", force: true do |t|
t.string "jira_link"
t.string "crowd_link"
end
In my rails console i create a JIRA::Instance object and when I try to destroy it I get this:
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'jira_generic_fields.instance_id' in 'where clause': SELECT `jira_generic_fields`.* FROM `jira_generic_fields` WHERE `jira_generic_fields`.`instance_id` = 1
Why does ActiveRecord use jira_generic_fields.instance_id instead of jira_generic_fields.jira_instance_id and how can I fix this while keeping the models under the same JIRA namespace?
In the end specifying the foreign_key in the models solved this ...
class JIRA::GenericField < ActiveRecord::Base
self.table_name = "jira_generic_fields"
belongs_to :jira_instance, foreign_key: 'jira_instance_id', class_name: JIRA::Instance
end
class JIRA::Instance < ActiveRecord::Base
self.table_name = "jira_instances"
has_many :jira_generic_fields, dependent: :destroy, foreign_key: 'jira_instance_id', class_name: JIRA::GenericField
end
I don't really like it but it will have to do for now.