Rails polymorphic has_one build - ruby-on-rails

Given a ContentBlock model:
class ContentBlock < ActiveRecord::Base
has_one :block_association
has_one :image, through: :block_association, source: :content, source_type: "Image"
has_one :snippet, through: :block_association, source: :content, source_type: "Snippet"
accepts_nested_attributes_for :image, allow_destroy: true
accepts_nested_attributes_for :snippet, allow_destroy: true
end
BlockAssociation model:
class BlockAssociation < ActiveRecord::Base
belongs_to :content_block
belongs_to :content, polymorphic: true
end
Snippet model:
class Snippet < ActiveRecord::Base
has_one :block_association, as: :content
has_one :content_block, through: :block_association
validates :body, presence: true
end
I need to do:
#content_block.build_snippet
but this gives:
undefined method 'build_snippet' for #<ContentBlock:0x007ffb7edde330>
How would I achieve the intended result?
The form would be something like this:
<%= simple_form_for #content_block do |f| %>
<%= f.simple_fields_for f.object.snippet || f.object.build_snippet do |sf| %>
<%= sf.input :body %>
<% end %>
<% end %>
(Originally I had assumed that content_block would simply belong_to :content, polymorphic: true but that seemed inadequate due to the multiple content types.)
This is kind of close to what I'm doing, but I just can't quite get my head around it: http://xtargets.com/2012/04/04/solving-polymorphic-hasone-through-building-and-nested-forms/

class ContentBlock < ActiveRecord::Base
has_one :snippet, through: :block_association, source: :content, source_type: "Snippet"
end
This tells rails, you want instances of ContentBlock (lets make content_block this instance) to have one instance of Snippet called "snippet" of the fake-type "content" through BlockAssociation. ContentBlock instances should hence be able to respond to content_block.content which would retourn a collection of a snippet and/or image (I left out the image part in the code-snippets). How content_block can call only snippet content no one knows yet.
What does your BlockAssociation Model know:
class BlockAssociation < ActiveRecord::Base
belongs_to :content_block
belongs_to :content, polymorphic: true
end
It knows it belongs to a content_block and knows (since it will respond to content) one or more contents which have a content_type ('Snippet') and a content_id (1 or whatever snippets id is), the combination of those makes the relation to snippet
Now what you were missing was the Snippet part:
class Snippet < ActiveRecord::Base
has_one :block_association, :as => :snippet_content
has_one :content_block, :through => :content_association # I'm actually not quite sure of this
end
Which tells block_association how to call this type of content, since you want to differ between image-content and snippet-content. Now content_block.snippet_content should return the snippet and snippet.block_content should return block_content.
I hope I didn't mess anything up, these relations allways get my head spinning

Related

Rails build belongs_to association "must exist" error

I am trying to build a has_many model in the belongs_to model association in rails. The association is correct, but it shows error "must exist". I tried putting the optional: true, but it does not seem to be working.
Models
class User::Product < ApplicationRecord
has_one: :promo_code
end
class User::PromoCode < ApplicationRecord
belongs_to: :product, optional: true
accepts_nested_attributes_for :product
end
PromoCodesController
def new
#promo_code = User::PromoCode.new
#product.build_product
end
def create
#promo_code = User::PromoCode.new(promo_code_params)
#promo_code.save
end
def promo_code_params
params.require(:user_promo_code).permit(:product_id, :product_attributes => [:name])
end
form
form_with(model: promo_code) do |form|
form.fields_for :product do |f|
f.text_field :name
end
end
When the form saves an error appears saying "must exist", which I am assuming is referring to the foreign key in belongs_to.
Any ideas in what I can be doing wrong? I think the code above is the only relevant code that I have regarding this issue.
Looking into the issue linked by #engineersmnky, it looks as if this is a known bug when using accepts_nested_attributes_for.
This can be solved by using the inverse_of option to clarify the bi-directional relationship:
class User::Product < ApplicationRecord
has_one: :promo_code, inverse_of: :product
end
class User::PromoCode < ApplicationRecord
belongs_to: :product, optional: true, inverse_of: :promo_code
accepts_nested_attributes_for :product
end
Try that and see if it resolves your problem.
try this in the models respectively
has_one :promo_code, -> { PromoCode.order(:id) }, class_name: 'PromoCode',inverse_of: :product
belongs_to :product, inverse_of: :promo_code

How do I access the extra attribute on join table in my show page?

My models look like this:
class Project < ApplicationRecord
has_many :comments
has_many :contractor_projects
has_many :contractors, through: :contractor_projects
validates_presence_of :title, :contract_number, :category, :project_start_date, :project_end_date, :substantial_completion_date, :category, :solicitation_number, :project_officer, :location
accepts_nested_attributes_for :contractor_projects
end
class Contractor < ApplicationRecord
has_many :contractor_projects
has_many :projects, through: :contractor_projects
validates :name, presence: true
validates :email, presence: true, uniqueness: true
end
class ContractorProject < ApplicationRecord
belongs_to :contractor
belongs_to :project
end
The ContractorProject model has an extra attribute #bid_status that I want to reflect on project's show page but it does not appear even though it's in the params when i raised it.
below is sample method for your case
def show
#project = Project.find(params[:id]
#contractors = #project.contractors
end
inside show.html.erb, you have to loop it, since it may get more than one records
<% #contractors.each do |contractor| %>
<%= contractor.bid_status %>
<% end %>

Rails 4 strong parameters does not work for a polymorphic association

