I'm practicing Rails. I want to have messages and tags for them (There should be many tags for single message and many messages for single tags). I have 2 relevant models: Messages and Tags. They are associated using has_and_belongs_to_many. I'm using ffaker to populate tables
Models:
Message:
class Message < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_and_belongs_to_many :tags
end
Tag:
class Tag < ActiveRecord::Base
has_and_belongs_to_many :messages
end
Migrations:
Messages:
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.text :content
t.timestamps
end
end
end
Tags:
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :title
end
end
end
Join table:
class CreateMessagesTagsJoinTable < ActiveRecord::Migration
def change
create_table :messages_tags, id: false do |t|
t.references :tag, index: true
t.references :message, index: true
end
end
end
Seeds file:
5.times { Message.create([content: FFaker::CheesyLingo.paragraph]) }
Message.all.each do |msg|
rand(4).times { Comment.create([content: FFaker::CheesyLingo.sentence, message_id: msg.id]) }
2.times { msg.tags.create([title: FFaker::HipsterIpsum.word.gsub(' ','-').downcase]) }
end
Comments are irrelevant. So firstly I'm populating messages table here. Secondly I'm populating tags table from inside of messages. What I end up is populated messages and tags table where every message has 2 tags.
Now, here is the question:
How do I associate already created tags to messages and vice-versa? I know how to do so by creating them, but now I want to associate few tags to single message. I also would like to associate few messages to single, already created tag.
How to do that, what is the syntax?
This should work
Message.all.each do |msg|
rand(4).times { Comment.create([content: FFaker::CheesyLingo.sentence, message_id: msg.id]) }
tags = 2.times { msg.tags.create([title: FFaker::HipsterIpsum.word.gsub(' ','-').downcase]) }
msg.tags = tags
end
or you can do it manually
msg = Message.first
tag = Tag.first
tag.messages << msg
# or
msg.tags << tag
Related
I have a model called categories currently they belong to product but I'd like them to belong to store instead. I have several thousand of these so what I'd like to do is create a migration that adds a store_id to categories and then, gets the associated product.store.id from it's current association and adds that to the store_id. After that I'd like to remove the product association.
Does anybody know how to easily and safely achieve that?
Should you add the association in the wrong direction, you can use change_table to reverse the association:
class CreateFavorites < ActiveRecord::Migration[5.0]
def change
create_table :favorites do |t|
t.belongs_to :article
t.timestamps
end
end
end
class RemoveArticleFromFavorites < ActiveRecord::Migration[5.0]
def change
change_table :favorites do |t|
t.remove_references :article
end
end
end
class AddFavoriteToArticles < ActiveRecord::Migration[5.0]
def change
change_table :article do |t|
t.belongs_to :favorite
end
end
end
First rename column to store_id,
rename_column :categories, :product_id, :store_id
Then change the assosciation.
Now you can either write a rake task to transfer the data or you can manually do it via console.
It's better way to write a rake task.
According to your requirement your rake task can be, get the store from the product and assign to the category according to your requirement.
require 'rake'
namespace :category do
task :product_to_store => :environment do
Category.all.each do |category|
product = Product.find(category.store_id) //you will get product,as now it chnaged to store_id
if product.present?
category.store_id = product.try(:store).try(:id) //assign the product's store_id to the category, use try to reject errored records
category.save
end
end
end
end
Now run, **`rake category:product_to_store`**, thats it, the data gets transfered.
You can just add new migration that will create the new reference with categories as store.
class YourMigrationName < ActiveRecord::Migration
def up
add_reference :categories, :store, index: true
Category.all.each do |category|
category.store_id = category.product_id
category.save
end
remove_column :product_id
end
def down
add_reference :categories, :product, index: true
Category.all.each do |category|
category.product_id = category.store_id
category.save
end
remove_reference :categories, :store, index: true
end
end
May be if you have added the product reference and index then write the same as the store so then it will delete the index as well.
you have data in the column you don't want to lose, then use rename_column
I have 3 tables: proposals, items/proposals (items is nested inside proposals) and invoices.
I want to create invoices for those items in the proposals that got approved. How would the associations for these look like? Also, how would I set up the invoices form to choose only those items that got approved by the client?
Consider creating two different line items models for Proposal and Invoice.
class Proposal < ActiveRecord::Base
has_many :proposal_line_items
end
class ProposalLineItem < ActiveRecord::Base
belongs_to :proposal
end
class Invoice < ActiveRecord::Base
has_many :invoice_line_items
end
class InvoiceLineItem < ActiveRecord::Base
belongs_to :invoice
end
You can consider having an "approved" attribute in proposal line items. In the invoice form, you can show proposal line items approved by the client.
The suggestion of having separate line items for Proposal and Invoice is based on ERP data modeling principles to maintain the integrity of Invoice.
Update
For example here are the sample migrations for the models suggested
class CreateProposalLineItems < ActiveRecord::Migration
def change
create_table :proposal_line_items do |t|
t.references :proposal, index: true, foreign_key: true
t.string :name
t.integer :approved
t.timestamps null: false
end
end
end
class CreateProposals < ActiveRecord::Migration
def change
create_table :proposals do |t|
t.string :name
t.timestamps null: false
end
end
end
class InvoicesController < ActionController
def new
#approved_items = Proposal.find(params[:proposal_id]).proposal_line_items.where(:approved => 1)
end
end
You can iterate over the #approved_items in your view and display it to users.
V
I am in quite the pickle , I think I overreach with my current ruby knowledge but I don't want to give up.
I currently have a tweeter that can post and people can follow other people thanks to https://www.railstutorial.org/book/ . I do want to add hashtags to this tutorial tweeter. In order to do I created 2 tables since tweet and hashtag is a many to many relationship . The tables are :
class CreateHashrelations < ActiveRecord::Migration
def change
create_table :hashrelations do |t|
t.integer :tweet_id
t.integer :hashtag_id
t.timestamps null: false
end
add_index :hashrelations, [:tweet_id, :hashtag_id], unique: true
end
end
which is the extra table you need to keep the keys of the tweet and hashtag . And the other table is the hashtag table where I have the id and the name of the hastag
class CreateHashtags < ActiveRecord::Migration
def change
create_table :hashtags do |t|
t.string :name
t.timestamps null: false
end
end
end
In the models I put the following relathipships:
class Hashtag < ActiveRecord::Base
has_many :hashtagrelations, dependent: :destroy
has_many :tweets, through: :hashtagrelations
validates :name, presence: true
end
class Hashrelation < ActiveRecord::Base
belongs_to :tweet
belongs_to :hashtag
validates :tweet_id, presence: true
validates :hashtag_id, presence: true
end
class Tweet < ActiveRecord::Base
.....
has_many :hashtagrelations, dependent: :destroy
has_many :hashtags, through: :hashtagrelations
....
end
When a tweet is submited I save it and if it is saved I want to see if it has hashtags and if it does I want to add the necessary data in the Hashtagrelations and Hashtags tables.
I try to do this this way :
class TweetsController < ApplicationController
......
def create
#tweet = current_user.tweets.build(tweet_params)
if #tweet.save
add_hashtags(#tweet)
flash[:success] = "Tweet created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
......
private
........
def add_hashtags(tweet)
tweet.content.scan(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/){ |tag|
newhash[:new] = tag
#hashtag = Hashtag.new(new_hash[:new])
#hashtag.save
data[:new] = [tweet.id,#hashtag.id]
#hashrel = Hashtagrel.new(data[:new])
#hashrel.save
}
end
end
Which is not the right way. I tried to add the newhash and data because if I only did tag there I would get
When assigning attributes, you must pass a hash as an argument.
I realise that this is kind of a silly question but I have found no tutorial that teaches me how should I add this data to my tables. I would be grateful for your help
This is an array of values:
data[:new] = [tweet.id,#hashtag.id]
Before moving things inside another variable (or data structure), try being explicit first.
#hashrel = Hashtagrelation.new(tweet_id: tweet.id, hashtag_id: #hashtag.id)
The rest of the code looks good, I think you've got it.
I created a new model called 'hashtags' and a new table in my database
Here's the schema.db
create_table "hashtags", :force => true do |t|
t.string "hashtags"
t.integer "post_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
hashtags.rb
class Hashtags < ActiveRecord::Base
attr_accessible :hashtags, :post_id
end
def create
hashtag_regex = /\b#\w\w+/
#post = current_user.posts.build(params[:post])
#post.hashtags = #post.text.scan(hashtag_regex)
end
Inside the post model, this is what I've added
class Post < ActiveRecord::Base
attr_accessible :content
belongs_to :user
has_many :hashtags, dependent: :destroy
For all posts where there's a hashtag (or could be 2+ hashtags), I want to list them in descending order by date in a new page called '/hashtags'.
So this is how I want it to show in view
4/30/2013
#tag3 #tag2 by user2
#tag1 by user5
4/29/2013
#tagz by user10
#tagx #tagy by user3
4/25/2013
#tagz #tagy #tagx by user2
Inside views\static_pages\hashtags.html.erb
I'm trying to create this view, but how could I best go about this?
To save the hash tags you need a before save callback method, and get rid of that logic in the controller.
class Post < ActiveRecord::Base
after_save :process_hashtags
def process_hashtags
hashtag_regex = /\B#\w\w+/
text_hashtags = text.scan(hashtag_regex)
text_hashtags.each do |tag|
hashtags.create name: tag
end
end
end
This callback will iterate through the array of hashtags that it finds in the text, and create a Hashtag model and associate it with the Post model.
Let me know if it works, as it might need some adjustments.
This is how you would dot it in your Model: For
class Post < ApplicationRecord
has_many :hashtags, dependent: :destroy
after_commit :process_hashtags
def process_hashtags
hashtag_regex = (/#\w+/)
text_hashtags = content.to_s.scan(hashtag_regex)
text_hashtags.each do |name|
HashTag.create name: name, address: user.country
end
end
end
Model for # for hashTags
class HashTag < ApplicationRecord
belongs_to :post
end
Can not call my associate table in my view. have tried this. it is an application that only adds to the players. after the press "start game" and then he should come to the result view where the results of all players. then I will of course have the name of the "players" table and then the binding results from the "results" table. now in quire, I enter bidningen in the background as long as
view:
<% #playersname.each do |p|%>
<ul>
<li><%= p.name %></li>
<li><%= p.results.try(:result) %></li>
</ul>
<%end%>
Controller:
class ResultsController < ApplicationController
def index
#playersname = Player.all
end
end
Model:
class Result < ActiveRecord::Base
# attr_accessible :title, :body
has_many :players
end
migration:
class CreateResults < ActiveRecord::Migration
def change
create_table :results do |t|
t.string "result", :limit => 40
t.string "cal", :limit => 40
t.string "sum",:limit => 300
t.timestamps
end
end
end
The CreateResults migration miss the player_id column.
Do belongs_to :player in your Result class and correct the migration (or do another one).
[Edit] I wasn't clear enough : I think you inverted the relationship logic.
You actually did the "one result has several players".
I suppose you want that a player has several results ?
class Player
has_many :results
end
class Result
belongs_to :player
end
So you can do :
#myplayer = Player.all.first
#myplayer.results #it is an array of player's results
#myplayerresult = #myplayer.results.first
puts #myplayerresult.result
If you want a one-to-one relationship, consider replacing has_many :results by has_one :result and so you can do #myplayer.result to get your result.