Rails 4 - Validates uniqueness with has_many through - ruby-on-rails

I have these three models:
User:
class User < ActiveRecord::Base
validates :name, presence: true
validates :surname, presence: true
validates :email, presence: true, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
has_many :permissions, dependent: :destroy
has_many :stores, through: :permissions
end
Store:
class Store < ActiveRecord::Base
validates :name, presence: true
validates :description, presence: true
has_many :permissions
has_many :users, through: :permissions
end
Permission:
class Permission < ActiveRecord::Base
belongs_to :user
belongs_to :store
end
How can i validate the uniqueness of the user.email based on the store.id?

You don't.
You should validate the uniqueness of the users email in User. And validate the uniqueness of the user_id and store_id in Permission.
class User < ApplicationRecord
# ...
validates_uniqueness_of :email
end
class Permission < ApplicationRecord
validates_uniqueness_of :user_id, scope: 'store_id'
end
This allows a user to have permissions for multiple stores - but does not allow duplicates. In general when linking records together use id's - not something like emails.

Related

Rails: create relationship between user posts comments and likes

I am trying to implement likes into my rails app.
like model
class Like < ApplicationRecord
belongs_to :comment
belongs_to :post
belongs_to :user
validates :user_id, uniqueness: {scope: :post_id}
validates :user_id, uniqueness: {scope: :comment_id}
end
user model
class User < ApplicationRecord
has_secure_password
has_one :profile, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :posts, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :liked_comments, :through => :likes, :source => :comment, dependent: :destroy
has_many :liked_posts, :through => :likes, :source => :post, dependent: :destroy
has_one_attached :avatar
validates :username, presence: true, uniqueness: true
validates :email, presence: true, uniqueness: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password, length: { minimum: 8 }
validates :password, format: { with: /\A[a-zA-Z0-9!##$%^&*()_]+\z/ }
# validates :password, confirmation: true
# validates :password_confirmation, presence: true
end
post & comment model
class Post < ApplicationRecord
belongs_to :category
belongs_to :user
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy
has_many :liking_users, :through => :likes, :source => :user
has_many_attached :images
def liked?(user)
!!self.likes.find{|like| like.user_id == user.id}
end
end
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
has_many :likes, dependent: :destroy
has_many :liking_users, :through => :likes, :source => :user
has_many_attached :images
def liked?(user)
!!self.likes.find{|like| like.user_id == user.id}
end
end
The issue I'm having is when I run my seed data I get one of two errors NoMethodError: undefined method "marked_for_destruction?" for false:FalseClass if set up like so:
#like0 = Like.create!(
user: #admin0,
post: #post3 & #post5 & #post6 & #post8 & #post9,
comment: #comment1,
username: #admin0.username
)
or NotNullViolation: ERROR: null value in column "comment_id" violates not-null constraint if set up like so:
class Like < ApplicationRecord
belongs_to :comment, optional: true
belongs_to :post, optional: true
belongs_to :user
validates :user_id, uniqueness: {scope: :post_id}
validates :user_id, uniqueness: {scope: :comment_id}
end
#like0 = Like.create!(
user: #admin0,
post: #post3,
comment: #comment1,
username: #admin0.username
)
my question is how can I better set up the relationship between these 4 models so as to not get these errors?

Cascade of deletes ActiveRecord

How can add a cascade of deletes that will remove Profile, TodoList, and TodoItem rows for any User removed.
User Model:
class User < ActiveRecord::Base
has_one :profile
has_many :todo_lists
has_many :todo_items, through: :todo_lists, source: :todo_items
validates :username, presence: true
end
Profile Model:
class Profile < ActiveRecord::Base
belongs_to :user
validates :first_name, presence: true
validates :last_name, presence: true
validates :gender, inclusion: %w(male female)
validate :first_and_last
validate :male_Sue
def first_and_last
if (first_name.nil? and last_name.nil?)
errors.add(:base, "Specify a first or a last.")
end
end
def male_Sue
if (first_name == "Sue" and gender == "male")
errors.add(:base, "we are prevent male by name Sue.")
end
end
end
TodoList Model:
class TodoList < ActiveRecord::Base
belongs_to :user
has_many :todo_items, dependent: :destroy
default_scope { order :list_due_date }
end
TodoItem Model:
class TodoItem < ActiveRecord::Base
belongs_to :todo_list
default_scope {order :due_date }
end
Thanks, Michael.
I guess adding dependent: :destroy will do.
#user.rb
class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
has_many :todo_lists, dependent: :destroy
has_many :todo_items, through: :todo_lists, source: :todo_items, dependent: :destroy
validates :username, presence: true
end
From the docs:
has_many, has_one and belongs_to associations support the :dependent option. This allows you to specify that associated records should be deleted when the owner is deleted
By using dependent: :destroy on your association in the User class, anytime you destroy a User, all associated objects to that instance gets destroyed as well.
You can check this documentation for more information.

