In my app I have a number of issues which fall under the general pattern of:
Model with association
Association relies on some property of model and therefore caches it
Model changes
Association breaks because cached property is out of date
Is there a specific rails gem or pattern that would allow you to perform something like this on the model:
on_change :currency, notify: :booking
Then on the association:
on_notification :currency, :update_currency
Or something along those lines?
I can see that you could do it easily enough with observers or callbacks but would like to DRY this up
Related
I would like to know if there is a way in rails to validate the presence (AND existence in the database) of an association without using any additional gem if possible (i.e the associated "belongs_to" object is in the database and valid before saving).
validates_presence_of does not work because you can use a newly created and unsaved object.
I know all about the validates_existence gem but I would like to avoid it if possible.
You can use validates_associated in conjunction with validates_presence_of.
validates_associated will run validations on the associated model (which you have to define in that model).
validates_presence_of validates presence of the association.
EDIT
Addressing your comment: not a common scenario to validate presence in db specifically, but you can do this:
validate :association_exists
def association_exists
# query database for the association record and return true if it exists
# self is model instance inside this method
end
In general, I have a website which needs to have complex registration process. And in that registration process I need to include 4 tables from database.
Now... I cannot validate one by one model and to enter 4 of them in database. Is there a way to make common points of all those tables in one model?
Let's say:
User model has columns: username, name, etc.
Package model has: type, account_number
etc
And in registration process I need to include username, name, account_number and to validate them. How to do that?
Without seeing your model structure, this is just speculation, but here goes:
--
Virtual Attributes
In your User model, you can use attr_accessor to create a set of virtual attributes - which basically mean you can create a series of setter / getter methods in your User model.
Although I don't think this will help you directly, it should give you an idea as to how you can create single-model validation:
#app/models/user.rb
Class User < ActiveRecord::Base
attr_accessor :new, :values, :to, :validate
validates, :new, :values, :to, :validate, presence: true
end
This will allow you to create the attributes in the User model - and although they won't be saved, you can then use them to validate against
attr_accessor
To give you a quick overview of this method, you first need to remember that Rails is just a collection of modules and classes.
This means that every model you load is just a class which has been populated with a series of getter / setter methods. These methods are defined by ActiveRecord from your data table columns, and are why you can call #user.attribute
The magic is that if you use attr_accessor, you'll basically create your own attributes in your User model - which won't be saved in the database, but will be treated like the other attributes your objects have, allowing you to validate them
Because your registration process seems to be complex, I would go even futher as virtual attributes and use Form Objects
7 Patterns to Refactor Fat ActiveRecord Models
LA Ruby Conference 2013 Refactoring Fat Models
ActiveModel Form Objects
I understand that you multistep registration. You shouldn't create 4 models only because your view pages needs it. You should:
remove validation from User model and add validation on each form
create 4 different forms (for example extends by ActiveModel or user gem reform)
add validation to each form
after form.valid? save part of user info to #user object
Thats all.
I have two models:
class Customer < ActiveRecord::Base
has_many :contacts
end
class Contact < ActiveRecord::Base
belongs_to :customer
validates :customer, presence: true
end
Then, in my controller, I would expect to be able to create both in
"one" sweep:
#customer = Customer.new
#customer.contacts.build
#customer.save
This, fails (unfortunately translations are on, It translates to
something like: Contact: customer cannot be blank.)
#customer.errors.messages #=> :contacts=>["translation missing: en.activerecord.errors.models.customer.attributes.contacts.invalid"]}
When inspecting the models, indeed, #customer.contacts.first.customer
is nil. Which, somehow, makes sense, since the #customer has not
been saved, and thus has no id.
How can I build such associated models, then save/create them, so that:
No models are persisted if one is invalid,
the errors can be read out in one list, rather then combining the
error-messages from all the models,
and keep my code concise?
From rails api doc
If you are going to modify the association (rather than just read from it), then it is a good idea to set the :inverse_of option on the source association on the join model. This allows associated records to be built which will automatically create the appropriate join model records when they are saved. (See the ‘Association Join Models’ section above.)
So simply add :inverse_of to relationship declaration (has_many, belongs_to etc) will make active_record save models in the right order.
The first thing that came to my mind - just get rid of that validation.
Second thing that came to mind - save the customer first and them build the contact.
Third thing: use :inverse_of when you declare the relationship. Might help as well.
You can save newly created related models in a single database transaction but not with a single call to save method. Some ORMs (e.g. LINQToSQL and Entity Framework) can do it but ActiveRecord can't. Just use ActiveRecord::Base.transaction method to make sure that either both models are saved or none of them. More about ActiveRecord and transactions here http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
I have a Change model that utilizes single table inheritance that has the following attributes:
id
type #this is a single table inheritance type field.
description
dynamic_id
I also have two sub classes, Race which is a subclasses of Change and Workout which is a sub class of Race.
class Race < Change
end
class Workout < Race
end
I have a fourth class called Track and I'd like to create the following four associations by just using the dynamic_id field in the Change object. (i.e. I have not explicitly added race_id and workout_id to the Change table. Instead I want to use the dynamic_id as the race_id for the Race class and the dynamic_id as the workout_id for the Workout class) By doing this, I will avoid having a lot of nil fields in my database.)
Here are the four associations I'm trying to create.
Race Model - belongs_to :track
Workout Model - belongs_to :track
Track Model - has_many :races
Track Model - has_many :workouts
I've been trying to accomplish this with associations using :class_name and :foreign_key, but I can't seem to get it working. Is this actually possible. I realize its probably not a best practice, but I'd still like to see if it doable. Thanks for your input.
What you are looking for are "polymorphic associations". You can find more in the rails guides: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Your case is a bit special because you want to use polymorphic associations with STI. I remember that there was a bug with this combination but it could be fixed by now.
I did not read it completely but this blog post seems to describe the situation: http://www.archonsystems.com/devblog/2011/12/20/rails-single-table-inheritance-with-polymorphic-association/
The problem I encountered with polymorphic associations and STI is described here: Why polymorphic association doesn't work for STI if type column of the polymorphic association doesn't point to the base model of STI?
When I create a scaffold and I need to have a belongs_to relation to another model, I add a field called model_id (replacing model with that model's name):
rails generate scaffold Grade user_id:integer subject_id:integer letter:string
Then in the above Grade model, I might add:
belongs_to :user
belongs_to :subject
Rails automatically adds user_id and subject_id to the list of attr_accessible fields. Do I do any harm by also adding :user and :subject to the list of attr_accessible fields so that I can mass assign using those as well?
attr_accessible is intended to protect against mass-assignment attacks that come from data that is externally sent to your application. In most cases you're probably doing things like this in your create & update actions:
#model = Model.new(params[:model])
or
#model.update_attributes(params[:model])
You should ask yourself why you'd have one form that uses subject_id and another that uses subject. The only real harm here is inconsistency, which can actually be pretty detrimental to large projects. If you follow the convention that all forms will use the actual database column (subject_id), then you'll save yourself some headache in the future when you can't remember out why you did it two different ways.
If you're updating attributes through the console, you can either use update_attributes(params[:model], without_protection: true) or a gem I wrote called sudo_attributes which lets you do sudo_update_attributes(params[:model]).
I think it doesn't hurt you, but will bring a kind of mess in your code