I have following model structure
class SetBenchmark < ApplicationRecord
self.table_name = 'benchmarks'
has_many :my_question_sets
end
class MyQuestionSet < ApplicationRecord
belongs_to :set_benchmark, class_name: 'SetBenchmark',
end
class MyAdmin < ApplicationRecord
has_many :my_question_sets
end
Following is controller render method
render json: my_admin, include: { my_question_sets: [ 'benchmark.*'] }
I am not getting benchmark data in response. Is it because it has different class name than table name?
I think you should use association names, not table names in json specification.
render json: my_admin, include: { my_question_sets: :set_benchmark }
https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json
So I have a calendar controller and a view called _show.html.erb (it's a partial which is loaded into a bigger view). I'm trying to use an attribute 'starts_at' which belongs to an 'event' model object, but my html highlights it saying "cannot find starts_at".
If I try and load my main page it gives the error:
NoMethodError in Pages#main
undefined method `each' for nil:NilClass
Here is the calendar controller with my show method:
class CalendarsController < ApplicationController
def new
#calendar = Calendar.new(calendar_params)
end
def create
#calendar = Calendar.new(calendar_params)
end
private
def calendar_params
params.require(:customer_id)
end
def show
#calendar = current_customer.calendar
#events = #calendar.events
end
end
My events controller:
class EventsController < ApplicationController
before_filter :logged_in?
def new
#event = Event.new
#calendar = current_customer.calendar
end
def create
#calendar = current_customer.calendar
#event = #calendar.events.build(event_params)
if #event.save
redirect_to '/main' #'/main/#{#calendar.id}'
else
redirect_to '/compose'
end
end
Here is the _show.html.erb snippet:
<td id = "mon900" class ="freeslot" width=75 valign=top>
<p class=MsoNormal style='margin-bottom:0cm;margin-bottom:.0001pt;line-height:
normal'> </p>
<% #events.each do |e|%>
<p><%= e.starts_at %></p> <!-- It wont pick up starts_at-->
<% end %>
</td>
Here is the code where I am rendering this partial. It is being rendered in main.html.erb which belongs to my Pages controller:
<%= render partial: '/calendars/show', locals: {} %>
Pages controller:
class PagesController < ApplicationController
#before_action :require_customer, only: [:main]
def home
end
def main
end
end
Just for clarity, here are my calendar and event models:
class Calendar < ActiveRecord::Base
belongs_to :customer
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :calendar
end
The :starts_at attribute of an event is of datetime type. When I was learning how to do ruby on rails (admittedly not long ago) I was told this sort of ruby injection would work. What is the problem here? Thanks!
You'll need some validation that the user exists so something like this should work:
#controllers/pages
def main
#events = current_user ? current_user.calendar.events : []
end
Clarification; this is the above ternary operation written out:
if current_user
#events = current_user.calendar.events
else
#events = [] # an empty array, which will address "undefined method `each' for nil:NilClass"
end
This way if there is no user logged in it will return an empty array of "events". You could just as easily replace the array with a scope of actual events, such as:
#events = current_user ? current_user.calendar.events : Event.all
Similar questions have been asked before but I just can't figure this out.
So I have Model_A and Model_B. Model_B belongs_to Model_A. What I want to do is when I create Model_A is to automatically call the create method for Model B. Then a script takes over a generates a bunch of data for Model_B. I use after_create because this only has to happen once.
It needs done this way. If you wanna know the details feel free to ask...
So I got Model_A here. I just can't seem to get the right syntax in create_model_b. In the example I used I just get an error saying the method doesn't exist for Model_A.
class Model_A < ActiveRecord::Base
belongs_to :Model_B
after_create :create_model_b
...
def create_model_b
#so I tried a bunch of stuff here but none of it worked
#I need to create a Model_B which will contain the current Model_A id
#ex. self.model_b.create(model_a_id: self.id)
end
end
Model_B doesn't do anything special really:
class Model_B < ApplicationController
def create
#model_b = Model_B.new(model_b_params)
create_the_data
respond_to do |format|
if #model_b.save
#redirect
else
#uh oh
end
end
end
end
Thanks!
Two ways:
1.
class Model_A < ApplicationController
def create
#model_a = ModelA.new(model_a_params)
if #model_a.save
ModelB.create(model_a_id: #model_a.id, .....)
#create data for model B either here or with after_create (of model B)
redirect_to somewhere_awesome_path
else
# rescue error
render 'new'
end
end
2.
class Model_A < ActiveRecord::Base
after_create :create_model_b
...
def create_model_b
ModelB.create(model_a_id: id)
end
end
Your Model A contains half of an association: belongs_to :Model_B
Your Model B is missing its association to Model A.
Depending on the relationship you set up, you can complete the association with a has_one :Model_A, or has_many :Model_A, for example.
Here's Active Record documentation for reference.
When I call the the create method i get a nameError.
Failure/Error: post :create, { user: { email: 'charles#example.com',
NameError:
uninitialized constant API::V1::UsersController::UserV1Serializer
why is it adding that UsersController module to the class it is looking for? In my sessions controller I use the same exact render line and it doesn't complain. what is going on?
app/controllers/api/v1/users_controller.rb
class API::V1::UsersController < API::V1::BaseController
...
...
def create
user = User.new(user_params)
if user.save
sign_in :user, user, store: false
end
render json: user, serializer: UserV1Serializer, root: 'user'
end
app/serializers/api/v1/user_v1_serializer.rb
class API::V1::UserV1Serializer < ActiveModel::Serializer
attributes :id, :email
def attributes
hash = super
if scope == object
hash[:token] = object.authentication_token
end
hash
end
end
Instead of specifying UserV1Serializer, you need to specify the full namespaced class name API::V1::UserV1Serializer.
If you just specify the UserV1Serializer, its looking for the serializer class within current controller API::V1::UsersController::UserV1Serializer which is why you get an error as
uninitialized constant API::V1::UsersController::UserV1Serializer.
Use this instead:
render json: user, serializer: API::V1::UserV1Serializer, root: 'user'
I'm rendering a model and it's children Books in JSON like so:
{"id":2,"complete":false,"private":false, "books" [{ "id":2,"name":"Some Book"},.....
I then come to update this model by passing the same JSON back to my controller and I get the following error:
ActiveRecord::AssociationTypeMismatch (Book (#2245089560) expected, got ActionController::Parameters(#2153445460))
In my controller I'm using the following to update:
#project.update_attributes!(project_params)
private
def project_params
params.permit(:id, { books: [:id] } )
end
No matter which attributes I whitelist in permit I can't seem to save the child model.
Am I missing something obvious?
Update - another example:
Controller:
def create
#model = Model.new(model_params)
end
def model_params
params.fetch(:model, {}).permit(:child_model => [:name, :other])
end
Request:
post 'api.address/model', :model => { :child_model => { :name => "some name" } }
Model:
accepts_nested_attributes_for :child_model
Error:
expected ChildModel, got ActionController::Parameters
Tried this method to no avail: http://www.rubyexperiments.com/using-strong-parameters-with-nested-forms/
Are you using accepts_nested_attributes_for :books on your project model? If so, instead of "books", the key should be "books_attributes".
def project_params
params.permit(:id, :complete, :false, :private, books_attributes: [:id, :name])
end
I'm using Angular.js & Rails & Rails serializer, and this worked for me:
Model:
has_many :features
accepts_nested_attributes_for :features
ModelSerializer:
has_many :features, root: :features_attributes
Controller:
params.permit features_attributes: [:id, :enabled]
AngularJS:
ng-repeat="feature in model.features_attributes track by feature.id
My solution to this using ember.js was setting the books_attributes mannualy.
In controller:
def project_params
params[:project][:books_attributes] = params[:project][:books_or_whatever_name_relationships_have] if params[:project][:books_or_whatever_name_relationships_have]
params.require(:project).permit(:attr1, :attr2,...., books_attributes: [:book_attr1, :book_attr2, ....])
end
So rails checks and filters the nested attributes as it expected them to come
This worked for me. My parent model was an Artist and the child model was a Url.
class ArtistsController < ApplicationController
def update
artist = Artist.find(params[:id].to_i)
artist.update_attributes(artist_params)
render json: artist
end
private
def artist_params
remap_urls(params.permit(:name, :description, urls: [:id, :url, :title, :_destroy]))
end
def remap_urls(hash)
urls = hash[:urls]
return hash unless urls
hash.reject{|k,v| k == 'urls' }.merge(:urls_attributes => urls)
end
end
class Artist < ActiveRecord::Base
has_many :urls, dependent: :destroy
accepts_nested_attributes_for :urls, allow_destroy: true
end
class Url < ActiveRecord::Base
belongs_to :artist
end
... and in coffeescript (to handle deletions):
#ArtistCtrl = ($scope, $routeParams, $location, API) ->
$scope.destroyUrls = []
$scope.update = (artist) ->
artist.urls.push({id: id, _destroy: true}) for id in $scope.destroyUrls
artist.$update(redirectToShow, artistError)
$scope.deleteURL = (artist,url) ->
artist.urls.splice(artist.urls.indexOf(url),1)
$scope.destroyUrls.push(url.id)
Something is missing from all of the answers, which is the inputs for fields_for in the form.
The form works if you do this:
f.fields_for #model.submodel do ..
However, the form is sent as model[submodel], but that's what causes the error others have mentioned in their answers. If you try to do model.update(model_params), Rails will raise an error that it's expecting a Submodel type.
To fix this, make sure you follow the :name, value format:
f.fields_for :submodel, #model.submodel do ...
Then in the controller, make sure you put _attributes on your params:
def model_params
params.require(:model).permit(submodel_attributes: [:field])
end
Now the save, update, etc. will work fine.
Wasted several days trying to figure out how to use accepts_nested_attributes with Angular, and the issue is always the same: Rails whitelist will not allow the variables into the params hash. I've tried every single different whitelisting syntax that everyone said on SO and other blogs, tried using :inverse, tried using habtm and mas_many_through, tried manually rolling my own solution but that wont work if the whitelist wont allow params through, tried doing what http://guides.rubyonrails.org says about 'Outside the Scope of Strong Parameters', tried removing whitelisting all together which isnt really an option but it causes other problems anyways. Not sure why rails 4 strong parameter whitelisting wont allow arbitrary data thru, thats a huge problem especially if accepts_nested_attributes doesn't work either.... I guess we are left to just create/delete all associations on a separate page/form/controller and look like an idiot making my end users use several forms/pages to do something that should be easily doable on 1 page with 1 form. Ya know, usually I expect Angular to screw me, but this time Angular worked quite well and it was actually Rails 4 that screwed me twice on 1 issue that should be very straightforward.