I have notices and comments on those notices. The comments are also of class notice. When a notice is submitted for creation, its commentee_id will be blank if it is an original notice. If the notice is a comment on another notice, its commentee_id will be the id of the notice it is commenting on.
notices_controller.rb:
def create
#character = Character.find_by(callsign: params[:callsign])
#notice = #character.notices.build(notice_params)
if #notice.save
.
.
end
end
def notice_params
params.require(:notice).permit( :content, :picture, :latitude, :longitude,
active_comment_relationship_attributes: [:commentee_id] )
end
notice.rb:
belongs_to :character
has_one :active_comment_relationship, class_name: "Commentrelationship",
foreign_key: "commenter_id",
dependent: :destroy
has_one :supernotice, through: :active_comment_relationship, source: :commentee
accepts_nested_attributes_for :active_comment_relationship
before_validation :create_commentee
private
def create_commentee
if !commentee_id.blank?
create_active_comment_relationship(commentee_id: :commentee_id)
end
end
The new notice model doesn't get successfully created. I get the error message:
undefined local variable or method `commentee_id' for #<Notice:0x0000010c3a4708>)
When create_commentee is called, the new #notice instance has been created in memory but not yet saved to the database (create_commentee is a before_validation callback). At this stage, how do you correctly access commentee_id?
Neither this (!commentee_id.blank?):
def create_commentee
if !commentee_id.blank?
create_active_comment_relationship(commentee_id: :commentee_id)
end
end
nor this (commentee_id not :commentee_id):
def create_commentee
if !commentee_id.blank?
create_active_comment_relationship(commentee_id: commentee_id)
end
end
makes any difference.
At least one problem is that you're doing this:
if !:commentee_id.blank?
...when you should be doing this:
if !commentee_id.blank?
:commentee_id is a symbol, which is like a special kind of string, and it will never be blank (well, unless it's :""). You want to call the commentee_id method, i.e. without :).
P.S. If you want to be slightly more idiomatic, I'd recommend this instead:
if commentee_id.present?
Related
I want to create an Invoice and the regarding InvoiceItems at the same time. While Invoice has_many :invoice_items and an InvoiceItem belongs_to :invoice. How do I perform such action in Rails 7 so that a User can add multiple invoiceItems to their invoice via Turbo? I dont need to know how TurboStreams and stuff work, since I am familiar, but I just cant get the InvoiceItems to be created at the same time as the Invoice.
I already found this post, but could not get any useful information out of it.
Models
Invoice.rb
class Invoice < ApplicationRecord
belongs_to :project
has_many :invoice_items, foreign_key: :invoice_id # not sure if this foreign_key is necessary
accepts_nested_attributes_for :invoice_items
end
invoice_item.rb
class InvoiceItem < ApplicationRecord
belongs_to :invoice
end
Controllers
Invoice_controller.rb
def create
#project = Project.find(params[:project_id])
#client = Client.find(params[:client_id])
#invoice = #project.invoices.new(invoice_params)
#invoice_item = #invoice.invoice_items.new
#invoice.invoice_items_attributes = [:invoice_id, :amount]
#invoice.client_id = #client.id
respond_to do |format|
if #invoice.save
....
def invoice_params
params.require(:invoice).permit(... :invoice_item, invoice_item_attributes: [:id, :invoice_id, :amount, ...])
end
Currently I try using a form_for inside of the Invoice form like:
<%= form.fields_for #invoice.invoice_items.build do |lorem| %>
Which gives me following error in the console (but saves the invoice as expected:
Unpermitted parameter: :invoice_item. Context: { controller: InvoicesController, action: create, request: #<ActionDispatch::Request:0x000000010a0c8d88>, params: {"authenticity_token"=>"[FILTERED]", "invoice"=>{..., "invoice_item"=>{"invoice_id"=>"", "amount"=>"3"}}, "button"=>"", "controller"=>"invoices", "action"=>"create", "user_id"=>"1", "client_id"=>"1", "project_id"=>"1"} }
notice that the invoice_id is not passed to the invoice_item.
Via console something like
#invoice = Invoice.new
#invoice.invoice_items.new(amount: "3", ...)
#invoice.save!
Does work weirdly but it does not translate to my code.
What am I doing wrong here?
# invoice_item_attributes is wrong
def invoice_params
params.require(:invoice).permit(... :invoice_item, invoice_item_attributes: [:id, :invoice_id, :amount, ...])
end
Should be
# invoice_items_attributes is right
def invoice_params
params.require(:invoice).permit(... :invoice_item, invoice_items_attributes: [:id, :invoice_id, :amount, ...])
end
Notice the missing 's'.
https://www.ombulabs.com/blog/learning/rails/nested-forms.html
After following the GoRails screencast on how to properly set nested form attributes in rails, I still came across errors. I eventually could trace them and found this neat post which game the hint to use inverse_of and autosave: true. I am not 100% sure what those do, even though I will read now to find out, but my stuff is working properly now :)
Modified Model
class Invoice < ApplicationRecord
belongs_to :project
has_many :invoice_items, inverse_of: :invoice, autosave: true
accepts_nested_attributes_for :invoice_items
...
I am building a very simple application managing users and keys. Keys are nested attributes of a user. I am inspired by RailsCast #196.
The models are defined as:
class User < ApplicationRecord
#Validations
---
#Relations
has_many :keys, :dependent => :destroy
accepts_nested_attributes_for :keys, :reject_if => :all_blank, :allow_destroy => true
end
class Key < ApplicationRecord
#Validations
---
#Relations
belongs_to :user
end
The users controller includes strong parameters:
def user_params
params.require(:user).permit(:nom, :prenom, :section, :email, :password, keys_attributes: [:id, :secteur, :clef])
end
And I wish to initialize 1 key for each new user (users controller):
# GET /users/new
def new
#user = User.new
key = #user.keys.build({secteur: "Tous", clef: "ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678"})
end
I tried many ways to initialize the user's key, but I can't figure out how to pass parameters to it. User is always created, but the first key is not. No error message is issued.
Remove the '{' tags inside the build method parameters. Should be:
key = #user.keys.new(secteur: "Tous", clef: "ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678")
Also, the build method is just an alias for 'new' and used to behave differently on rails 3 apps so I've always strayed away from it and just use the more familiar 'new' method. Obviously don't forget to then save the object at some point in your controller.
So I have this model relationships
class User
has_one :wallet, :foreign_key => :user_id
end
class Wallet
after_initialize :set_value
def set_value
# Whatever
end
end
And I'd like that when I do User.last.wallet, User.last.wallet.new gets called.
I could achieve this by creating another method in the User model:
def get_wallet
self.wallet||self.wallet.new
end
and call get_wallet when needed.
But can't I get this without this useless and dirty extra method?
Something like:
has_one :wallet, :foreign_key => :user_id #, :build_if_not_found => true
Gems like this one: https://github.com/febuiles/auto_build don't do what I want: they build Wallet after creating the User object instead of creating when User.last.wallet is called.
Thanks
You can try this:
class User
has_one :wallet, :foreign_key => :user_id
def wallet
super || build_wallet
end
end
You still need to add some extra code, but it will do exactly what you want without any additional calls.
I'm in the process of building my first Rails application and am going crazy knowing this is probably a simple fix. I'm having trouble returning the proper object and attribute from a method. I have three models: User, Feed and Subscription.
User:
class User < ActiveRecord::Base
.
.
has_many :subscriptions, dependent: :destroy
has_many :feeds, through: :subscriptions
def subscribe!(feed_id)
subscriptions.create!(feed_id: feed.id)
end
end
Subscription:
class Subscription < ActiveRecord::Base
.
.
belongs_to :user, class_name: "User"
belongs_to :feed, class_name: "Feed"
validates :user_id, presence: true
validates :feed_id, presence: true
end
Feed:
class Feed < ActiveRecord::Base
.
.
belongs_to :subscriptions
def self.create_feed(feed_url)
feed = Feedzirra::Feed.fetch_and_parse(feed_url)
unless exists? :feed_url => feed.url
create!(
:title => feed.title,
:feed_url => feed.feed_url,
:url => feed.url,
:etag => feed.etag,
:last_modified=> feed.last_modified
)
end
end
end
Whenever I call user.subscriptions.create!(feed) explicitly from the console it works just fine, but the test using it don't pass. The test in question:
describe "subscribing" do
let(:feed) { Feed.create_feed("http://www.somevalidfeed.com/feed/") }
before { #user.subscribe!(feed) }
end
This returns:
NameError: undefined local variable or method `feed' for #<User:0x007f9c951b3b98>
I don't understand why this is returning an object or why it's trying to find the id of the User instead of the Feed object that's being passed to it. After searching for a few hours I just can't seem to get pointed in the right direction, so any help would be very much appreciated!
In your User class, I think your subscribe method is wrong, it should be
def subscribe!(feed)
subscriptions.create!(feed_id: feed.id)
end
probably here's the problem:
def subscribe!(feed_id)
subscriptions.create!(feed_id: feed.id)
end
probably you meant
def subscribe!(feed_id)
subscriptions.create!(feed_id: feed_id)
end
or even
def subscribe!(feed)
subscriptions.create!(feed_id: feed.id)
end
I'm having trouble with Active Record callbacks in a model which contains accepts_nested_attributes
I call build_associated_partiesusing an after_create callback, but these values are not being saved and I get <nil> errors. I've also tried using before_create & after_initialize callbacks without success.
What is causing the callbacks to fail?
connection.rb
class Connection < ActiveRecord::Base
attr_accessible :reason, :established, :connector, :connectee1,
:connectee2, :connectee1_attributes,
:connectee2_attributes, :connector_attributes
belongs_to :connector, class_name: "User"
belongs_to :connectee1, class_name: "User"
belongs_to :connectee2, class_name: "User"
accepts_nested_attributes_for :connector, :connectee1, :connectee2
belongs_to :permission
after_create :build_associated_parties
# builds connectee's, connector, permission objects
def build_associated_parties
build_connector
build_connectee1
build_connectee2
build_permission
end
connection_controller.rb
class ConnectionsController < ApplicationController
def new
#connection = Connection.new
end
def create
#connection = Connection.new params[:connection]
if #connection.save
flash[:notice] = "Connection created successfully!"
redirect_to #connection
else
render :new
end
end
end
However, if I instead build these attributes inside the controller as shown below, I don't get the error. This is nice, but it seems to go against keeping business logic code out of the controller.
class ConnectionsController < ApplicationController
def new
#connection = Connection.new
#connection.build_connectee1
#connection.build_connectee2
#connection.build_connector
end
end
How can I accomplish the same functionality with code in the model? Are there advantages to keeping it in the model?
You called your method build_associated_parties after connection is created, so how these methods:
build_connector
build_connectee1
build_connectee2
build_permission
know what params it will use? So they don't know what values are passed into method then they will get error. In controller, they didn't have error because they used values of params[:connection].
On your form, if you already have fields for connector, connectee1, connectee2, you should put code which initialize object in your new controller. When you save #connection, it's saved those object too. I think these codes aren't need to put into model. Your model only should put other logic code, like search or calculation...
after_create is a big no. Use after_initialize in your model and use self inside your build_associated_parties method. See if that works.
Moved logic out of controller and back into model. However, the build_* code was overwriting the values I was passing into the nested attributes.
By adding unless {attribute} to these build_ methods, I was able to properly pass in the values.
class Connection < ActiveRecord::Base
attr_accessible :reason, :established, :connector, :connectee1, :connectee2,
:connectee1_attributes, :connectee2_attributes, :connector_attributes
belongs_to :connector, class_name: "User"
belongs_to :connectee1, class_name: "User"
belongs_to :connectee2, class_name: "User"
accepts_nested_attributes_for :connector, :connectee1, :connectee2
belongs_to :permission
after_initialize :build_associated_parties
validates :reason, :presence => true
validates_length_of :reason, :maximum => 160
#builds connectee's, connector, permission objects
def build_associated_parties
build_connector unless connector
build_connectee1 unless connectee1
build_connectee2 unless connectee2
end