Why is this join table always doing posts_tags? - sql

When I use the following:
class CreateJoinTableTagsPosts < ActiveRecord::Migration
def change
create_join_table :tags, :posts do |t|
t.index [:tag_id, :post_id]
end
end
end
or the following:
class CreateJoinTableTagsPosts < ActiveRecord::Migration
def change
create_join_table :posts, :tags do |t|
t.index [:post_id, :tag_id]
end
end
end
I always get a table that is posts_tags and as a result helper methods in the posts model:
class Post < ActiveRecord::Base
belongs_to :blogs
has_and_belongs_to_many :tags, join_table: 'tags_posts'
has_and_belongs_to_many :categories, join_table: 'categories_posts'
has_many :comments
validates :title, presence: true
def has_tag?(tag_name)
tag == tag_name
end
def assign_tag=(tag_name)
tag = Tag.find_by(name: tag_name) || Tag.create(name: tag_name)
self.tag = [tag] if tag
end
end
Don't actually work. As you can see the assign_tag method wont actually work, tests that are done as such:
it "should create a page for a post" do
#post.assign_tag = 'Sample Tag'
end
fails because relation tags doesn't exist. I believe this can be solved by creating the appropriate join table of tags_posts instead of the one it always creates posts_tags
ideas?

create_join_table uses the lexical order of the arguments to name the table.
You can override this using a table_name option:
create_join_table :posts, :tags, table_name: 'tags_posts' do |t|
t.index [:post_id, :tag_id]
end

Related

Change primary and foreign key in ActiveRecord

I have two models :
class Settlement < ActiveRecord::Base
set_primary_key :settlement_identifier
has_many :streets
attr_accessible :city, :name, :service_available, :zip, :country_id,: settlement_identifier
end
class Street < ActiveRecord::Base
belongs_to :settlement, foreign_key: "settlement_identifier"
attr_accessible :name, :settlement_identifier, :street_identifier
end
Because I am doing import for streets and settlements, I need to point streets via settlement_identifier, not settlement_id .
When I do
Street.first.settlement #it compare settlement_identifiers from both tables
But when try to get streets from single settlement like :
Settlement.first.streets
It throws an error
SELECT "streets".* FROM "streets" WHERE "streets"."settlement_id" = 4263
ActiveRecord::StatementInvalid: PG::Error: ERROR: column streets.settlement_id does not exist .
I want that query to be :
SELECT "streets".* FROM "streets" WHERE "streets"."settlement_identifier" = 4263
Any help ?
I solved this problem. Here is solution below :
class CreateSettlements < ActiveRecord::Migration
def change
create_table :settlements, primary_key: :settlement_identifier, id: :false do |t|
t.string :name
t.string :zip
t.string :city
t.string :service_available
t.integer :country_id
t.timestamps
end
end
def down
drop_table :settlements
end
end
Here I set primary_key in my migration to settlement_identifier, and set id to false
Also, my Street migration is:
class CreateStreets < ActiveRecord::Migration
def change
create_table :streets do |t|
t.string :name
t.integer :settlement_identifier
t.string :street_identifier
t.timestamps
end
end
end
So, Street has reference to Settlement via settlement_identifier .
Settlement model :
class Settlement < ActiveRecord::Base
has_many :streets, foreign_key: "settlement_identifier"
attr_accessible :city, :name, :service_available,:settlement_identifier
end
Street model :
class Street < ActiveRecord::Base
belongs_to :settlement, foreign_key: "settlement_identifier"
attr_accessible :name, :settlement_identifier, :street_identifier
end
I tried to set primary_key on Settlement model but that didn't work.
This works fine for me. If anyone have another solution, please put comment or code example.

has_many relationship with images in the def create

