Rails 3: Delete association duplicates from the database - ruby-on-rails-3

I've got these models:
class Relationship < ActiveRecord::Base
belongs_to :student
belongs_to :guardian
end
class Student < ActiveRecord::Base
has_many :relationships
has_many :guardians, through: :relationships
end
class Guardian < ActiveRecord::Base
has_many :relationships
has_many :students, through: :relationships
end
I don't want any guardian to be related many times with one student. This can be prevented with a validation, but let's say it's too late and there are case where this is happening. For example, where g is a guardian:
g.relationships
[
[0] #<Relationship:0x0000000bc33650> {
:id => 40321,
:relationship_type_id => 2,
:student_id => 41700,
:guardian_id => 45820,
:created_at => Tue, 23 Apr 2013 17:44:29 UTC +00:00,
:updated_at => Tue, 23 Apr 2013 17:44:29 UTC +00:00,
},
[1] #<Relationship:0x0000000bc32e80> {
:id => 40923,
:relationship_type_id => 2,
:student_id => 41700,
:guardian_id => 45820,
:created_at => Tue, 23 Apr 2013 18:58:46 UTC +00:00,
:updated_at => Tue, 23 Apr 2013 18:58:46 UTC +00:00,
}
]
As you can see, these two relationships share the same student_id. I want to find out if there's a way I can delete from the database the duplicated relationships. I've tried the following lines of code to no avail:
g.relationships.uniq!(&:student_id)
g.update_attributes(relatioships: g.relationships.uniq!(&:student_id))
How can I solve this problem. Thanks!

subquery = Relationship.select("student_id, guardian_id").
group(:student_id, :guardian_id).
having("count(*) > 1").to_sql
groups = Relationship.joins("JOIN (#{subquery}) sub ON relationships.student_id = sub.student_id AND relationships.guardian_id = sub.guardian_id").
group_by{|r| [r.student_id, r.guardian_id] }
groups.values.each do |duplicates|
duplicates.drop(1).each(&:destroy)
end

Related

Saving wrong id for messaging system