I have set a polymorphic association and added a nested form in the view. Im trying to create the main record and the association at the same time. The main record gets created but the association won't.
Here are the two models in question :
class UnRegistered < ActiveRecord::Base
has_one :vehicle, as: :detailable, dependent: :destroy
belongs_to :dealer
class Vehicle < ActiveRecord::Base
belongs_to :purchase_details, polymorphic: true
belongs_to :brand
belongs_to :model
belongs_to :color
belongs_to :customer
Here's the form definitions :
<%= form_for(#un_registered, url: panel_un_registereds_path, remote: true) do |f| %>
<%= f.fields_for :vehicle do |f_vehicle| %>
Here's a sample params set I get :
{"utf8"=>"✓", "un_registered"=>{"vehicle"=>{"brand_id"=>"", "model_id"=>"", "year"=>"", "engine_number"=>"gdfg", "chassis_number"=>"", "color"=>"", "options"=>""}, "original_price"=>"", "insurance"=>"", "freight"=>"", "tt"=>"", "tt_date"=>"", "duty"=>"", "clearance_fee"=>"", "other_expenses"=>"", "dealer_id"=>"", "landing_date"=>"", "loading_date"=>""}, "controller"=>"panel/un_registereds", "action"=>"create"}
Here's the controller actions :
def create
#un_registered = UnRegistered.create(un_registered_params)
end
def un_registered_params
params.require(:un_registered).permit(:original_price, :insurance, :freight, :tt, :tt_date, :duty, :clearance_fee, :other_expenses, :loading_date, :landing_date, :dealer_id, vehicle_attributes: [:id, :brand_id, :model_id, :engine_number, :chassis_number, :color_id, :year, :options, :selling_price, :customer_id, :purchase_date, :_destroy])
end
Full form code :
https://gist.github.com/THPubs/9665e0e5594e15fcc76a
New method :
def new
#un_registered = UnRegistered.new
end
Your form is fine. You just need to add below changes.
In your un_registered.rb model
class UnRegistered < ActiveRecord::Base
has_one :vehicle, as: :detailable, dependent: :destroy
belongs_to :dealer
accepts_nested_attributes_for :vehicle #this one
end
And in your controller,
def new
#un_registered = UnRegistered.new
#un_registered.build_vehicle #this one
end

How to best way define the main object in the polymorphic relations?

I have models:
dress.rb
class Dress < ActiveRecord::Base
has_many :images, as: :imageable
end
publication.rb
class Publication < ActiveRecord::Base
has_many :images, as: :imageable
end
image.rb
class Image < ActiveRecord::Base
mount_uploader :name, ImageUploader
belongs_to :imageable, polymorphic: true
end
I have two ideas how its write:
Add main_image_id (:integer) to Image, Publication and other models. Also can create a relation has_one :main_image, class_name: 'Image', foreign_key: :main_image_id. But this is bad way because this field need add to every created Model which has polymorphic relations with Image.
Add main (:boolean) to Image. And check every time true or false. This is bad way too because table images will have unused fields.
Who has any thoughts?
2nd way is better. this way you can order images by this field and make scopes.
# image.rb
scope main, ->{where(main: true)}
# publication.rb
scope images, ->{images.order(:main)}
#images.main #=> will give you needed result as
#publiscations.images #=> will give you first image as main.
And if you think about extra column, you will need to add it anyway. So why not to use it where it is more useful
STI
Maybe try STI
#app/models/asset.rb
Class Asset < ActiveRecord::Base
mount_uploader :name, ImageUploader
end
#app/models/image.rb
Class Image < Asset
belongs_to :imageable, polymorphic: true
delegate :url, to: :asset #-> not used Carrierwave, so I don't know how this is done
end
#app/models/dress.rb
Class Dress < ActiveRecord::Base
has_many :images, as :imageable
end
#app/models/publication.rb
Class Publication < ActiveRecord::Base
has_many :images, as :imageable
end
This will give you the ability to call the following:
#app/controllers/dresses_controller.rb
Class DressesController < ApplicationController
def index
#dresses = Dress.all
end
end
#app/views/dresses/index.html.erb
<% #dresses.each do |dress| %>
<%= dress.images.each do |image| %>
<%= image_tag image.url %>
<% end %>
<% end %>

find_or_create on a has many through relationship

I have a has many through relationship in my app:
Shows has many Bands through => Lineups
Bands are unique by :name
class Show < ActiveRecord::Base
attr_accessible :city_id, :title, :dateonly, :timeonly, :image, :canceled, :venue_attributes, :bands_attributes
belongs_to :city
belongs_to :venue
has_many :lineups
has_many :bands, through: :lineups
has_and_belongs_to_many :users
end
class Lineup < ActiveRecord::Base
belongs_to :show
belongs_to :band
end
class Band < ActiveRecord::Base
attr_accessible :name, :website, :country, :state
has_many :lineups
has_many :shows, through: :lineups
validates :name, presence: true
validates_uniqueness_of :name
before_save :titleize_name
private
def titleize_name
self.name = self.name.titleize
end
end
New Bands are created like this:
(lets say we have a show record already saved called s1)
> s1.bands.new(name: "Wet Food")
> s1.save
Right now this will only save if a band named "Wet Food" doesn't already exist
In which model is the best place to do a Band.find_or_create in this relationship so that an existing band can be used if one with the same name exists?
This is generally the type of call that would go in a Controller (or maybe a service object), but not in a Model. It really depends on the particular user flow that you're trying to accomplish in your app. Basically, where ever you are already using s1.bands.new, you could use this instead :
s1.bands.where(name: 'Wet Food').first_or_create

Resources