I am trying to write a scope which will get data from two models, haven't done this before, so looking to see if anyone can help me with the theory and the practical.
My two models are defined like so
class Member < ActiveRecord::Base
belongs_to :membership
accepts_nested_attributes_for :membership
attr_accessible :membership_id, :forename, :middlename, :surname, :house_no, :house_name, :street, :town, :postcode, :home_tel, :mobile_tel, :work_tel, :email, :start_date, :expiry_date
end
class Membership < ActiveRecord::Base
has_many :members, :dependent => :destroy
attr_accessible :membership_type, :cost
end
I would like to get all the members who have joined today collating the total amount in £ it has generated
So can i perform a sum in a scope or would i have to write a method that gets the total and then use that method within the scope?
class Member < ActiveRecord::Base
scope :new_memberships_cash_today, ->() {
where(:start_date => Date.today)
}
end
That's as far as i have got so far, not correct I know .Really not sure how to construct the rest, any help appreciated
I presume something like that will suffice:
joins(:memberships).where(:start_date => Date.today).sum('memberships.cost')
Related
In my Rails 5 app I have the following setup:
class Client < ApplicationRecord
has_one :address, :as => :addressable, :dependent => :destroy
accepts_nested_attributes_for :address, :allow_destroy => true
end
class Company < Client
has_many :people
end
class Person < Client
belongs_to :company
end
class Address < ApplicationRecord
belongs_to :addressable, :polymorphic => true
validates :city, :presence => true
validates :postal_code, :presence => true
end
A person can belong to a company but doesn't necessarily have to.
Now I want to validate a person's address only if that person doesn't belong to a company. How can this be done?
There might be other approaches as well, but based on my experience, something like this should work.
validates :address, :presence => true, if: -> {!company}
Hope this helps.
Validations can take either an if or unless argument, which accept a method, proc or string to determine whether or not to run the validation.
In your case:
validates :address, presence: true, unless: :company
Update according to comments
The above only takes care of skipping the validation itself, but due to accepts_nested_attributes_for OP still saw errors when trying to persist a missing address. This solved it:
accepts_nested_attributes_for :address, reject_if: :company_id
Nabin's answer is good but wanted to show another way.
validate :address_is_present_if_no_company
def address_is_present_if_no_company
return if !company_id || address
errors.add(:address, "is blank")
end
I'm trying to understand how to implement one-to-many relationship through reference table. I'm looking on this guide I though just write on one model has_many so it will be one-to-many but I'm not completely sure (I wrote something but it's not working). Anyway I'm doing this to save for me a table, and doing it right and not just working.
The model is as following:
Microposts, :id, :content
Tag, :id, :name
Tag_microposts, :tag_id, :micropost_id
Article, :id, :text
Article_microposts, :article_id, :micropost_id
I can do two microposts tables with the id of the tag/article. But I think doing it like this is better and righter.
In the end what's interesting me is to get microposts through tag model. So in the tag_controller be able to do:
def index
#tag = Tag.find(params[:id])
#microposts = #tag.microposts
end
Some code:
class Tag < ActiveRecord::Base
...
has_many :tag_microposts, foreign_key: :tag_id, dependent: :destroy
has_many :microposts, through: :tag_microposts, source: :micropost
...
end
class TagMicropost < ActiveRecord::Base
validates :tag_id, presence: true
validates :micropost_id, presence: true
end
class Micropost < ActiveRecord::Base
belongs_to :user
belongs_to :tag
validates :content, presence: true, length: {minimum: 10, maximum: 250}
validates :user_id, presence: true
end
May I ask why you are using a reference table for this? You can do a one-to-many association with only the two models you are associating. If you want to associate a tag with many posts you can just do this in your models.
class Tag < ActiveRecord::Base
has_many :microposts
end
class Micropost < ActiveRecord::Base
belongs_to :tag
#added in edit
belongs_to :article
validates :content, presence: true, length: {minimum: 10, maximum: 250}
validates :user_id, presence: true
end
This should let you do:
#tag.microposts
Just fine. The forgien key will be stored in your Micro post model so be sure to add the column. You can use an active migration for that. Call the column tag_id, rails should take care of the rest.
Edit*
A added the article association. The problem you raised is only relevant if you need to get the article/tag given the micropost. The code to do that is still pretty simple with this model.
#tag ||= #micropost.tag
Using the conditional assignment operator like this will only assign #tag if the association is there. If you give me more specifics about how these models will be used I can give you a better answer.
I have a has many through relationship in my app:
Shows has many Bands through => Lineups
Bands are unique by :name
class Show < ActiveRecord::Base
attr_accessible :city_id, :title, :dateonly, :timeonly, :image, :canceled, :venue_attributes, :bands_attributes
belongs_to :city
belongs_to :venue
has_many :lineups
has_many :bands, through: :lineups
has_and_belongs_to_many :users
end
class Lineup < ActiveRecord::Base
belongs_to :show
belongs_to :band
end
class Band < ActiveRecord::Base
attr_accessible :name, :website, :country, :state
has_many :lineups
has_many :shows, through: :lineups
validates :name, presence: true
validates_uniqueness_of :name
before_save :titleize_name
private
def titleize_name
self.name = self.name.titleize
end
end
New Bands are created like this:
(lets say we have a show record already saved called s1)
> s1.bands.new(name: "Wet Food")
> s1.save
Right now this will only save if a band named "Wet Food" doesn't already exist
In which model is the best place to do a Band.find_or_create in this relationship so that an existing band can be used if one with the same name exists?
This is generally the type of call that would go in a Controller (or maybe a service object), but not in a Model. It really depends on the particular user flow that you're trying to accomplish in your app. Basically, where ever you are already using s1.bands.new, you could use this instead :
s1.bands.where(name: 'Wet Food').first_or_create
I'm trying to skip validations of a belongs_to association if a criteria is true. Below is my code
class Venue < ActiveRecord::Base
has_many :events
validates_presence_of :name, :postcode, :category
end
class Event < ActiveRecord::Base
belongs_to :venue
accepts_nested_attributes_for :venue
end
So what I need to do is skip the Venues validates_presence_of validation if a criteria from the Events model is true. So lets say if the event_type was equal to '1' then it will ignore the Venues validates_presence_of call but if event_type was '2' then it will still execute the validates_presence_of call.
There's a Railscast on this topic. You can also check out Rails Conditional Validation
based on the link above, you'll have to pass a lambda such as:
:if => lambda { |venue| venue.event.try(:event_type) == 2 }
In the end I did something very similar to this
I'm trying to keep track of how many signup_conversions a user creates.
Therefore, I have those two following models:
signup_conversion.rb
class SignupConversion < ActiveRecord::Base
belongs_to :user
belongs_to :convertee, :class_name => "User", :foreign_key => 'convertee_id'
attr_accessible :convertee_id
end
user.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
belongs_to :signup_conversion
has_many :signup_conversions
end
Would this work that way? Or am I missing something crucial here?
I haven't tried the code, but I give you some tips that hope you find useful.
I think every has_many/has_one statement should have its correspondent belongs_to, so three belongs_to and one has_many doesn't look good.
I'm not sure has_one :signup_conversion and has_many :signup_conversions would play well together, so I'd rather change the names. I changed the other names as well to try to make the associations clearer although I'm not sure I fully understand the real concepts they represent. You will probably come up with better names.
By default, the foreign key is guessed adding the suffix _id to the association name, so you don't need to specify it in this case. Also, I don't think you need to make that attribute accessible, at least not for the association to work.
signup_conversion.rb
class SignupConversion < ActiveRecord::Base
belongs_to :owner , :class_name => "User"
belongs_to :convertee, :class_name => "User"
end
user.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_one :owned_signup_conversion , :class_name => "SignupConversion"
has_many :triggered_signup_conversions, :class_name => "SignupConversion"
end
You have more code than you need here; you only need
class SignupConversion < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_many :signup_conversions
end
Then you need a user_id column in your signup_conversions table, and you call
#user.signup_conversions
in your views or controller.