Rails 4 - Saving/deleting with one-to-one association - ruby-on-rails

I'm building an application where I have one-to-one relationship, Reservation 'has_one' Order. Now my question is, how can I delete Reservation from the DB if a user decides to not completing the form for Order? I've tried looking up ways to do this but biggest issue is not knowing what to search for.
Currently my create action saves the Reservation once the form is submitted and then redirects to the form for Order payment to be completed.

Instead of worrying about deleting a Reservation if an order isn't complete, why not just prevent creation of the Reservation in the first place?
class Reservation
has_one :order
accepts_nested_attributes_for :order
validates_presence_of :order
Consider combining the creation of both objects into a single page, or passing params or objects between steps of your controller.

You should create both of them at one time
I think your best option is to use before_save in Order model to create a Reservation
Also check for accepts_neseted_attributes so both of them can be created with each other

Related

How to save related Models in one transaction?

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

How to intercept accepts_nested_attributes_for?

I have a Rails application, with two models: SalesTransactions and PurchaseOrders.
In the PurchaseOrders model, new entries are registered using 'purchase_order_number' as the key field. I use the create method of the model to search if that 'purchase_order_number' has been previously registered, and if so, reuse that record and use its id in the SalesTransaction record. If that name wasn't already registered, I go ahead and perform the create, and then use the new PurchaseOrder record id in the SalesTransaction (the foreign_id linking to the associated PO).
Note that I don't have the existing PurchaseOrder record id until I've done a look-up in the create method (so this is not a question of 'how do I update a record using 'accepts_nested_attributes_for'?', I can do that once I have the id).
In some situations, my application records a new SalesTransaction, and creates a new PurchaseOrder at the same time. It uses accepts_nested_attributes_for to create the PurchaseOrder record.
The problem appears to be that when using 'accepts_nested_attributes_for', create is not called and so my model does not have the opportunity to intercept the create, and look-up if the 'purchase_order_number' has already been registered and handle that case.
I'd appreciate suggestions as to how to intercept 'accepts_nested_attributes_for' creations to allow some pre-processing (i.e. look up if the PurchaseOrder record with that number already exists, and if so, use it).
Not all Sales have a PurchaseOrder, so the PurchaseOrder record is optional within a SalesTransaction.
(I've seen a kludge involving :reject_if, but that does not allow me to add the existing record id as the foreign_id within the parent record.)
Thanks.
You could use validate and save callbacks to do what you need.
Assuming the setup:
class SalesTransaction < ActiveRecord::Base
belongs_to :purchase_order, :foreign_key => "po_purchase_order_no",
:primary_key => "purchase_order_no"
accepts_nested_attributes_for :purchase_order
end
class PurchaseOrder < ActiveRecord::Base
has_many :sales_transactions, :foreign_key => "po_purchase_order_no",
:primary_key => "purchase_order_no"
before_validation :check_for_exisitng_po # maybe only on create?
accepts_nested_attributes_for :sales_transactions
private
def check_for_exisitng_po
existing_po = PurchaseOrder.find_by_purchase_order_no(self.purchase_order_no)
if existing_po
self.id = existing_po.id
self.reload # don't like this, also will overwrite incoming attrs
#new_record = false # tell AR this is not a new record
end
true
end
end
This should give back full use of accepts_nested_attributes_for again.
gist w/tests
Two ideas: Have you taken a look at association callbacks? Perhaps you can "intercept" accepts_nested_attributes_for at this level, using :before_add to check if it is already in the DB before creating a new record.
The other idea is to post-process instead. In an after_save/update you could look up all of the records with the name (that ought to be unique), and if there's more than one then merge them.
I was going to write a before_save function, but you say this:
It uses accepts_nested_attributes_for to create the PurchaseOrder record.
So in the SalesTransaction process flow, why look it up at all? You should just get the next one available... there shouldn't be a reason to search for something that didn't exist until NOW.
OK, I've left this question out there for a while, and offered a bounty, but I've not got the answer I'm looking for (though I certainly appreciate folk trying to help).
I'm concluding that I wasn't missing some trick and, at the time of writing, there isn't a neat solution, only work-arounds.
As such, I'm going to rewrite my App to avoid using accept_nested_attributes_for, and post the SalesTransaction and the PurchaseOrder records separately, so the create code can be applied in both cases.
A shame, as accept_nested... is pretty cool otherwise, but it's not complete enough in this case.
I still love Rails ;-)

Ruby on Rails: Is there a way to temporarily disable polymorphic associations?

