I have a rails app with the following models:
class Product < ActiveRecord::Base
has_many :stores, through: :product_store
attr_accessible :name, :global_uuid
end
class ProductStore < ActiveRecord::Base
attr_accessible :deleted, :product_id, :store_id, :global_uuid
belongs_to :product
belongs_to :store
end
Since this model is for a REST API of a mobile app, I create the objets remotely, on the devices, and then sync with this model. As this happens, it may happen that I have to create a ProductStore before having an id set for Product. I know I could batch the API requests and find some workaround, but I've settled to have a global_uuid attribute that gets created in the mobile app and synced.
What I'd like to know is how can I make this code in my controller:
def create
#product_store = ProductStore.new(params[:product_store])
...
end
be aware that it will be receiving a product_global_uuid parameter instead of a product_id parameter and have it properly populate the model.
I figure I can override ProductStore#new but I'm not sure if there's any ramification when doing that.
Overriding .new is a dangerous business, you don't want to get involved in doing that. I would just go with:
class ProductStore < ActiveRecord::Base
attr_accessible :product_global_uuid
attr_accessor :product_global_uuid
belongs_to :product
before_validation :attach_product_using_global_uuid, on: :create
private
def attach_product_using_global_uuid
self.product = Product.find_by_global_uuid! #product_global_uuid
end
end
Having these kinds of artificial attr_accessors that are only used in model creation is kind of messy, and you want to avoid passing in anything that isn't a direct attribute of the model you are creating where possible. But as you say there are various considerations to balance, and it's not the worst thing in the world.
Related
As you can see in the schema below, a user can create courses and submit a time and a video (like youtube) for it, via course_completion.
What I would like to do is to limit to 1 a course completion for a user, a given course and based one the attribute "pov" (point of view)
For instance for the course "high altitude race" a user can only have one course_completion with pov=true and/or one with pov=false
That mean when creating course completion I have to check if it already exist or not, and when updating I have to check it also and destroy the previous record (or update it).
I don't know if I'm clear enough on what I want to do, it may be because I have no idea how to do it properly with rails 4 (unless using tons of lines of codes avec useless checks).
I was thinking of putting everything in only one course_completion (normal_time, pov_time, normal_video, pov_video) but I don't really like the idea :/
Can someone help me on this ?
Thanks for any help !
Here are my classes:
class CourseCompletion < ActiveRecord::Base
belongs_to :course
belongs_to :user
belongs_to :video_info
# attribute pov
# attribute time
end
class Course < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :courses
has_many :course_completions
end
You could use validates uniqueness with scoping Rails - Validations .
class CourseCompletion < ActiveRecord::Base
belongs_to :course
belongs_to :user
belongs_to :video_info
validates :course, uniqueness: { scope: :pov, message: "only one course per pov" }
# attribute pov
# attribute time
end
invoice_serializer.rb
class InvoiceSerializer < ActiveModel::Serializer
attributes :id, :document_no, :customer_id, :currency_id, :date,
:due_date, :notes, :invoice_status_id, :total, :tax_total, :grand_total
# This is not working, associated objects are still rendered
unless #object.respond_to? :count
has_many :invoice_lines
end
has_many :invoice_payments
has_one :customer
has_one :invoice_status
end
This is my serializer class. I have two 'has_many' associations and I don't want them in a collection. I only want them in singular form. Just to be more clear, I don't want has_many association in invoices#index, it affects performance in a bad way but I want them in invoices#show, invoices#edit actions.
How do I achieve that? How can I associate models conditionally?
My gut reaction would be to create another serializer and extend this one, adding has_many :invoice_payments to it.
Then simply use the extended controller when you want the associations, and the original one when you don't.
def InvoiceIndexSerializer < InvoiceSerializer
has_many :invoice_payments
end
(NB: untested)
This way your serializer stays ignorant of an outside state like which action you're using.
I have three models and they look like (simplified):
class Airline < ActiveRecord::Base
attr_accessible :name
has_many :airplanes
has_many :airplane_switches
end
class Airplane < ActiveRecord::Base
attr_accessible :airline_id, :register
belongs_to :airline
has_many :airplane_switches
end
class AirplaneSwitch < ActiveRecord::Base
attr_accessible :airline_id, :airplane_id
belongs_to :airplane
belongs_to :airline
end
Airplanes could have been in some Airlines, so I needed another model that indicates if an Airplane was in one or more Airlines.
I am building a form to let users upload some info about an Airplane, they just select the airplane register (callsign) and then they will get a list to choose in which Airline it was.
This will work over an AJAX request. But, I am trying to figure out how to show the Airline name from my controller, to avoid another AJAX call by fetching another JSON file just to get the name of the Airline based on the airline_id in AirplaneSwitch.
#airplane = Airplane.find_by_register(params[:register])
#airplane_switches = #airplane.airplane_switches # Here I need to join also each Airline.name
I think this way would be more efficient, but I have no idea if it's possible to do.
This should work:
#airplane.airplane_switches.select('*, airlines.name as airline_name').joins(:airline)
Let's say you have variable airplane_switch that contains AirlineSwitch instance fetched in that way. All you need to do to get your airline name is:
airplane_switch.airline_name
I have two models
class Department < ActiveRecord::Base
has_many :checklists
attr_accessible :deadline
after_update :update_checklist
class Checklist < ActiveRecord::Base
belongs_to :department
attr_accessible :content, :category
Basically, the 'department' model has a virtual attribute called 'deadline', and it is in type of date. The actual value of 'deadline' is stored in another model 'checklist', in format of string.
Every time when 'deadline' is updated, I would like to check if there is an entry in 'checklist', and create (if not yet) or update (if already has an entry).
I was thinking this way
def deadline=(deadline)
#cl = Checklist.find_or_create_by_department_id_and_category(self.id, 'deadline')
#cl.update_attributes(:content => deadline.to_s)
#cl.save
end
def deadline
#deadline = self.checklists.find_by_category('deadline')
Date.parse(#deadline.to_s)
end
But the above virtual attribute is not working.
When searching for the answer, I found on rails cast that callback will be a better solution for this kind of situation. So I am trying to something like:
class Department < ActiveRecord::Base
after_update :update_checklist
def update_checklist
#cl = Checklist.find_or_create_by_department_id_and_category(self.id, 'deadline')
#cl.update_attributes(:content => ???)
end
I am not sure how to pass the value to the callback.
Please help me with this design. what is the standard way to handle this? Thank you in advance!
update_checklist is a method of Department. So within update_checklist, you can access any Department attributes, just like self.id, self.deadline is what you want.
Say I have a three models that look (basically) like this:
class User < ActiveRecord::Base
has_many :projects
has_many :deliverables
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :deliverables
end
class Deliverable < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
Now, say I want the following to happen: when a project is transferred from one user to another, all associated deliverables will be transferred along with it. So something like:
project = Project.find(some_criteria)
deliverables = project.deliverables
project.user_id = new_user_id
deliverables.each do |d|
d.user_id = new_user_id
end
Is there some way to automate what I just described? I could always of course just put that into a method (like transfer_user), but I would prefer for it to happen automatically whenever user_id is set to a new value.
You probably still want to implement the transfer_user method, but add a callback in your project model by doing:
class Project < ActiveRecord::Base
after_save :transfer_user, :if => "user_id_changed?"
end