I couldn't come up with a better title, but for my app for landlords I faced a problem where I don't want to remove old tenants. I want to store them in a database for future reference. So that leaves me with problem of making sure that only one tenant can have "active" status at any given time.
Now, I tried to find a way of forcing that on a database level, but then I thought about rails validations. I can check that on adding or modifying my tenants. And since my app will never be big enough for multiple users to update the same record in DB concurrently, I decided to implement that directly in the app.
Let's assume our Flat has_many :tenants
and Tenant belongs_to :flat
I will take advantage of rails model callback order. And first four go as below:
So now I can use this order to force validation of my record to see if any other tenants are active. If there is another tenant that is active, I won't allow the record to save. Sounds simple to me.
validate :force_one_active | |
def force_one_active | |
flat = Flat.find_by(id: flat_id) | |
determine_active | |
if self.active? | |
other_tenants = Tenant.where(flat_id: flat_id, active: true).where("rent_to >= ? ", Date.today) | |
end | |
if other_tenants.present? && flat.taken_until > Date.today | |
errors.add(:flat_id, I18n.t('forms.too_many_active')) | |
end | |
end | |
def determine_active | |
if rent_from <= Date.today && rent_to >= Date.today | |
self.active = true | |
else | |
self.active = false | |
end | |
end |
Few things happened above. I use my method determine_active
in my validation. I do that because in my force_one_active
function, it first checks if the tenant should be active at all, then performs a search to check if there are any additional tenants with the same status, if it happens to find that the flat is occupied, it stops the record from saving on a validation level, thus leaving active = false
.
I've also added another function to set flat's taken
value if our current tenant happened to be active.
before_save :occupy_flat | |
def occupy_flat | |
flat = Flat.find_by(id: flat_id) | |
if self.active? | |
flat.taken = true | |
flat.taken_until = self.rent_to | |
flat.save! | |
else | |
flat.taken = false | |
flat.taken_until = self.rent_to | |
flat.save! | |
end | |
end |
Now, because this method is in before_save
callback it will never run unless validation has passed successfully, as per order shown previously.
It's not perfect, but it works for a smaller scale app like mine.
Top comments (0)