Rails connect the table task
Part of my RoR application is responsible for managing the portfolio of website projects. One website can have many images associated with it. One image can only be associated with one structure. I am using has_many with: through parameter to connect images with designs through a join table. And when the image is deleted, the associated entry in the connection table must be deleted. So I have the following models For images:
class Image < ActiveRecord::Base
has_one :images_site_designs , :class_name => "ImagesSiteDesigns" , :dependent => :destroy
has_one :site_design , :through => :images_site_designs
end
For site_designs:
class SiteDesign < ActiveRecord::Base
belongs_to :client
has_many :images_site_designs , :class_name => "ImagesSiteDesigns"
has_many :images , :through => :images_site_designs
end
And join the images_site_designs table:
class ImagesSiteDesigns < ActiveRecord::Base
belongs_to :image
belongs_to :site_design
end
Generating new images for site_designs is fine, so the following code works fine:
@site_design = SiteDesign.find(params[:id])
@site_design.images << Image.new(params[:image])
But when I try to delete the image the following error appears:
ActiveRecord::StatementInvalid in ImagesController#destroy
Mysql::Error: Unknown column 'id' in 'where clause': DELETE FROM `images_site_designs` WHERE `id` = NULL
It seems that rails are using the wrong column name to query the join_designs table images_site_designs. How can I fix this?
UPD:
image_controller which removes the image:
def destroy
@image = Image.find(params[:id])
@image.destroy
respond_to do |format|
format.html { redirect_to(images_url) }
format.xml { head :ok }
end
end
Migration:
class CreateImages < ActiveRecord::Migration
def self.up
create_table :images do |t|
t.string :url
t.string :name
t.text :description
t.timestamps
end
end
def self.down
drop_table :images
end
end
class CreateSiteDesigns < ActiveRecord::Migration
def self.up
create_table :site_designs do |t|
t.string :name
t.text :concept
t.text :description
t.integer :client_id
t.timestamps
end
end
def self.down
drop_table :site_designs
end
end
class CreateImagesSiteDesigns < ActiveRecord::Migration
def self.up
create_table :images_site_designs , :id => false do |t|
t.integer :image_id
t.integer :site_design_id
end
end
def self.down
drop_table :images_site_designs
end
end
source to share
It is impossible to answer your question because it is not shown which code is causing the error. Presumably a call to destroy on an image instance, but can't be sure.
However, you don't seem to need this join model. The requirement seems to be met like this:
class SiteDesign < ActiveRecord::Base
belongs_to :client
has_many :images
end
class Image < ActiveRecord::Base
belongs_to :site_design
end
Of course this would require a transfer (drag and drop the join table and add site_design_id to the image table), but this seems to be a cleaner solution. Any reason not to do this?
source to share
If you want one image to be associated with only one design, why then are you using the has_and_belongs_to relationship (you are using an n: m table!)
I would refactor the following:
1) Transfer the model of your images and add the Site_design_id attribute
class CreateImages < ActiveRecord::Migration
def self.up
create_table :images do |t|
t.string :url
t.string :name
t.text :description
t.integer :site_design_id
t.timestamps
end
end
def self.down
drop_table :images
end
end
2) Drop ImagesSiteDesigns-Migration
drop_table :images_site_designs
3) Change the models to:
class SiteDesign < ActiveRecord::Base
belongs_to :client
has_many :images
end
class Image < ActiveRecord::Base
belongs_to :site_design
end
So you get a 1: n relationship and this should be the best solution for your specification.
You can use your models like this:
Image.first.site_design
=> <SiteDesign #id:...>
SiteDesign.first.image
=> <Image #id...>
SiteDesign.image = Image.new(params[:image])
...
source to share