I have a simple Rails 3 application and I am trying to create a new record that belongs to it's owner. It's working by passing the id to a hidden field in the new form of the child record.
This works well and once the new child form submitted it correctly gets associated in the child/parent relationship.
What I am trying to do, is lookup values form the parent within the new child form. The problem is that the child relationship is not yet created. Is there anyway I can use a .where lookup in the view? Or, is there a better way of doing this?
At the moment I am passing the animal_id though to the new Claim form and it's inserted into a hidden field labelled animal_id.
What I am trying to do:
<%= #animal.where(:animal_id => params[:animal_id]).id %>
The above would ideally get the animal ID from the soon-to-be-associated animal. Is there any sort of before_filter or anything that could take the passed params from the URL and temporarily create the relationship just for the new form view and then permanently create the relationship once the form is submitted?
I've tried adding the following to my Claims controller and then called #animal.AnimalName in the view but I get NoMethodError:
before_filter :find_animal
protected
def find_animal
if params[:animal_id]
Animal.find(params[:animal_id])
end
end
The URL of the new claim is correctly showing the animal ID so I'm not sure why it's not finding it:
http://localhost:3000/claims/new?animal_id=1
The model relations are as follows:
animal has_many claims
animal has_one exclusion
claim has_one animal
exclusion has_one animal
Update
I've corrected my relationships to as follows:
animal has_one exclusion
exclusions belong_to animal
First, you should also update
claim has_one animal
to be
claim belongs_to: animal
, since you already have
animal has_many claims
From this, it seems like the parent is the Animal, and the Claim is the child, thus the child claim form is trying to somehow access the parent animal's id.
If I correctly understood your situation up to this point, why not just access the animal_id value you passed to the form? You've already passed that value to the new method in the claims_controller, right?
So when you press the submit button, and it passes all the values of the form, including the animal_id value, to the create method of the claims_controller,
claims_controller.rb
def create
#claim = claim.new(claim_params)
if #claim.save
redirect_to claim_path(#claim)
else
render 'new'
end
end
private
def claim_params
params.require(:claim).permit(:foo, :bar. :animal_id)
end
Explanation:
Once you click submit in your form, it will POST all of the data in the params hash called :claim. :foo :bar :animal_id would be three hypothetical names of three of your form fields, including your hidden animal_id.
We make a private method to take the accepted input strings form the form, and do .require and .permit to prevent from sql injection.
The claim_params method returns the hash of parameters required to pass into the Claim.new method so that you can create a new Claim object (aka row, aka record).
So claim.new(claim_params) creates a new claim from the form, including the animal_id, via the claims_params method's return value, which is was filtered from the form input.
The .new method creates a record, but does not actually save into the table.
.save does.
Alternatively, instead of using .new and .save, you can use .create, which does both in succession in one shot.
Hopefully I understood your app, question, and needs well enough for this to be what you need.
I'm curious,
Also, can you clarify what is being excluded? That is, what the exclusion table looks like.
Is the purpose to simply prevent some animals from being listed and "claim-able?"
What reason would there be for your application that it couldn't have the Animals model have a column named :excluded, and validate against duplications (the point being that you don't have one row of an animal as excluded and a second row of the same animal not excluded, resulting in a duplication)
Is there any sort of before_filter or anything that could take the
passed params from the URL and temporarily create the relationship
just for the new form view and then permanently create the
relationship once the form is submitted?
I think the 'thing' you are looking for is build. Before filter will not solve that problem nicely.
Ruby on Rails. How do I use the Active Record .build method in a :belongs to relationship?
Build vs new in Rails 3
If it doesn't help can you post in here how did you implement models associations ?
with the relations update:
animal has_one exclusion
exclusion has_one animal
due to documentation for has_one (http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one)
Specifies a one-to-one association with another class. This method
should only be used if the other class contains the foreign key.
and that somehow implicates that at least one side should have belongs_to definition on or
has_many through something
Related
I want to write a method that creates a bunch of almost-duplicate records, just with one or two parameters changed. I'll make a form to control those parameters, I'm just wondering about how best to write the method, and where do keep it.
Presently in my document.rb I've written this:
def self.publish(brand, components, template)
brand.users.each do |user|
Document.create(:component_ids => components, :message => 'Message.', :template_id => template.id, :user_id => user.id)
end
end
It doesn't feel right though. Is there a better way to do this?
This code is fine if your security model allows all these fields to be bulk assignable by mention in attr_accessible in the model. If it doesn't then you're better off using the block form of create. Also, if Document, Template and User are ActiveRecord instances, you should let Rails manage the details of ids.
def self.publish(brand, components, template)
brand.users.each do |user|
Document.create do |doc|
doc.component_ids = components,
doc.message 'Message.',
doc.template = template,
doc.user = user
end
end
end
One final note is that component_ids must be serialized to store a list. This is probably a flaw in your model design. The better way is (probably) to specify Component belongs_to User and also User has_many Components. I.e. Component contains a foreign key to User. If it's necessary for a Component to belong also to many users, then you'll need either has_and_belongs_to_many or has_many ... through. The Rails guide on relations describes all this in more detail.
With the right relations set up, the code will become:
def self.publish(brand, components, template)
brand.users.each do |user|
Document.create do |doc|
doc.components = components, # Components is now a list of active records.
doc.message 'Message.',
doc.template = template,
doc.user = user
end
end
end
The resulting SQL will get all the foreign keys and (if necessary) relation tables filled in correctly.
Tracker belongs_to Property
Property belongs_to Branch
I am trying to efficiently do something like this:
Get all trackers where the branch's ID is 0 or 1. Tracker knows nothing about Branch except through Property.
Is it possible to form some sort of "where" statement that could get this information?
You have to use intermediate relation Property or you need to define new relation between Tracker and Branch
Tracker.joins(:property).merge(Property.where(branch_id: [0,1]))
I have an associations like:
class Contact
has_many :addresses
has_many :email_addresses
has_many :phone_numbers
end
I want to save all the records (address, email and phones) in once single save statement. For that I wrote following code.
contact = Contact.new(contact_params)
contact.addresses.build(address_params1)
contact.addresses.build(address_params2)
contact.email_addresses.build(email_params1)
contact.email_addresses.build(email_params2)
contact.phone_numbers.build(phone_params1)
contact.phone_numbers.build(phone_params2)
contact.save
It does save the contact, but not saving other records. What am I missing here?
NOTE: I am not using any form to save data. I am importing data.
Add validates_associated in your contact model and then check
validates_associated :addresses, :email_addresses, :phone_numbers
First, In my opinion, you just CAN'T do that.
If you want to save addresses, you need a contact_id for each of them.
And, when you save contact, rails will first validate contact and all sub-objects. This means
contact.valid? must be true before you can save any record.
Since contact does not have an id before save to db, contact_id in any of addresses is empty.
Therefore, contact.valid? will always be false as long as you need to CREATE new contact and its sub-objects at the same time
To summarize, here is the steps of rails:
validate contact itself, success!(NOTE: note saved)
validate address for each of contact.addresses, contact_id not provided, fail!
Second, my suggestion about your problem
About your problem "It does save the contact, but not saving other records.", you need to tell rails that you want to save sub-objects(as shown bellow). Otherwise, rails will ignore other objects. However, even though you did this, you would also meet the problem I described above.
#contact.rb
accepts_nested_attributes_for :addresses
...
My suggestion: you can use transaction.
creat contact
build all sub-objects
save contact again
code sample:
#contacts_controller.rb
Contact.transaction do
contact = Contact.new(contact_params)
contact.addresses.build(address_params)
...
contact.save
end
I have two models, Invitation and RSVP. An invitation has many rsvps and rsvp belongs to an invitation. I want to run a query that will return all invitations and the rsvps that belong to each invitation. I want to have all the attributes that belong to both invitation and rsvp. I'm aware of includes and have been trying things like
#results = RSVP.where(user_id: 3).includes(:invitation)
but I'm only getting the attributes of RSVP returned. I ideally want to have the attributes of the Invitation that RSVP belongs to added to the results. What concept am I missing or should I think of this a different way?
Let us assume that the Invitation model has two fields event_name and event_date that you want to access in your query results. You can customize the select list if provide a joins clause.
RSVP.select("rsvps.*, invitations.event_name invitation_event_name,
invitations.event_date invitation_event_date"
).where(user_id: 3).joins(:invitation).each do |rsvp|
puts rsvp.invitation_event_name, rsvp.invitation_event_date
end
RSVP.where(...) with or without the includes(...) is going to return a collection of RSVP objects. By including the :invitation association each RSVP has, you're eager-loading the :invitation for each RSVP in the collection all at once. This prevents a separate SELECT * FROM invitations WHERE ... query from being run for every RSVP in the collection when you refer to it's :invitation association.
.includes is nothing but a query optimization if you plan on using an association for objects within a collection. It does not merge attributes from the association into the model instances in the result set.
If you want to have an attribute from the associated Invitation included on the RSVP instances, you can use Rails delegate method. You can read about it here.
On your RSVP model you'd do something like this, listing out the desired attributes from Invitation in place of the placeholders I've left below.
class RSVP < ActiveRecord::Base
has_one :invitation
delegate :some_invitation_attribute, :another_invitation_attribute, to: :invitation
Now you can call :some_invitation_attribute and :another_invitation_attribute directly on an RSVP instance.
#results = RSVP.where(user_id: 3).includes(:invitation)
puts #results.first.some_invitation_attribute # delegates the .some_invitation_attribute method call to the associated Invitation
Im building an app where Users have some sort of wishlist
a User can have only one wishlist, and can add existing Items to that wishlist
however the Items belong to other Users on the site
I need to be able to access the wishlist items through current_user.wishlist.items (im using Devise so current_user is available)
i though of adding a wishlist_id column to the items table, but that wouldnt work since items can belong to multiple wishlists.
this seems simple but im having a hard time visualizing the relationship or the migration im supposed to generate
class User < ActiveRecord::Base
has_one :wishlist # or belongs_to :wishlist, it depends which you prefer
end
class Wishlist < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :items
end
And of course:
./script/rails generate migration create_item_wishlists wishlist_id:integer item_id:integer
to create join table between items and wishlists.
UPDATE: To answer "frank blizzard" question in comment:
Let's say you have the same structure as in my answer (just change Item to Product or other model name), with HABTM relationship you just need to add new "item" to collection of "items", and then save wishlist:
#user.wishlist.items << item
#user.wishlist.save
You can make it method in user:
class User
def add_to_wishlist(item)
wishlist.items << item
end
end
If you want to remove or modify collection of "items", just use any Ruby method from Array and then save wishlist, which will check differences for you and save only changes.