I've got an association between models that is polymorphic.
Example:
class Review
belongs_to :review_subject, :polymorphic => true
end
However, I would like to make a call on this :review_subject association where I know all the results will be of a certain type.
In my case, I want to do this so I can join in the review_subject and then impose a condition upon it. Doing so on a polymorphic relation normally causes this to raise an EagerLoadPolymorphicError. The logic behind this is that it's not able to know what model to load in order to perform the join, but that does not apply in my case because I already know only one model will be involved in this query.
Example, where I know that all relevant reviews will belong_to a Book, and I want to only show reviews where the books have authors:
Review.joins(:review_subject)
.where(review_subject_type => "Book")
.where("reviewed.book_author IS NOT NULL")
Is there a way to temporarily disable the polymorphic relationship?
The best solution I've come up with is to add a second associationin the Review model, belongs_to :review_subject_books_only that is not polymorphic, and can be called on only in this situation. However, this is an ugly hack both in the model, and in that it also messes up include calls unless the views also refer to a Review's review_subject_books_only.
Do the query the other way around:
Book.joins(:reviews).where('book_author is not null')

Rails: Relationship between two loosely related models

I am working on a Ruby on Rails 3 web application and am not sure how to relate two of the models.
In our organization sales reps go out on appointments. If the appointment is successful, it will result in creating an order (which then has the items ordered related to it, but that's for another day.) If this appointment is not successful, it will be marked as no sale and as you might have guessed, no order is created.
On the other hand, sometimes sales happen without an appointment. For example, a customer may call into the store and order something. In this case, an order can exist without an appointment.
It would be simple if there were no relationship between orders and appointments, but there has to be for ease of use for the end user. For example, if an appointment generates an order, but later the buyer cancels, they will mark the appointment as sale cancelled and then the system should automatically set the order as cancelled. Likewise,they may choose to cancel the order, then the appointment would have to be cancelled automatically by the system.
How does a developer handle something like this? Does the appointment :have_many => orders? does the order :belong_to => appointments? I don't know what to do!
Please help me with this, I am a pretty new rails developer and I feel in over my head! Thank you!
As you already said, the following will work fine:
class Appointment < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :appointment
end
belongs_to requires the field appointment_id to be present in the orders table. But, if the order is not associated with an order then appointment_id does not need to be set. You can have multiple belongs_to associations for a given class.

How do I implement this code without needing to access current_user.id from a model in RoR?

I have a note model, with the following association
note.rb
has_many :note_categories, :dependent => :destroy
has_many :categories, :through => :note_categories
The NoteCategory model was created to function as a join table between notes and categories.
I need to implement the following:
A user removes a category from a note. This can be done by either removing one category from a note (deletes one entry in the note_categories table), or by deleting the note entirely (deletes all entries in the note_categories table relating to the note)
Before the row/s in note_categories is/are deleted, I need to determine if the user who is deleting the category from a note is the same user who initially created the category (creator field in the category model)
If it is the same user, the category entry itself is to be deleted
Obviously to do this, I need to access the id of the user, to check against the creator field of the Category. I am already using a before_destroy method in the NoteCategory model to do some other things, but I can't access current_user.id in there because it's a model, and current_user is a method in the Application Controller. From the questions I've read here on SO, it seems that accessing the id of the current user from a model is bad form.
I don't think I can use the controller in this circumstance because when a note is deleted, the :dependent => :destroy line means that the associated rows in note_categories are deleted as well. I need to do the creator check in this situation as well, but the note_categories rows are removed via the destroy method in the model, not the controller, which is the behavior specified by :dependent => :destroy.
So how should I go about doing it? Thanks for reading!
One way of doing this could be to add an attr_accessor to the Note model like so:
# in Note.rb
attr_accessor :destroyed_by
and set it before destroying the record:
# NotesController#destroy
#note.destroyed_by = current_user.id
#note.destroy
Then in your Note.rb before_destroy call, you can check the Category's creator id against the destroyed_by id.
You say
Before the row/s in note_categories is/are deleted, I need to determine if the user who is deleting the category from a note is the same user who initially created the category (creator field in the category model)
If it is the same user, the category entry itself is to be deleted
Are you sure you wan't to do such a thing? What if a user has used the same category for many notes, and he wants to delete it form one of them? You sure would want to delete an entry from note_categories, but should you also delete the category itself?
A common implementation for such a scenario is to check while deleting a note_category (through perhaps a before_destroy) whether this one is the last note_categories for the category, and delete it if it is. This also means that if a note is deleted, only the related note_categories should be deleted, and not the categories themselves.
I think that you want to maintain a log of who created the post and who is deleting it. The before_destroy method does the part of deleting associations and I think it is working fine for you. as of maintaining record of whether the user who created the note is deleting the note or not comes under logging part. I hope this helps you around this problem
http://rohitsharma9889.wordpress.com/2010/08/19/logging-in-ruby-on-rails/
EDIT:
You can also try reading this article. I would recommend you to prefer this one on the above one
Environment variables in Model

Resources