I have a table with friendships. It's build like in http://railscasts.com/episodes/163-self-referential-association. Now I want to the most efficient way to get all relations, which are made from both sides (The normal and the inverse friendship).
In MySQL it should look like:
SELECT * FROM Friendship as f1, Friendship as f2 WHERE f1.user_id = f2.friend_id AND f1.friend_id = f2.user_id
In a comment on railscast someone build the extension of the mutual_friends part. But these three lines don't work in my case:
Devise User Model:
class User < ActiveRecord::Base
has_many :features
has_and_belongs_to_many :tags
has_many :friendships
has_many :friends, through: :friendships
has_many :inverse_friendships, class_name: 'Friendship', foreign_key: 'friend_id'
has_many :inverse_friends, through: :inverse_friendships, source: :user
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :first_name, :last_name, :email, :password, :date_of_birth, :gender, :password_confirmation
validates_inclusion_of :gender, :in => ['female', 'male'], message: 'Kein Geschlecht ausgewaehlt'
validates :first_name, presence: true
validates :last_name, presence: true
validates_date :date_of_birth, :on_or_before => lambda { Date.current }
def mutual_friends
inverse_friends.where('user_id in (?)', friend_user_ids)
end
end
Devise User Migration:
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
t.string :email, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.datetime :remember_created_at
t.integer :sign_in_count, :default => 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
t.string :first_name
t.string :last_name
t.datetime :date_of_birth
t.string :gender
t.integer :tag_id
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
end
end
Friendship Model
class Friendship < ActiveRecord::Base
attr_accessible :user_id, :friend_id
belongs_to :user
belongs_to :friend, class_name: 'User'
end
Friendship Migration
class CreateFriendships < ActiveRecord::Migration
def self.up
create_table :friendships do |t|
t.integer :user_id
t.integer :friend_id
t.timestamps
end
end
def self.down
drop_table :friendships
end
end
The Error in this solution is:
undefined local variable or method `friend_user_ids' for #<User:0x007fcb93e86de8>
The orginal constellation in the comment on Railscast is:
Friend Model
class Friend < ActiveRecord::Base
belongs_to :profile
belongs_to :friend, :class_name => 'Profile'
end
Profile Model
class Profile < ActiveRecord::Base
has_many :friends
has_many :friend_profiles, :through => :friends, source: :friend
has_many :friended_by, class_name: "Friend" , :foreign_key => "friend_id"
has_many :friended_by_profiles, :through => :friended_by, source: :profile
def mutual_friends
friended_by.where('profile_id in (?)', friend_profile_ids)
end
def mutual_friend_profiles
Profile.where('id in (?)', mutual_friends.pluck(:profile_id))
end
def requesting_friends
friended_by.where('profile_id not in (?)', friend_profile_ids)
end
def requesting_friend_profiles
Profile.where('id in (?)', requesting_friends.pluck(:profile_id))
end
def pending_friends
friends.where('friend_id not in (?)', friended_by_profile_ids)
end
def pending_friend_profiles
Profile.where('id in (?)', pending_friends.pluck(:friend_id))
end
end
Thanks for helping!
The solution how I get it to work:
Remove mutual_friend
def mutual_friends
inverse_friends.where('user_id in (?)', friend_user_ids)
end
Change the logic in the FriendshipController to:
#all mutual friendships with marked true in both relations
#user = current_user
#user_friends = #user.friendships.select{ |friendship| friendship.marked == true }
#user_inverse_friends = #user.inverse_friendships.select{ |friendship| friendship.marked == true }
#friendships = #user_friends.select{ |friend| #user_inverse_friends.map{|inverse_friend| inverse_friend.user_id}.include? friend.friend_id}
#friendships with false mark
#skiped_friendships = current_user.friendships.select { |friendship| friendship.marked != true}
#marked friend: users friendships without the mutual valid friendships
#marked_friends = current_user.friendships - #friendships - #skiped_friendships
Related
I have Users and Protests tables. Users can create protests.
I want to add user foreign key to Protests table with a different name "creator". Whenever I run my migrations, I expect to see creator_id in my Protests table, but I see user_id instead in my schema.rb. What could be the reason for that? Thank you.
User Model
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :first_name, :last_name, :email, presence: true
has_many :protests, foreign_key: :creator_id
has_many :attendances
has_many :attended_protests, through: :attendances, source: :protest
has_many :passengers
has_many :transportations, through: :passengers
end
Protest Model
class Protest < ApplicationRecord
validates :name, :description, :location,
:starts_at, :creator, presence: true
has_attached_file :image, styles: {thumb: "100x100#", medium:"400x600#" }, default_url: "/images/default_:style_avatar.png"
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
belongs_to :creator, class_name: :User
has_many :attendances
has_many :users, through: :attendances
has_many :transportations
end
Protests Migration
class CreateProtests < ActiveRecord::Migration[5.1]
def change
create_table :protests do |t|
t.string :name
t.text :description
t.string :location
t.datetime :starts_at
t.references :user
end
end
end
What I get in my schema.rb
...
create_table "protests", force: :cascade do |t|
t.string "name"
t.text "description"
t.string "location"
t.datetime "starts_at"
t.bigint "user_id"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.index ["user_id"], name: "index_protests_on_user_id"
end
...
You see user_id because of this line in your migration:
t.references :user
To get creator_id you can use integer instead of references:
t.integer :creator_id
Try changing you migration to:
t.references :creator, index: true, foreign_key: { to_table: :users }
credit to this answer:
https://stackoverflow.com/a/42056089/4553162
I have categories model which I would like to be able to use with different models. That's how I ended up using Polymorphic with has_many.
With Rails_admin everything works without a problem. But, when I want to create a form by myself, I can't seem to make it save. Here is what I have:
category.rb
class Category < ActiveRecord::Base
has_many :categorizings, inverse_of: :category, dependent: :destroy
has_many :cars, through: :categorizings, :source => :categorizable,
:source_type => 'Car'
end
categorizing.rb
class Categorizing < ActiveRecord::Base
belongs_to :category
belongs_to :categorizable, :polymorphic => true
end
car.rb
class Car < ActiveRecord::Base
has_many :categorizings, :as => :categorizable, inverse_of: :car, dependent: :destroy
has_many :categories, through: :categorizings
end
vendor.rb
class Vendor < ActiveRecord::Base
has_many :categorizings, :as => :categorizable, inverse_of: :vendor, dependent: :destroy
has_many :categories, through: :categorizings
end
cars_controller.rb
class CarsController < ApplicationController
def new
#car = Car.new
end
def create
#car = current_user.cars.build(car_params)
if #car.save
redirect_to #car
else
render 'new'
end
end
private
def car_params
params.require(:car).permit(:name, :details, :type, :category_ids => [] )
end
end
schema.rb
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "categorizings", force: :cascade do |t|
t.integer "category_id"
t.integer "categorizable_id"
t.string "categorizable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "categorizings", ["categorizable_type", "categorizable_id"], name: "index_categorizings_on_categorizable_type_and_categorizable_id", using: :btree
This is what I have in the form
<%= f.collection_select :category_ids, Category.all, :id, :name %>
And I receive this error:
Unpermitted parameter: category_ids
I am very confused right now and lost in models. Dont know this is the best approach or not. I would be glad if someone could tell me where I do the mistake.
Using this select_tag solved my problem
<%= f.select :category_ids, Category.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>
but stops working if I disable multiple like
:multiple => false
I have created a BETA Invitation system by following this tutorial. http://railscasts.com/episodes/124-beta-invitations. Users can also follow one another in my Rails app.
How can I have the Invitee follow the person who Invited him on sign up?
Currently, I am trying to establish this in my User Model using a method, but I am having trouble creating the method that allows the Invitee to follow the Inviter via sender_id/user_id.
This is the code I have used so far.
SCHEMA
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.integer "invitation_id"
t.integer "invitation_limit"
t.timestamp "created_at", :null => false
t.timestamp "updated_at", :null => false
t.string "password_reset_token"
t.timestamp "password_reset_sent_at"
end
create_table "invitations", :force => true do |t|
t.integer "sender_id"
t.string "recipient_email"
t.string "token"
t.datetime "sent_at"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
MODELS
USER
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :invitation_token
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
has_many :sent_invitations, :class_name => 'Invitations', :foreign_key => 'sender_id'
belongs_to :invitation
after_create :follow_inviter #---------- HERE!!
def follow_inviter #---------- HERE!!
inviters = Invitation.find_by_sender_id
inviters.each do |invite|
self.follow!(invite)
end
end
def invitation_token
invitation.token if invitation
end
def invitation_token=(token)
self.invitation = Invitation.find_by_token(token)
end
def following?(other_user)
relationships.find_by_followed_id(other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by_followed_id(other_user.id).destroy
end
end
RELATIONSHIPS
class Relationship < ActiveRecord::Base
attr_accessible :followed_id
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
INVITATION
class Invitation < ActiveRecord::Base
attr_accessible :recipient_email, :sender_id, :sent_at, :token
belongs_to :sender, :class_name => "User"
has_one :recipient, :class_name => "User"
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
before_create :generate_token
private
def generate_token
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
end
This should work.
def follow_inviter
if invitation = Invitation.find_by_recipient_email(email)
follow!(invitation.sender)
end
end
But your models associations are not well defined. For example has_one :recipient, :class_name => "User" in the Invitation expects that there is a recipient_id in the table, but is not. You should review that.
I might be wrong, I'm a junior rails developer, but it looks like where you have inviters = Invitation.find_by_sender_id you should have something like this inviters = Invitation.find_by_sender_id(id_of_sender) where id_of_sender is the id of the person who sent the invitation.
The find_by_sender_id takes 1 argument (the ID of the sender to be found) and that is why you get the error wrong number of arguments (0 for 1).
Also for what it's worth, I'm pretty sure the find_by_*_id methods, where * is a model in your database, are deprecated. Rails 4 uses something like Invitation.find(id_of_sender). When you call the find method on an active record model, it takes an id as a parameter.
You can also use Invitation.find_by(email: 'user#example.com') to find records based on any property you give them.
Apologies if I am misunderstanding your question... I have a similar requirement, where one user can mark another as a 'favorite' user (i.e. users table has a self-relationship)
To implement this, I added a table called user_favorite like this:
db\schema.rb
create_table "user_favorites", :id => false, :force => true do |t|
t.integer "user_id"
t.integer "favorite_user_id"
end
add_index "user_favorites", ["user_id"], :name => "index_user_favorites_on_user_id"
app\models\user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :favorite_users, :class_name => 'User', :join_table => "user_favorites", :foreign_key => "user_id", :association_foreign_key => "favorite_user_id"
def is_favorite?(user)
self.favorite_users.include?(user)
end
def toggle_favorite(favorite_user)
if favorite_user
if self.favorite_users.include?(favorite_user)
self.favorite_users.delete favorite_user
else
self.favorite_users << favorite_user
end
end
end
end
I'm making an extension for refinerycms in rails and this is the structure I follow :
Project has many project_images
Project_images belongs_to Project & Image ( The refinery Image class )
Now when I want to create a new object of ProjectImage in my Project class I always get this error :
Unknown primary key for table refinery_projects_images in model Refinery::Projects::ProjectImage.
I don't need a primary key for this table because it is a join table. Here is the code of my models and migration file:
Migration.rb
class CreateProjectsProjects < ActiveRecord::Migration
def up
create_table :refinery_projects do |t|
t.string :title
t.text :description
t.string :investor
t.string :location
t.string :area
t.string :purpose
t.string :architect
t.string :users
t.integer :position
t.integer :position
t.timestamps
end
add_index :refinery_projects, :id
create_table :refinery_projects_images, :id => false do |t|
t.references :image
t.references :project
t.integer :position
t.string :category
t.string :caption
end
add_index :refinery_projects_images, [:image_id, :project_id], :uniq => true
end
def down
if defined?(::Refinery::UserPlugin)
::Refinery::UserPlugin.destroy_all({:name => "refinerycms-projects"})
end
if defined?(::Refinery::Page)
::Refinery::Page.delete_all({:link_url => "/projects/projects"})
end
drop_table :refinery_projects
drop_table :refinery_projects_images
end
end
Project.rb
module Refinery
module Projects
class Project < Refinery::Core::BaseModel
self.table_name = 'refinery_projects'
attr_accessible :title, :description, :investor, :location, :area, :purpose, :architect, :users, :position, :position, :images_attributes
acts_as_indexed :fields => [:title, :description, :investor, :location, :area, :purpose, :architect, :users]
validates :title, :presence => true, :uniqueness => true
has_many :project_images
has_many :images, :through => :project_images, :order => 'position ASC'
accepts_nested_attributes_for :images, :allow_destroy => false
def images_attributes=(data)
ProjectImage.delete_all(:project_id => self.id)
(0..(data.length-1)).each do |i|
unless (image_data = data[i.to_s]).nil? or image_data['id'].blank?
project_image = self.project_images.new(:image_id => image_data['id'].to_i, :position => i)
# Add caption if supported
if false
project_image.caption = image_data['caption']
end
self.project_images << project_image
end
end
end
end
end
end
ProjectImage.rb
module Refinery
module Projects
class ProjectImage < Refinery::Core::BaseModel
self.table_name = 'refinery_projects_images'
attr_accessible :image_id, :position
belongs_to :image, :class_name => 'Refinery::Image'
belongs_to :project, :class_name => 'Refinery::Projects::Project'
end
end
end
Somebody knows why he keeps looking for the primary key?
Refinery::Core::BaseModel is somehow derived from ActiveRecord::Base. When you use that class then your table layout needs an id. If you don't want an id, Have a look at has_and_belongs_to_many in the rails guides: http://guides.rubyonrails.org/association_basics.html
Ok so I have this relationship in Rails:
class Position < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
class User < ActiveRecord::Base
has_many :companies, :through => :positions
has_many :positions
class Company < ActiveRecord::Base
has_many :positions
has_many :users, :through => :positions
Here is the schema for positions:
create_table "positions", :force => true do |t|
t.integer "company_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.boolean "regular_user", :default => true
end
The regular_user is signaling the admins and the employees so my question is how do set the regular_user to 0 or false from this data:
#user = User.find(params[:user_id])
#company = Company.find(params[:company_id])
#user.companies << #company
Is there a better way to do this? I was thinking:
Position.create(user_id: params[:user_id], company_id: params[:company_id], regular_user: 0)
But is there a standard for setting associations?
Try this:
class User < ActiveRecord::Base
has_many :positions
has_many :companies, :through => :positions
has_many :companies_as_non_regular_user, :through => :positions,
:conditions => {:"positions.regular_user" => false}
...
end
#user = User.find(params[:user_id])
#company = Company.find(params[:company_id])
#user.companies_as_non_regular_user << #company