I am trying to set up a messaging system on my app and I had it working, but now ActiveRecord is saving the wrong id for the recipient_id.
Here's where the user.id is loaded as 3 but when the values are inserted the messages's recipient.id is 5:
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 ORDER BY "users"."id"
ASC LIMIT 1
Message Load (0.4ms) SELECT "messages".* FROM "messages" LIMIT 1
User Load (0.6ms) SELECT "users".* FROM "users" LIMIT 1
(0.5ms) BEGIN
SQL (1.3ms) INSERT INTO "messages" ("content", "created_at", "recepient_id", "sender_id",
"subject", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["content", "erferfe"],
["created_at", Wed, 17 Dec 2014 19:15:08 UTC +00:00], ["recepient_id", 5], ["sender_id", 3],
["subject", "erfer"], ["updated_at", Wed, 17 Dec 2014 19:15:08 UTC +00:00]]
(5.4ms) COMMIT
Redirected to http://0.0.0.0:3000/messages
Completed 302 Found in 23ms (ActiveRecord: 9.6ms)
Here's MessagesController#create:
def create
#message = current_user.sent_messages.new(message_params)
#message.recepient_id = User.find_by(params[:id]).id
if #message.save
flash[:notice] = 'Message has been sent.'
redirect_to messages_path
else
render :action => :new
end
end
Here's messages.rb:
class Message < ActiveRecord::Base
belongs_to :sender, class_name: "User", primary_key: "sender_id"
belongs_to :recepient, class_name: "User", primary_key: "recepient_id"
belongs_to :user
This is the routing for the messages:
resources :users do
resources :messages
end
This is the view:
<div class="ui button"><i class="mail icon"></i><%= link_to 'Message', new_user_message_path(#user) %></div>
Message_params:
def message_params
params.require(:message).permit(:subject, :user_id, :content, :recepient_id)
end
User.rb
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :omniauthable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauth_providers => [:facebook, :twitter]
TEMP_EMAIL_PREFIX = 'change#me'
TEMP_EMAIL_REGEX = /\Achange#me/
attr_accessor :login
has_many :projects, class_name: "Project", foreign_key: "creator_id"
has_many :sent_messages, class_name: "Message", foreign_key: "sender_id"
has_many :recieved_messages, class_name: "Message", foreign_key: 'recepient_id'
has_many :messages
has_many :projects, dependent: :destroy
has_many :authentications, :dependent => :destroy
validates :email, presence: true,
uniqueness: true,
format: {
with: /\A[A-Za-z0-9._%+-]+#[A-Za-z0-9\.-]+\.[A-Za-z]+\Z/
}
validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
def downcase_email
self.email = email.downcase
end
def generate_password_reset_token
update_attribute(:password_reset_token, SecureRandom.urlsafe_base64(48))
end
def self.find_for_facebook_oauth(auth)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(:first_name => auth.extra.raw_info.first_name,
:last_name => auth.extra.raw_info.last_name,
:avatar => auth.info.image,
:provider => auth.provider,
:uid => auth.uid,
:email => auth.info.email,
:password => Devise.friendly_token[0,20]
)
user.confirm!
end
user.save
end
def self.find_for_twitter_oauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.avatar = auth.info.image
user.save
end
end
After code change suggested:
User Load (1.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 ORDER BY "users"."id" ASC
LIMIT 1
Message Load (0.3ms) SELECT "messages".* FROM "messages" LIMIT 1
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "messages" ("content", "created_at", "sender_id", "subject", "updated_at")
VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["content", "efvefvf"], ["created_at", Wed, 17 Dec 2014
21:02:48 UTC +00:00], ["sender_id", 2], ["subject", "erfvef"], ["updated_at", Wed, 17 Dec 2014
21:02:48 UTC +00:00]]
(7.2ms) COMMIT
Also when I go /users/:user_id/messages my css and javascript files don't work
I am assuming that this feature is initialized from a User "Profile" page which allows the current session user (sender) to send a message to the profile user (recipient).
First off, in your Message model, you do not need belongs_to :user on the Message model nor do you need has_many :messages on the User model.
Next, you have way over complicated the #create action:
def create
#message = current_user.sent_messages.new(message_params)
#message.recipient_id = params[:user_id]
if #message.save
flash[:notice] = 'Message has been sent.'
redirect_to messages_path
else
render :action => :new
end
end
Now, adjust your message_params. You do not need the user_id (will be provided by current_user) nor do you need the recipient_id (will be provided by the url param).
def message_params
params.require(:message).permit(:subject, :content)
end
Give that a try.

Rails 4 has_many through with condition, joining same table twice

I have 3 models, User, Division and Staff. Staff belongs to User & Division respectively and stored a value called role. There are currently 2 roles which are "1" and "2". Below is my relationship.
User:
class User < ActiveRecord::Base
has_many :staffs, :dependent => :destroy
# manager view
has_many :manage_divisions, -> { where staffs: { role: 1 } }, :through => :staffs, :source => :division
has_many :manage_division_staffs, :through => :manage_divisions, :source => :staffs
# staff view
has_many :work_for_divisions, -> { where staffs: { role: 2 } }, :through => :staffs, :source => :division
has_many :work_for_division_staffs, :through => :work_for_divisions, :source => :staffs
end
I want to get the divisions which the user manage through the staff table by User.manage_divisions. Then get the staff list of those divisions by User.manage_division_staffs. But now when I call User.manage_division_staffs, what return seems the staffs with role = 1 of the divisions(but not all staffs). Also I want to do something similar for User.work_for_divisions.
Below is the sql query generated when I called User.manage_division_staffs:
SELECT "staffs".*
FROM "staffs"
INNER JOIN "divisions" ON "staffs"."division_id" = "divisions"."id"
INNER JOIN "staffs" "staffs_manage_division_staffs_join" ON "divisions"."id" = "staffs_manage_division_staffs_join"."division_id"
WHERE "staffs"."role" = 1
AND "staffs_manage_division_staffs_join"."user_id" = ? [["user_id", 3]]
I know what I am doing wrong but I don't know the way to correct it. Any ideas??

Trouble getting attributes into my build_order method for stripe payments

I'm building a video marketplace where members can buy videos.
However I'm struggling to create the order so that it has the video_id, member_id and price.
This is my code:
Video model:
has_many :members, through: :orders
has_many :orders
accepts_nested_attributes_for :orders
Member model:
has_many :orders
has_many :videos, through: :orders
accepts_nested_attributes_for :orders
Order model:
attr_accessible :price, :stripe_card_token, :member_id, :video_id
belongs_to :video
belongs_to :member
accepts_nested_attributes_for :video
accepts_nested_attributes_for :member
validates :member_id, presence: true
validates :video_id, presence: true
validates :price, presence: true
attr_accessor :stripe_card_token
def save_with_stripe
video = #video.find_by_id(params[:id])
member = #member.find_by_id(params[:id])
if valid?
#order = Stripe::Charge.create(
amount: video.price,
currency: "gbp",
card: stripe_card_token,
description: member.email
)
save!
end
rescue
errors.add :base, "There was a problem with Stripe"
end
Orders Controller:
def new
#order = Order.new
build_order
end
def create
#order = Order.new(params[:order])
if #order.save_with_stripe
flash[:success] = "Enjoy the video!"
else
render partial: 'shared/buynow'
end
end
def build_order
#order.build_member(
member_id: #member.id,
video_id: #video.id,
price: #video.price,
)
end
I think my issue is somewhere in the "build_order" method, but I've tried various different ways and end up with various different errors.
It works in my console:
[221] pry(main)> Order.create(member_id: "36", video_id: "99", price: "1000")
(0.2ms) BEGIN
SQL (0.6ms) INSERT INTO "orders" ("created_at", "member_id", "price", "stripe_card_token", "updated_at", "video_id") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["created_at", Fri, 18 Oct 2013 14:50:09 UTC +00:00], ["member_id", 36], ["price", 1000], ["stripe_card_token", nil], ["updated_at", Fri, 18 Oct 2013 14:50:09 UTC +00:00], ["video_id", 99]]
(0.5ms) COMMIT
=> #<Order id: 3, video_id: 99, member_id: 36, stripe_card_token: nil, price: 1000, created_at: "2013-10-18 14:50:09", updated_at: "2013-10-18 14:50:09">
Any help on what I'm doing wrong would be greatly appreciated.
Cheers,
Mark
Why are you trying to build a member object through the order? Looks like you already have the member and video, so why not just pass the same hash of values in to the call to new?
e.g.
#order = Order.new(member_id: #member.id, video_id: #video.id, price: #video.price)