I am trying to create many images for a single product.
Since the number of images per product is as many as the user wants to enter I have created 2 separate models, product and product_image.
Product has many product_images
and product_images belongs_to product
I'm almost certain that this section of code is the problem (this is the product_image controller)
def create
#product_image = ProductImage.new(params[:product_image])
#product = #product_image.product
if #product_image.save
#product_image.product_id = #product.id
#product_image.save
redirect_to #product_image, notice: 'Product image was successfully created.'
else
render :template => "products/edit"
end
end
At the moment the code allows me to upload an image via paperclip but totally disregards the product_id and just puts the product_image_id in that field instead.
I checked the db through the cmd line to see this.
So how do I get an image to be created with an ID of a particular product? I've searched this site but the questions that exist do seem to offer the solution that I require.
Thanks for any help you can offer.
Here are the migrations that I used for the models relating to products and product_images
I apologize for the mess, I was very indecisive in my initial development which caused lost of little changes to be made as I gained more knowledge about the whole rails system
products
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.text :description
t.string :image_url
t.decimal :price, precision: 8, scale: 2
t.timestamps
end
end
end
and, products
class AddColumnsToProducts < ActiveRecord::Migration
def change
drop_table :products
create_table :products do |t|
t.string :product_title
t.text :product_desc
t.string :product_image_url
t.decimal :price, precision: 8, scale: 2
t.timestamps
end
end
end
and, products
class AddColumnToProducts < ActiveRecord::Migration
def change
add_column :products, :department, :string
end
end
and, products
class AddMoreColumnsToProducts < ActiveRecord::Migration
def change
add_column :products, :display_on_home_page, :boolean, default: false
add_column :products, :is_highight_product, :boolean, default: false
end
end
and, products
class RenameIsHighightProductInProducts < ActiveRecord::Migration
def up
rename_column :products, :is_highight_product, :is_highlight_product
end
def down
end
end
and, products
class RenameProductImageUrlInProducts < ActiveRecord::Migration
def up
rename_column :products, :product_image_url, :image_url
end
def down
end
end
and product images table created
class CreateProductImages < ActiveRecord::Migration
def change
create_table :product_images do |t|
t.integer :product_id
t.string :title
t.text :description
t.string :image_file_name
t.string :image_content_type
t.integer :image_file_size
t.datetime :image_updated_at
t.timestamps
end
end
end
and, products
class AlterTableProducts < ActiveRecord::Migration
def up
end
remove_column :products, :image_url
add_column :products, :product_image_id, :integer
def down
end
end
and, product_images
class AddColumnToProductImages < ActiveRecord::Migration
def change
add_column :product_images, :image_path, :string
end
end
and, product_images
class RenameColumnImagePathInProductImages < ActiveRecord::Migration
def up
rename_column :product_images, :image_path, :image_url
end
def down
end
end
and, product_images
class AddProductTitleColumnToProductImages < ActiveRecord::Migration
def change
add_column :product_images, :product_title, :string
end
end
and finally, products
class DropPriceFromProductsAndAddPriceToProducts < ActiveRecord::Migration
def up
end
remove_column :products, :price
add_column :products, :price, :decimal, :precision => 8, :scale => 2
def down
end
end
I am not sure what is going wrong because there is a little too less info in your question. But let me show quickly, how this should be set up (simplified).
new rails app:
rails new stack_product
creating the models
rails g model product
rails g model image
You get all this (you have to add the attr_accessible attributes by hand here)
app/models/product.rb
class Product < ActiveRecord::Base
attr_accessible :title, :description
has_many :images
end
app/models/image.rb
class Image < ActiveRecord::Base
attr_accessible :name, :path, :product_id
belongs_to :product, foreign_key: "product_id"
end
db/migrations/20131011195035_create_products.rb
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.primary_key :id
t.string :title
t.string :description
t.string :image_url
t.timestamps
end
end
end
20131011195421_create_images.rb
class CreateImages < ActiveRecord::Migration
def change
create_table :images do |t|
t.primary_key :id
t.integer :product_id
t.string :name
t.string :path
t.timestamps
end
end
end
use rails console in the terminal.
rails console
the fire:
Product.create({title: 'Ford Mustang', description: 'The one and only Shelby'})
...
Image.create({product_id: 1, name: 'Image Mustang', path: '/images/mustang.png'})
Image.create({product_id: 1, name: 'Image Mustang from behind', path: '/images/mustang2.png'})
then you can query the objects
p = Product.find(1)
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT 1 [["id", 1]]
=> #<Product id: 1, title: "Ford Mustang", description: "The one and only Shelby", image_url: nil, created_at: "2013-10-11 20:14:06", updated_at: "2013-10-11 20:14:06">
Image.where("product_id=?", p.id)
Image Load (0.3ms) SELECT "images".* FROM "images" WHERE (product_id=1)
=> [#<Image id: 1, product_id: 1, name: "Image Mustang", path: "/images/mustang.png", created_at: "2013-10-11 20:14:09", updated_at: "2013-10-11 20:14:09">, #<Image id: 2, product_id: 1, name: "Image Mustang from behind", path: "/images/mustang2.png", created_at: "2013-10-11 20:14:26", updated_at: "2013-10-11 20:14:26">]
So this works fine. If you would create forms for this, you would have one for the products and another one for the images. The images form would have a dropdown with all the products (productname and value with id). The dropdown for the products would be named product_id and then the product's id would be saved in the image table as product_id.
You should maybe scaffold all this to see how it is be done by Rails.

Rspec does not load the objects through has_and_belongs_to_many association

I have this in my model:
class Instance < ActiveRecord::Base
has_and_belongs_to_many :owners, :class_name => 'User'
and this:
class User < ActiveRecord::Base
has_many :instances
and I have this migration:
class CreateInstancesUsersJoinTable < ActiveRecord::Migration
def up
create_table :instances_users, :id=>false do |t|
t.string :instance_id
t.string :user_id
end
end
def down
drop_table :instances_users
end
end
And in instance controller I have:
#instance.owners << owner
but the tests says that owner is not into owners array. But when I say:
p #instace.owners - before or after #instance.owners << owner
the test passes. Does anyone know why this happens?
In User model you should write
has_and_belongs_to_many :instances
instead of
has_many :instances

self.votable not assigning values to the votable instance in a model callback?

I have a Post model that has a polymorphic association with a Vote model:
post.rb:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :total_votes
belongs_to :user
has_many :votes, :as => :votable, :dependent => :destroy
end
vote.rb
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
before_create :update_total
protected
def update_total
self.votable.total_votes ||= 0
self.votable.total_votes += self.polarity
end
end
As you can see in vote.rb I want to accomplish the following:
Each time an instance of Vote is created, I want to update the total_votes column of the votable model instance (in this case an instance of Post).
But nothing happens, when I create a vote for a post with post.votes.create(:polarity => 1), the total_votes column of the post still being nil.
Any suggestions to fix this?
EDIT:
This didn't work either:
class Vote < ActiveRecord::Base
belongs_to :votable, :polymorphic => true
belongs_to :user
before_create :update_total
protected
def update_total
self.votable.total_votes ||= 0
self.votable.total_votes += self.polarity
self.votable.save!
end
end
schema.rb:
create_table "posts", :force => true do |t|
t.string "content"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "title"
t.integer "comments_count", :default => 0, :null => false
t.integer "total_votes"
end
When you create the Vote object, the Post object already exists. You update it, but you don't save it. Try with the following code:
def update_total
self.votable.total_votes ||= 0
self.votable.total_votes += self.polarity
self.votable.save!
end

Using Rails Gem Active Admin with Associations

I'm trying out the new Rails gem http://activeadmin.info/ and it's working great! However I can't find any documentation on how to use it across associations. For example:
class Membership < ActiveRecord::Base
belongs_to :course
belongs_to :person
class Course < ActiveRecord::Base
has_many :memberships
has_many :people, :through => :memberships
class Person < ActiveRecord::Base
has_many :memberships
has_many :courses, :through => :memberships
The membership join table includes some extra data as well (ie: attendance). I'm trying to show the membership with both the course and student name - and allow filtering / sorting on those names. As far as I have found, Active Admin doesn't work across associations. Has anyone else been successful in doing that, or found another gem that does? Thanks so much!
ingredient.rb
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :products, :join_table => :ingredients_products
end
product.rb
class Product < ActiveRecord::Base
has_and_belongs_to_many :ingredients, :join_table => :ingredients_products
end
don't forget the migrations for the joining table (:id to false!)
class CreateProductsIngredients < ActiveRecord::Migration
def self.up
create_table :ingredients_products,:id => false do |t|
t.integer :product_id
t.integer :ingredient_id
t.timestamps
end
end
def self.down
drop_table :products_ingredients
end
end
Now define the form in you ActiveAdmin resource, override the default
ActiveAdmin.register Product do
form do |f|
f.inputs "Details" do
f.input :product_name
f.input :brand
f.input :ingredients # don't forget this one!
end
end
I've been playing with ActiveAdmin for a while now, here's how I managed to get associations to work in Indexes and Forms.
I've just guessed some of your model columns below. Also note, in the form. The 'person' section will show all the columns for editing, whereas the 'course' section will just show the specified column.
ActiveAdmin.register User do
index do
column :id
column :name
column :attendance
column :person do |membership|
membership.person.name
end
column :course do |membership|
membership.course.name
end
default_actions
end
form do |f|
f.inputs "Membership" do
f.input :name
f.input :created_at
f.input :updated_at
end
f.inputs :name => "Person", :for => :person do |person|
person.inputs
end
f.inputs :name => "Course", :for => :course do |course|
course.input :name
end
f.buttons
end
end
I haven't tested this, but you should be able to apply these ideas to your case. It's working for mine.
Update: I've just read your question again and noted that you're wanting to be able to sort on the association column. I've just checked my implementation and this indeed is not working. My answer may be useless to you but I'll leave it here anyway (might help someone else).
I've just started using this gem myself, and while I haven't gotten around to showing association information, here's how you create a form for associations:
form do |f|
f.inputs
f.has_many :associations do |association|
association.inputs
end
f.buttons
end
That will give you a basic form with scaffolding.
ingredient.rb
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :products, :join_table => :ingredients_products
end
product.rb
class Product < ActiveRecord::Base
attr_accessible ingredient_ids
has_and_belongs_to_many :ingredients, :join_table => :ingredients_products
end
migration_xxx.rb
class CreateProductsIngredients < ActiveRecord::Migration
def self.up
create_table :ingredients_products,:id => false do |t|
t.integer :product_id
t.integer :ingredient_id
t.timestamps
end
end
def self.down
drop_table :products_ingredients
end
end
products.rb
ActiveAdmin.register Product do
form do |f|
f.inputs "Details" do
f.input :product_name
f.input :brand
f.input :ingredients
end
end
...
end