I have Retailer and Supplier model which inherits from the User model. I been struggling to create Friendship model between them. I want retailer to send friend request to supplier based on #suuplier.account_number. Retailer can only send request if he has account number. So far i have request model which does exactly that. Here is what i have in controller,
class RequestsController < ApplicationController
before_action :authenticate_user!
def new
#retailer = current_user
#request = #retailer.requests.new
end
def create
#retailer = current_user
#supplier = Supplier.find_by(params[:account_number])
#request = #retailer.requests.new(request_params)
#request.retailer = current_user
#request.supplier = #supplier
if #request.save
redirect_to retailer_pending_suppliers_path, notice: "Your request has been successfully sent"
else
render 'new'
end
end
private
def request_params
params.require(:request).permit(:retailer_id, :supplier_id,:account_number ,:status)
end
end
It works but still i am still 100% not confident with what i am doing.This is like a brock wall moment, not sure what to do now. How do i implement friendship with them? what models do i need? and controller?
thank you in advance.
user model
class User < ActiveRecord::Base
before_validation :generate_account_number
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
def role?
self.class.name.downcase.to_sym
end
def admin?
self.type == "Admin"
end
def supplier?
self.type == "Supplier"
end
def retailer?
self.type == "Retailer"
end
def active_for_authentication?
super && approved?
end
def inactive_message
if !approved?
:not_approved
else
super # Use whatever other message
end
end
private
#this method generates and assigns random account number to the users
def generate_account_number
rand_num = SecureRandom.hex(3).upcase
if self.admin?
self.account_number = "EGYPT" + "-" + rand_num
elsif self.retailer?
self.account_number = "NJ" + "-" + rand_num + "-" + "RET"
elsif self.supplier?
self.account_number = "NJ" +"-" + rand_num + "-" + "SUP"
else
self.account_number = 0
end
end
end
Retailer Model
class Retailer < User
has_many :stations
has_many :retailer_suppliers
has_many :suppliers , through: :retailer_suppliers, as: :connections
has_many :requests
end
Supplier Model
class Supplier < User
has_many :retailer_suppliers
has_many :retailers, through: :retailer_suppliers
has_many :requests
end
Request model
class Request < ActiveRecord::Base
belongs_to :retailer
belongs_to :supplier
enum status: [:pending, :approved, :denied]
end
I have User type attribute in User model.
You can have a friendship model similar to your request model that belongs to both retailer and supplier
class Friendship < ActiveRecord::Base
belongs_to :retailer
belongs_to :supplier
end
Alternatively, just use request model with :status=> :approved to track friendship.
Show supplier the request, when the supplier clicks accept button, you can find the request and update_attributes for it from pending to approved
I think this railscast might help you: self referential association
But it's similar to Haider's response. You'll either need another joins table, or modify the requests joins table to indicate a friendship, which I'm guessing will need to store additional metadata.
Related
I have an application with users using devise for authentication, in the user model I have added in the database a column called admin with false value by default. that way I have managed to have access as administrator to certain parts of the application.
I have a subscription model and each user when authenticated gets a free value by default. what I want to achieve is that the admin user in your user list can be able to switch from free to premium. this is the code i have and i can't get it to work.
Users Model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
#Validaciones
validates :nombre, :apellido, presence: true
devise :database_authenticatable, :validatable, password_length: 8..128
#Relaciones
has_many :patients, dependent: :destroy
has_many :articles, dependent: :destroy
has_one :profile, dependent: :destroy
has_one :suscription, dependent: :destroy
#Creación de perfil
after_create :set_profile
def set_profile
self.profile = Profile.create()
end
#Creación de suscripcion
after_create :set_suscription
def set_suscription
self.suscription = Suscription.create()
end
end
Suscription Model:
class Suscription < ApplicationRecord
belongs_to :user
enum status: {
free: 0,
premium: 1
}
end
Users controllers:
class UsersController < ApplicationController
def index
#pagy, #users = pagy(User.order(created_at: :asc), items:12)
end
def show
#user = User.find(params[:id])
end
end
Suscriptios controller:
class SuscriptionsController < ApplicationController
before_action :set_suscription
def show
end
def edit
end
def update
#suscription = Suscription.find(params[:id]).update_params
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
private
def set_suscription
#suscription = (current_user.suscription ||= Suscription.create)
end
def suscription_params
params.require(:suscription).permit(:status)
end
end
Route:
#UPDATE PREMIUM
patch "suscriptions", to:"suscriptions#update", as: "user_premium"
View (Link):
<%= link_to 'Update', user_premium_path ,method: :patch %>
This should fix it:
subscriptions_controller.rb
def update
#suscription = Suscription.find(params[:id]).update(subscription_params)
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
view
<%= link_to 'Update', user_premium_path(id: #subscription.id, status: "premium"), method: :patch %>
One other thing that is not needed, but normally I would see something like this in a controller:
private
def set_suscription
#suscription = Suscription.find(params[:id])
end
which then makes your update method look like this:
def update
#subscription.update(subscription_params)
redirect_to profile_path
flash[:notice] = "La suscripción ha sido actualizada"
end
This is all assuming you are simply trying to update the subscription from free to premium with your link_to. I wouldn't recommend doing anything like this, because what if someone accidentally marks this? They can no longer go back to a free subscription. Maybe have a modal open that is routed to subscription edit with a drop down to select the status would be better?
I'm working on an app where users can search for games (data pulled from an API), and add them to a library. I managed to get the adding part working, but I'm having some issues with deleting a game from the user's library.
Here are my create and destroy functions.
def create
#library_game = Game.new
#library_game.game_id = params[:game_id]
#library_game.fetch_data
#library_game.save!
current_user.build_library
current_user.library.games << #library_game
redirect_to library_path
end
def destroy
current_user.games.destroy(game_id: params[:id])
redirect_to library_path
end
With the current code, when I try to delete something I get the following error:
ActiveRecord::AssociationTypeMismatch in GamesController#destroy
Game(#70255379902700) expected, got {:game=>"52921"} which is an instance of Hash(#70255376633020)
So it seems to be getting the id but it is expecting the entire game? I have tried editing my params to include just :game or :game_id but I am still getting errors.
Here is the link_to to delete the game.
<%= link_to 'Remove from Library', user_game_path(game.id), method: :delete %>
Here are my models showing associations.
Game
class Game < ApplicationRecord
has_many :library_games
has_many :libraries, through: :library_games
has_many :users, through: :libraries
serialize :data
attr_accessor :game_id
def fetch_data
game = GiantBomb::Game.detail(game_id)
self.data = Hash[game.instance_variables.map { |var| [var.to_s[1..-1], game.instance_variable_get(var)] } ]
end
def to_giant_bomb_game
GiantBomb::Game.new(data)
end
end
Library
class Library < ApplicationRecord
belongs_to :user
has_many :library_games
has_many :games, through: :library_games
end
User
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one :library
has_many :games, through: :library
def has_game?(game)
games.where(id: game.id).exist?
end
def build_library
return if library.present?
self.library = Library.new
end
end
Library_Game
class LibraryGame < ApplicationRecord
belongs_to :library
belongs_to :game
has_one :user, through: :library
end
What am I getting wrong with my destroy method?
Here's my understanding of your domain model,
Users (1 to 1)-> Library (1 to n)-> Library Games (1 to 1)-> Games
If this understanding is correct, then I see a problem with your create and destroy methods. You should be creating and destroying instances of LibraryGames, and not Games. Below is a rough idea of how you can implement things,
def create
# current_user.build_library # Wouldn't all your users already have libraries?
#library_game = LibraryGame.new
#library_game.game_id = params[:game_id]
#library_game.library = current_user.library
# #library_game.fetch_data # What does this do? This might not be needed here
#library_game.save!
redirect_to library_path
end
def destroy
current_user.library_games.destroy(params[:id])
redirect_to library_path
end
I am trying to develop a Shopping application which has 3 models namely User(Devise), Product and Batch. I've made an has_many association between User and Product and created a User(signed up in Devise). And then I changed the association into has_and_belongs_to_many and created a migration to create the join table. I've followed this answer https://stackoverflow.com/a/57017241/9110386 to add the Product to current_user. Then I deleted my User account and tried to sign up but it shows an error like this.
NoMethodError in Devise::RegistrationsController#create
undefined method `product' for # Did you mean? products products=
User model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
has_and_belongs_to_many :products, :dependent => :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates_length_of :product, maximum: 10
end
Product model:
class Product < ApplicationRecord
belongs_to :batch
has_and_belongs_to_many :user
validates :name, presence: true
validates_associated :user
end
Product Controller
class ProductsController < ApplicationController
before_action :authenticate_user!
def index
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def new
#product = Product.new
end
def edit
end
def create
end
def update
end
def destroy
end
def add_cart
product = Product.find(params[:product_id])
#current_user.products << product
#current_user.products << product unless current_user.products.include?(product)
if current_user.products.include?(product)
redirect_to products_path, notice: "Already in your cart"
else
current_user.products << product
redirect_to products_path, notice: "Added to cart"
end
end
end
What am I doing wrong here. And I also want to remove the Product from the cart by destroying it from the current_user. How to do that?
Thanks in advance.
You have left behind an old validation in your user model.
Delete this line in the app/models/user.rb file
validates_length_of :product, maximum: 10
Your error is flagging the Devise RegistrationsController’s create method. You’ve likely left a reference to user.product in there whereas users have products plural.
I have a "userinfos_controller" where user stores their information. They also can upload a video to their profile. I am showing this video under the show method in the userinfos_controller as shown below. I want the '#myvideo" to get the video associated with the user. When a user deletes their video and re-upload, the video_id changes. So I can't call that video using the video_id. Is there a way to call Video.userinfo.last? I don't want to call Video.last, because it'll give me the last video uploaded across all users, not just the last video uploaded by that particular user. You can also see in my rails console, the video has a user_id, userinfo_id and a video_id. So right now, under my show method, when I do:
def show
#myvideo = Video.find(params[:userinfo_id])
end
It looks for Video_id that is equal to userinfo_id, while I want it to look for the last video uploaded by that particular user/userinfo.
My id relationships(please right click and open in new tab to see more clearly):
My userinfos controller:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#userinfors = Userinfo.all
#myvideo = Video.all
end
def show
#myvideo = Video.find(params[:userinfo_id])
end
def new
#userinformation = current_user.userinfos.build
end
def create
#userinformation = current_user.userinfos.build(userinfo_params)
if #userinformation.save
redirect_to root_path
else
render 'new'
end
end
def edit
end
def update
end
def destroy
#userinformation.destroy
redirect_to userinfo_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.find(params[:id])
end
end
Show video view:
<div>
<%= video_tag #myvideo.introvideo_url.to_s, :size => "480x320", :controls =>true %>
</div>
Video model:
class Video < ActiveRecord::Base
belongs_to :userinfo
belongs_to :user
mount_uploader :introvideo, VideoUploader
end
Userinfo model:
class Userinfo < ActiveRecord::Base
belongs_to :user
has_many :videos
end
User model:
class User < ActiveRecord::Base
has_many :vidoes
has_many :userinfos
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
My migrations that show all the id's added:
My routes:
Either create a direct association between User and Video - or make it indirect through a join model. Don't do both.
Direct 1-n:
class User < ApplicationRecord
has_many :videos
has_many :user_infos
# ...
end
class Video < ApplicationRecord
belongs_to :user
# ...
end
class UserInfo < ApplicationRecord
belongs_to :user
has_many :videos, through: :user
end
Indirect 1-n:
class User < ApplicationRecord
has_many :user_infos
has_many :videos, through: :user_infos
# ...
end
class UserInfo < ApplicationRecord
belongs_to :user
has_many :videos
end
class Video < ApplicationRecord
belongs_to :user_info
has_one :user, through: :user_info
# ...
end
Both will let you do:
#user.videos.last
#user_info.videos.last
def show
#my_video = #user_info.videos.order(created_at: :desc).last
end
Try with ActiveRecord#last:
def show
#myvideo = Video.where('userinfo_id = ?', params[:userinfo_id]).last
end
That will give you the videos uploaded to userinfo with id equal to params[:userinfo_id] taking the last record.
Not sure if I understood you correctly, but I think you want to access the Video that your user previously uploaded, after it uploaded the new one.
Assuming you have a has_many :videos relationship set up on the User model, you can do this:
#user.videos.order(created_at: :desc).second
Or if you don't have the user instance and just have user_id.
Video.where(userinfo_id: params[:userinfo_id]).order(created_at: :desc).second
Hope this helps.
EDIT:
Or maybe you just want to access the latest users video. Again, I don't know how you set up your relations. I am assuming user can have many videos.
Video.where(userinfo_id: params[:userinfo_id]).order(created_at: :desc).first
Or shorter
Video.where(userinfo_id: params[:userinfo_id]).last
I am creating a blog that has pins and comments. I am wondering how to create a system whereby the admin can approve comments on blogs before users can view them.
First I tried adding a boolean field called pinreview to my comments model:
comment.rb
class Comment < ActiveRecord::Base
belongs_to :pin
scope :approved, ->{
where(:pinreview => false)
}
scope :pending, -> {
where(:pinreview => true)
}
scope :newest, -> {
order("created_at desc")
}
end
class CommentsController < ApplicationController
def create
#pin = Pin.find(params[:pin_id])
#comment = #pin.comments.create(params[:comment].permit(:name, :body))
redirect_to pin_path(#pin)
end
def destroy
#pin = Pin.find(params[:pin_id])
#comment = #pin.comments.find(params[:id])
#comment.destroy
redirect_to pin_path(#pin)
end
end
I have defined the user types through enums:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
enum access_level: [:guest, :admin]
has_many :pins
def admin
admin?
end
def guest
guest?
end
end
And in my view I have been trying things like this:
pins/show.html.erb
...
- if current_user && current_user.admin? ? #pin.comments = Comment.all : #pin.comments = Comment.where(:approved => false)
...
The pinned post shows, but not the user comments, and when logged in as admin, no space to approve comments shows. How would I create the view for admins to approve comments and then have the comments rendered in the guest user view when approved?
Also, I should mention that comments can be made by anonymous users – a user does not have to be signed up or logged in.
Would appreciate any guidance on this. I can provide more code if required. Thanks.
Simplest way to do this is like this:
(current_user.try(admin?) ? #pin.comments : #pin.comments.approved).each do |comment|