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
Related
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
I've got the following order_params:
{"user_id":"1","order_status_id":"1","delivery_type_id":"1","delivery_time":"10","order_items":[{"count":"5","item_id":"1"}]}
These params are used for creating an Order object with nested order_item objects. Models:
class Order < ActiveRecord::Base
validates :user, :order_status, :delivery_type, presence: true
belongs_to :user
belongs_to :order_status
belongs_to :delivery_type
has_many :order_items
accepts_nested_attributes_for :order_items
end
Order_item:
class OrderItem < ActiveRecord::Base
validates :item, :order, :count, presence: true
belongs_to :item
belongs_to :order
end
But when I'm trying to create a new Order:
Order.create!(order_params)
I got the following error:
OrderItem(#70182455585540) expected, got ActiveSupport::HashWithIndifferentAccess(#70182454696760)
How can I fix it? Thanks in advance.
You key for order_items attributes is wrong, which should be order_items_attributes if you use nested attributes.
...,"order_items":[{"count":"5","item_id":"1"}]}
should be
...,"order_items_attributes":[{"count":"5","item_id":"1"}]}
I'm feeling a little bit dumb to ask this, but I've been Googling my a*# off.
Well I have the following models:
class Company < ActiveRecord::Base
has_many :employments
has_many :users, through: :employments
validates_presence_of :name
validates_presence_of :description
validates_numericality_of :zip, only_integer: true
validates_presence_of :email
validates_presence_of :street
validates_presence_of :city
validates_presence_of :country
validates_presence_of :telephone
end
class Employment < ActiveRecord::Base
belongs_to :user
belongs_to :company
end
class User < ActiveRecord::Base
has_many :employments
has_many :companies, through: :employments
end
Important here is the company-Model which has some validations.
Now, I have the following Controller to create a new Company:
class CompaniesController < ApplicationController
def create
#company = Company.new(company_params) # The params were set with a private Method
#employment = #company.employments.build(user: current_user, is_admin: true)
if #employment.save
redirect_to :back, flash: { success: 'Success' }
else
#title = 'Create a new company'
render :new
end
end
end
The Problem is, that when I leave the companies-Fields blank, the company gets not created, but the employment-Model gets persistet in the Database.
I believe It has something to do with the Company.new()-Call I have to check, if the #company-Model gets created first, before the #employment-Model gets created.
How can I achieve that the validation gets tested first?
Thank you very much!
To validate associated object you need to use validates_associated. Please note the "Warning" and "Note" in the linked api document.
Try:
class Employment < ActiveRecord::Base
belongs_to :user
belongs_to :company
validates_associated :company
end
in addition to vinodadhikary's answer, you can also try saving the company. so instead of #employment.save, use #company.save. That should also save #employment when #company passes validations.
This question pertains to AMS 0.8
I've got two models:
class Subject < ActiveRecord::Base
has_many :user_combinations
has_ancestry
end
class UserCombination < ActiveRecord::Base
belongs_to :stage
belongs_to :subject
belongs_to :user
end
And two serializers:
class UserCombinationSerializer < ActiveModel::Serializer
attributes :id
belongs_to :stage
belongs_to :subject
end
class SubjectSerializer < ActiveModel::Serializer
attributes :id, :name, :description, :subjects
def include_subjects?
object.is_root?
end
def subjects
object.subtree
end
end
When a UserCombination is serialized, I want to embed the whole subtree of subjects.
When I try to use this setup I get this error:
undefined method `belongs_to' for UserCombinationSerializer:Class
I tried changing the UserCombinationSerializer to this:
class UserCombinationSerializer < ActiveModel::Serializer
attributes :id, :subject, :stage
end
In this case I get no errors, but the subject is serialized in the wrong way - not using the SubjectSerializer.
My questions:
Shouldn't I be able to use a belongs_to relation in the serializer?
If not - how can I get the wanted behaviour - embedding the subject tree using the SubjectSerializer?
This is not really elegant but it seems to be working :
class UserCombinationSerializer < ActiveModel::Serializer
attributes :id, :stage_id, :subject_id
has_one :subject
end
I don't really like calling has_one whereas it's actually a belongs_to association :/
EDIT: Disregard my comment about has_one/belongs_to ambiguity, the doc is actually pretty clear about it: http://www.rubydoc.info/github/rails-api/active_model_serializers/frames
In Active Model Serializer 0-10-stable, belongs_to is now available.
belongs_to :author, serializer: AuthorPreviewSerializer
belongs_to :author, key: :writer
belongs_to :post
belongs_to :blog
def blog
Blog.new(id: 999, name: 'Custom blog')
end
https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/serializers.md#belongs_to
So you could do:
class UserCombinationSerializer < ActiveModel::Serializer
attributes :id
belongs_to :stage, serializer: StageSerializer
belongs_to :subject, serializer: SubjectSerializer
end
What if you try with something like this:
class UserCombinationSerializer < ActiveModel::Serializer
attributes :subject,
:stage,
:id
def subject
SubjectSerializer.new(object.subject, { root: false } )
end
def stage
StageSerializer.new(object.stage, { root: false } )
end
end
My models look something like this:
class User < ActiveRecord::Base
attr_accessible: :name
has_many :reviews
end
class Product < ActiveRecord::Base
attr_accessible: :name
has_many :reviews
end
class Review < ActiveRecord::Base
attr_accessible: :comment
belongs_to :user
belongs_to :product
validates :user_id, :presence => true
validates :product_id, :presence => true
end
I am trying to figure out what the best way is to create a new Review, given that :user_id and :product_id are not attr_accessible. Normally, I would just create the review through the association ( #user.reviews.create ) to set the :user_id automatically, but in this case I am unsure how to also set the product_id.
My understanding is that if I do #user.reviews.create(params), all non attr_accessible params will be ignored.
You can do:
#user.reviews.create(params[:new_review])
...or similar. You can also use nested attributes:
class User < ActiveRecord::Base
has_many :reviews
accepts_nested_attributes_for :reviews
...
See "Nested Attributes Examples" on http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html.
It seems you would like to implement a many-to-many relationship between a User and Product model, with a Review model serving as a join table to connect the two with an added comment string. This can be accomplished with a has many through association in Rails. Start by reading the Rails Guides on Associations.
When setting up your Review model, add foreign keys for the User and Product:
rails generate model review user_id:integer product_id:integer
And set up your associations as follows:
class User < ActiveRecord::Base
has_many :reviews
has_many :products, through: :reviews
end
class Product < ActiveRecord::Base
has_many :reviews
has_many :users, through: :reviews
end
class Review < ActiveRecord::Base
# has comment string attribute
belongs_to :user
belongs_to :product
end
This will allow you to make calls such as:
user.products << Product.first
user.reviews.first.comment = 'My first comment!'
Here's how you would create a review:
#user = current_user
product = Product.find(params[:id])
#user.reviews.create(product: product)