Rails model associates another model more than once - ruby-on-rails-3

I got a model called Pages that looks like this:
title
description
image_id
background_image
logo
I also got a model calles Images that looks like this:
id
title
What I would like to be able to is to associate these three fields with the image model.
image_id
background_image
logo
image_id works perfectly because of the Page model association:
has_many :images
How do I make it work with background_image and logo?
Hope my question makes any sense. Thanks
UPDATE

In your Page model, you'll want to use belongs_to to specify that it contains a pointer to an Image. For the first one, that'll just be belongs_to :image.
But you want to have three pointers. You can do that; for each of the other fields, you'll add an option to belongs_to to specify that it's pointing to an Image. You do that using the :class_name option, like this;
belongs_to :logo, :class_name => 'Image'
You'll also probably want to name your columns with _id at the end, so background_image_id and logo_id - it's more Railsy that way.
All of this is documented in the Rails Guides; the :class_name option is specifically here.

Related

Adding a column Migration with a default value in ruby on rails

I am using SQLite3 and I would like the following to work:
class AddNameToGoal < ActiveRecord::Migration
def change
add_column :goals, :goal_name, :text, default: goal.exercise.name
end
end
Or maybe this makes more sense as what I'm trying to do:
add_column :goals, :gname, :text, default: Goal.find(row_id).exercise.name
How do I get the above to work.
I doubt it will work as it is but that's what I want.
Specifically, The user is associated with an Exercise through the exercise_id column.
belongs_to :user
belongs_to :exercise
has_many :workouts, dependent: :destroy
(This is the model for Goal)...
I would like the user to be able to choose their own name for the Goal but I can give them the hint to name the goal after the Exercise's name and if they choose to leave it blank it will default to the exercise's name. More importantly this must happen on the SQL side so that later when I have a collection drop down which requires a name of the goal they will need a name which corresponds to the exercise.
<%= f.collection_select(:goal_id, #goals, :id,
:goal_name, :include_blank => "Please Select") %>
The Exercise Model is made in Rails to have
id, Name, other columns.
Exercise Model:
class Exercise < ActiveRecord::Base
has_one :goal
Is there a strategy by which that is possible.
Another option would be to help me find a strategy for active record so that I can do:
<%= f.collection_select(:goal_id, #goals, :id,
:goal_name, :include_blank => "Please Select") %>
with (something else to replace goal_name with Exercise.name like goal.exercise.name and goal.id and show only the ID.
Doing this when you define the column on the table is problematic. I definitely think doing it upon creation, in the model, is how you'd want to go.
You might also check out the default_value_for gem and see if it helps.
Hi so in case any body ever wants to do this:
I figured out a strategy...
This is perfect for anything that has an instance of name in terms of the user.
For example, you have something that tracks your Car and you want to give that Car by default the name which comes from that Car's Make_and_Model (model) in Rails.
Naturally when someone says they have a new "Honda Accord" then they get to have that name, but if they ever want to change it to "Lucy" because her name is Lucy and you better treat her with the respect she deserves!, then this gives you the option to do that.
You do not want to change the name for that whole Make&Model you only want to change the name for that specific car.make_and_model which belongs_to User.
If you are wanting something to have a name that defaults to another name but allows the user to change that. Do that on the model level... by setting a before_save method inside the model... like so:
before_save :default_values
def default_values
self.goal_name = self.exercise.name if self.goal_name.nil?
end

Returning associations for specific model when there is a polymorphic association in Rails 3.2

I have a polymorphic association in a Rails 3 app where a User may favorite objects of various classes.
class Favorite < ActiveRecord::Base
belongs_to :user
belongs_to :favoriteable, :polymorphic => true
end
class User < ActiveRecord::Base
has_many :favorites
end
class Image < ActiveRecord::Base
has_many :favorites, :as => :favoriteable
end
class Video < ActiveRecord::Base
has_many :favorites, :as => :favoriteable
end
I would like to be able return a list of just a User's favorite_images for example.
user.favorite_images #returns a list of the images associated with the user via :favoritable
I'm guessing there is a straightforward way of doing this but I haven't been able to figure it out. Let me know if you need anymore info.
Thanks!
===edit====
I know that I could retrieve what I am looking for via
favorite_images = user.favorites.collect{|f| if f.favoriteable_type=='Image'; f.favoriteable; end}
I could define an instance method for the User class and put that inside. I was wondering if there is a way to do it as some sort of has_many association. Really just because going forward it would be easier to have all that in one place.
When you created the table for Favorite you created a couple of columns favoriteable_id and favoriteable_type and you can use this information to restrict your query.
If you do user.favorites you will get all of the favorites and to restrict them to say just the images then you can do user.favorites.where(favoriteable_type: 'image') but that just gives you the favorite records and it sounds like you want the actual images. To get those you can do it by then mapping and pulling the favoriteable out. You'll likely want to include it in the query though so you don't hit the database so much. I would also make this a method on User.
def favorite_images
favorites.includes(:favoriteable).where(favoriteable_type: 'Image').map(&:favoriteable)
end

How to implement has_many :through relationship in rails with this example

i've been searching through similar questions but i still don't get how implement this relationship. I have of course three models :
class Recetum < ActiveRecord::Base
attr_accessible :name, :desc, :duration, :prep, :photo, :topic_id
has_many :manifests
has_many :ingredients, :through => :manifests
end
class Ingredient < ActiveRecord::Base
attr_accessible :kcal, :name, :use, :unity
has_many :manifests
has_many :recetum, :through => :manifests
end
class Manifest < ActiveRecord::Base
attr_accessible :ingredient_id, :quantity, :receta_id
belongs_to :recetum
accepts_nested_attributes_for :ingredient
belongs_to :ingredient
end
Recetum would be a recipe (typo when scaffolding), this recipe may have one or more ingredients (already on the db). So when i create a new Recetum, i need the new recetum to be created and one record inserted in manifest for each ingredient entered by the user.
I would need some help now with views and controllers, how do i create the form for recetum with fields for the ingredients and more important what do i have to modify recetum controller.
Any suggestions or help would be very much appreciated as this part is crucial for my project, thanks in advance.
You have a couple options, and mainly they depend on what you want to do in your view. Do you want to display a set number of max_ingredients or do you want it to be completely dynamic? The dynamic case looks better for the user for sure, but it does make for some more complicated code.
Here is a good RailsCast which explains how to do it dynamically via JavaScript:
http://railscasts.com/episodes/74-complex-forms-part-2
Unfortunately, not everyone runs with JavaScript enabled so you may want to consider doing it the static way.
Firstly, I don't think you need accepts_nested_attributes_for in your Manifest model. However, I do think you need it in your Recetum model. If you're going the static route, you'll probably want to set a reject_if option too.
accepts_nested_attributes_for :manifests, reject_if: :all_blank
Once you do this, you'll need to add manifests_attributes to your attr_accessible.
With the static route, you'll need to prebuild some of the manifests. In your new controller you'll want something like this:
max_ingredients.times do
#recetum.manifests.build
end
In your edit and the error paths of your create and update, you may want:
(max_ingredients - #recetum.manifests.count).times do
#recetum.manifests.build
end
Finally, your view will need some way to set the ingredient. I'll assume a select box for now.
f.fields_for :manifests do |mf|
mf.label :ingredient_id, "Ingredient"
mf.collection_select :ingredient_id, Ingredient.all, :id, :name
You'll want to add some sort of formatting through a list or table probably.
Hopefully, that's enough to get you started.

Rails: How do I transactionally add a has_many association to an existing model?

Let's imagine I run an imaginary art store with a couple models (and by models I'm referring to the Rails term not the arts term as in nude models) that looks something like this:
class Artwork < ActiveRecord::Base
belongs_to :purchase
belongs_to :artist
end
class Purchase < ActiveRecord::Base
has_many :artworks
belongs_to :customer
end
The Artwork is created and sometime later it is included in a Purchase. In my create or update controller method for Purchase I would like to associate the new Purchase with the existing Artwork.
If the Artwork did not exist I could do #purchase.artworks.build or #purchase.artworks.create, but these both assume that I'm creating a new Artwork which I am not. I could add the existing artwork with something like this:
params[:artwork_ids].each do |artwork|
#purchase.artworks << Artwork.find(artwork)
end
However, this isn't transactional. The database is updated immediately. (Unless of course I'm in the create controller in which case I think it may be done "transactionally" since the #purchase doesn't exist until I call save, but that doesn't help me for update.) There is also the #purchase.artwork_ids= method, but that is immediate as well.
I think something like this will work for the update action, but it is very inelegant.
#purchase = Purchase.find(params[:id])
result = #purchase.transaction do
#purchase.update_attributes(params[:purchase])
params[:artwork_ids].each do |artwork|
artwork.purchase = #purchase
artwork.save!
end
end
This would be followed by the conventional:
if result
redirect_to purchase_url(#purchase), notice: 'Purchase was successfully updated.' }
else
render action: "edit"
end
What I'm looking for is something like the way it would work from the other direction where I could just put accepts_nested_attributes_for in my model and then call result = #artwork.save and everything works like magic.
I have figured out a way to do what I want which fairly elegant. I needed to make updates to each part of my Product MVC.
Model:
attr_accessible: artwork_ids
I had to add artwork_ids to attr_accessible since it wasn't included before.
View:
= check_box_tag "purchase[artwork_ids][]", artwork.id, artwork.purchase == #purchase
In my view I have an array for each artwork with a check_box_tag. I couldn't use check_box because of the gotcha where not checking the box would cause a hidden value of "true" to be sent instead of an artwork id. However, this leaves me with the problem of deleting all the artwork from a purchase. When doing update, if I uncheck each check box, then the params[:purchase] hash won't have an :artwork_ids entry.
Controller:
params[:purchase][:artwork_ids] ||= []
Adding this guarantees that the value is set, and will have the desired effect of removing all existing associations. However, this causes a pesky rspec failure
Purchase.any_instance.should_receive(:update_attributes).with({'these' => 'params'}) fails because :update_attributes actually received {"these"=>"params", "artwork_ids"=>[]}). I tried setting a hidden_value_tag in the view instead, but couldn't get it to work. I think this nit is worthy of a new question.
It is probably best to use make the purchase model a join table and have many to many associations.
Here is an example for your use case.
Customer model
has_many :purchases
has_many :artwork, :through => :purchase
Artwork model
has_many :purchases
has_many :customers, :through => :purchase
Purchase model
belongs_to :customer
belongs_to :artwork
The purchase model should contain customer_id and artwork_id.
you would also need to create a purchase controller that allows you create a new purchase object.
When a customer presses the purchase button it would create a new purchase object which includes the customer_id and the artwork_id. This allows you to create an association between the customer and the artwork they purchase. You can also have a price_paid column to save the price the customer paid at the time of purchase.
if you need more help you can research join many to many associations using :through.
hope it helps

model has two attributes that belong to another model

I have a model as follows:
Greeting
belongs_to :icon
belongs_to :icon, :foreign_key => :user_icon
I need to save the icon_id and also the user_icon id in the case I don't have a registered user.
Is this correct? Will I be able to access the icon by doing the following:
#greeting.icon.name
#greeting.user_icon.name
I want to improve this question so let me explain it better:
I want to save two objects from the same model in another model.
So Greeting belongs to Icon but I will have two fields in the Greetings table for foreign keys from the Icons table but labeled differently.
I call one foreign key attribute icon_id and the other user_icon_id.
To do this is the following correct:
Greeting
belongs_to :icon
belongs_to :icon, foreign_key => :user_icon_id
Almost correct, you need something like this:
belongs_to :icon
belongs_to :user_icon, :class_name => "Icon", foreign_key => :user_icon_id
If you change the name of the field in a has_one, has_many or belongs_to association in such a way that Rails can't convert it into a model name, you need to tell Rails which model you actually mean, hence the :class_name.
Nope. You need
belongs_to :user_icon, :foreign_key => :user_icon
If you want to have a greeting.user_icon accessor using the foreign key user icon in your database.