Rails 4 - Create record with has_many through

In my project i have something like this:
class User < ActiveRecord::Base
has_many :roles
has_many :websites, through: :roles
end
class Website < ActiveRecord::Base
validates :name, presence: true
has_many :roles
has_many :users, through: :roles
end
class Role < ActiveRecord::Base
validates :name, presence: true
belongs_to :user
belongs_to :website
end
So when I try to do:
User.first.websites.create(name: "First")
I have this error
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
How can i create a new User Website in one line?
The validation error is actually coming from the Role model, which also has validation for the name attribute.
You can do it in one line by creating the website through the role, using accepts_nested_attributes_for:
class Role < ActiveRecord::Base
validates :name, presence: true
belongs_to :user
belongs_to :website
accepts_nested_attributes_for :website
end
User.first.roles.create(name: "Role name", website_attributes: { name: "Website name" })
I think if you remove validates :name, presence: true from role model then it will work.

How to scope validation to specific model in polymorphic association.?

I have two model User and Investment and one polymorhic model Address
class User < ActiveRecord::Base
has_one :address, as: :addressable, dependent: :destroy
accepts_nested_attributes_for :address
end
class Investment < ActiveRecord::Base
has_many :addresses, as: :addressable, dependent: :destroy
accepts_nested_attributes_for :addresses, reject_if: lambda { |v| v['address'].blank? } && :address_blank, :allow_destroy => true
end
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
validates :address, presence: true
end
now validates :address, presence: true will applicable to both Investment as well as User
but i want it to applicable to only Investment not to User. so how do i do that.
Thanks.
in class Investment add
validates :address_id, presence: true
and remove bellow from class Address
validates :address, presence: true
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
validates :address, presence: true, if: :investment?
protected
def investment?
addressable_type == 'Investment'
end
end

Rails has_many :through validation

I'm having trouble validating a model from a has_many through association. Below are the relevant models:
Broadcast Model
class Broadcast < ActiveRecord::Base
attr_accessible :content,
:expires,
:user_ids,
:user_id
has_many :users, through: :broadcast_receipts
has_many :broadcast_receipts, dependent: :destroy
validates :user_id, presence: true
validates :content, presence: true
end
Broadcast Receipt Model
class BroadcastReceipt < ActiveRecord::Base
belongs_to :broadcast
belongs_to :user
attr_accessible :user_id, :cleared, :broadcast_id
validates :user_id , presence: true
validates :broadcast_id , presence: true
end
There is also an association with Users that have_many broadcasts receipts through broadcast receipts.
The problem appears to be with the following line:
validates :broadcast_id , presence: true
Whenever I try to create a Broadcast, I get a rollback with no error messages given. However, when removing the above line, everything works as expected.
This looks like a problem with the Broadcast not being saved before the Broadcast Receipts are being created.
Is there any way I'd be able to validate the broadcast_id is set on the receipt model?
This appears to be the same issue discussed here: https://github.com/rails/rails/issues/8828, which was solved by adding :inverse of to the has_many associations to the join model.
There might be some problem in your code structuring. You could give this version a try.
class Broadcast < ActiveRecord::Base
# I assume these are the recipients
has_many :broadcast_receipts, dependent: :destroy
has_many :users, through: :broadcast_receipts
# I assume this is the creator
validates :user_id, :content, presence: true
attr_accessible :content, :expires, :user_id, :user_ids
end
class BroadcastReceipt < ActiveRecord::Base
belongs_to :broadcast
belongs_to :user
# You should be able to validate the presence
# of an associated model directly
validates :user, :broadcast, presence: true
attr_accessible :cleared
end

Resources