Rails 3.1 postgresql shooting blanks into db

I'm using postgresql in development for the first time and have successfully installed the binary onto my 10.6 machine. I've created a Rails superuser, createdb => 'vitae_development' with this user. It shows up in $pqsl => '/du', but when I key in /dt, I get 'no relations'.
My pg gem is pg-0.12.0
In rails 3.10 console, I enter: User.create!(:name => "Sam", :email => "sam#email.me")
The resulting output is:
INSERT INTO "users" ("created_at", "email", "name", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["created_at", Wed, 21 Dec 2011 19:40:13 UTC +00:00], ["email", nil], ["name", nil], ["updated_at", Wed, 21 Dec 2011 19:40:13 UTC +00:00]]
That appears to be a bunch of blanks. I've searched the googles but must have missed the right search terms.
pgAdmin3 seems to show the tables in place, as best as I can determine, but with no data that I can find.
This is the relevant snippet from my database.yml:
development:
adapter: postgresql
database: vitae_development
username: rails
password:
pool: 5
timeout: 5000
For completeness, here's the whole user.rb:
class User < ActiveRecord::Base
attr_accessor :name, :email
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_insensitive => false }
end
What am I overlooking? I've done rake db:migrate.
Did you perhaps override the email or email= methods or put attr_accessor :email in your User.rb file?
EDIT:
Take out line 2 where it says attr_accessor :name, :email. attr_accessor in the context of rails is for object variables that won't be saved to the database.
Try eliminating the formatting validation on email...if it works I'd take a deeper look at email_regex

Ruby on Rails 3 - callbacks not working any more

Hi people.
I have a big problem. Untill 2 weeks ago, my code was working fine, but today I realize that some callbacks are not working any more.
The callback is the following:
class DetailPurchase < ActiveRecord::Base
belongs_to :purchase, :foreign_key => 'purchase_id'
belongs_to :product, :foreign_key => 'product_id'
belongs_to :buy_order_detail, :foreign_key => 'buy_detail_id'
def before_create
Storage.create!(:product_id => self.product_id, :current_quantity => self.quantity, :stg_data => purchase.prc_data)
end
end
The idea is that everytime a Detail_purhase is created, a storage with the same product should be created automatically after that.
But now it is not working, the only change is now I'm using jquery instead of prototype
Could that be the problem?
Weird it worked. Correct syntax is:
class DetailPurchase < ActiveRecord::Base
belongs_to :purchase, :foreign_key => 'purchase_id'
belongs_to :product, :foreign_key => 'product_id'
belongs_to :buy_order_detail, :foreign_key => 'buy_detail_id'
before_create :create_storage
def create_storage
Storage.create!(:product_id => self.product_id, :current_quantity => self.quantity, :stg_data => purchase.prc_data)
end
end