I'm working on a rails project and I am trying to add a new model, user_information and link it to a user model.
When trying to set up the create function in the user_controller, I'm struggling with how I can add the information from user_information and pass it as a parameter within the user object.
Any advice on nesting these parameters would be very helpful
It sounds like you're looking for nested attributes (http://guides.rubyonrails.org/form_helpers.html#nested-forms).
Model:
class User < ActiveRecord::Base
has_one :user_information
accepts_nested_parameters_for :user_information
class UserInformation < ActiveRecord::Base
belongs_to :user
Now you can pass nested parameters to create child associations in one step:
User.create(email: "<email>", user_information_attributes: { name: "john", etc.. })
(Note the _attributes suffix)
You are looking for nested attributes.
You should take a look at
gorails
nested forms and rails 4
Related
I have two models
class Information < ActiveRecord::Base
belongs_to :study
validates_presence_of :email
end
and
class Study < ActiveRecord::Base
has_many :informations
accepts_nested_attributes_for :informations
end
I show up a form of study which contains few fields for the informations and i want to validate presence of those fields. Only on validation success i wanted to save the study field values as well and i wanted to show errors if the validation fails. How can i do this? Thanks in advance.
You write validations in the models that you require, as normal. So if you need to validate presence of field foo in the Information class you'd just write validates_presence_of :foo in that class. Likewise validations for Study fields just go in the Study class. With nested attributes, when you update a Study instance from a params hash that contains nested attributes, it'll update the Information instance(s) too, running validations in passing. That's what the accepts_nested_attributes_for call is doing - it's giving "permission" for the appropriate bits of a params hash to be used in this way.
You can use reject_if to only reject new nested records should they fail to meet criteria. So I might let someone create a Study and only create one or more nested Information instances associated with that Study if they'd filled in field(s) in the form, but if they left them blank, the nested stuff wouldn't be created and saved (so you don't get pointless blank associated records). The Study would still be saved. For example:
accepts_nested_attributes_for(
:informations,
reject_if: proc() { | attrs | attrs[ 'title' ] .blank? }
)
This and more is covered in the API documentation here:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Beware that nested fields are intended for existing records only. If you were creating a new Study instance in a new/create action with no Information instances associated, you won't see any nested form fields for your Information class at all - when you might be expecting just one, for a blank new item. This can be very confusing if you aren't ready for it! You'll need to manually add a new Information instance to your Study instance in the controller or similar for the 'new' and 'create' actions, e.g. using before_filter :create_blank_object, only: [ :new, :create ], with, say:
def create_blank_object
#study = Study.new
#study.informations << Information.new
end
you can use validates_presence validation available in rails other wise you can write before_create or before_save callback method. write validation logic inside the before_create or before_save callback method.
Check out the API Doc for validates_associated:
Validates whether the associated object or objects are all valid. Works with any kind of association.
If you call a method on the parent object which runs validations (e.g. save), the validation on the associated objects will be called as well.
When you have a rails resource defined rails seems to automatically create a params entry of attributes for that resource. e.g. if my model Lesson has a subject attribute and I post subject=Maths it automatically creates the param[lesson] = { subject: 'Hello' }. The problem I am having is getting nested attributes to appear within this created lesson array.
I'm using mongoid as my backend and have an association on Lesson called activities. The code looks like this:
class Lesson
include Mongoid::Document
field :subject, type: String
embeds_many :activities, class_name: 'LessonActivity' do
def ordered
#target.sort { |x, y| x.display_order <=> y.display_order }
end
def reorder!
#target.each_with_index { |val, index| val.display_order = index }
end
end
accepts_nested_attributes_for :activities
However I can't work out how I access this activities from within params.require(:lesson).permit :activities
I can access it via params.permit(:activities) but that feels a bit messy
I've done some digging and found out what's going on with this.
It all comes from a rails feature, the Param wrapper, details and api. Which configured for json will automatically pass the attributes of the model into a param of the model name (in this case Lesson).
The attributes of the model that will be populated based on how the model responds to the method attribute_names so this gives two routes to achieve the aims of the question.
1 - Instruct my controller to include activities as part of Lesson parameters, e.g. using this method:
class Api::LessonsController < Api::ApiController
wrap_parameters Lesson, include: Lesson.attribute_names << :activities
2 - Update the attiribute_names method for the model to include :activities
I'm still left with a couple of things to resolve, namely the reason associations aren't part of attribute_names on Mongoid and if overriding it to include attribute names is a bad idea.
Basing on the params you provided for your JSON POST request, you will need the following code to whitelist the params you need:
def activities_params
params.require(:activities).permit(:title, :display_order, :content, :time)
end
The params forwarded by your JSON POST request did not have the :activities hash as a value to the :lesson key so whitelisting the params you need is simple like above.
I think you may have answered you question here:
"how I can make it part of lessons key or why I can't. I'm not passing a lesson parameter "
If I read that correctly, you are not passing the lesson param, just a hash of Activities?
That would explain why you can access
params.permit(:activities)
but not
params.require(:lesson).permit :activities
I try to add an existing child model to the parent via ajax, by only providing the new id.
My models:
class User < ActiveRecord::Base
has_many :books
attr_accessible :books
end
class Book < ActiveRecord::Base
belongs_to: :user
end
In my html form the user is viewing a User and it can select an existing Book to add. This will result in an Ajax request. I would like to only send the new Book, and not all the already assigned books. E.g. the User model has already Books 1 and 2, and now to user selects Book 3 to also be assigned.
I can not find the correct structure of the parameters. If I use the following, it completely overwrites the current associations.
// Ajax parameters
user[books] = [3]
How should is build the parameters such that it only adds the new book? And as a follow-up, how can I build the parameters to remove only a single association?
You have to send only one "book_id" in request.
Then in controller:
# assuming params hash is { :book_id => 3 }
#book = Book.find params[:book_id]
#user.books << #book
...
# Removing
#user.books.delete(#book)
# In `update` action
params[:user][:book_ids] = (#user.book_ids + params[:user][:book_ids]).flatten
#user.update_attributes(params[:user])
This post seems good for how to create two models with one form. But how would you do it if the two models share one or more of the attributes?
That post seems fairly outdated, I would recommend using accepts_nested_attributes_for and fields_for in your form instead. That said, overlapping attributes should probably be set in your model's callbacks. Say you want a project's name to be automatically set to first task's name.
class Project < ActiveRecord::Base
has_many :tasks
accepts_nested_attributes_for :tasks
before_validation :set_name_from_task
private
def set_name_from_task
self.name = tasks.first.name
end
end
If your 2 models are completely unrelated, you can assign certain params to them directly in the controller.
def create
#foo = Foo.new(params[:foo])
#bar = Bar.new(params[:bar])
#bar.common_attr = params[:foo][:common_attr]
# validation/saving logic
end
Although this is not a great practice, this logic should ideally be moved into models.
Alright, I'm going to try to explain this as best as possible:
I have two models
employer.rb
class Employer < ActiveRecord::Base
has_many :listings
end
listing.rb
class Listing < ActiveRecord::Base
belongs_to :employer
end
Employers login through the employers_controller and listings are created through listings_controller
I'm having trouble getting the id from the employer table being inserted into the employer_id column in each individual created listing. I hope this makes sense. If anyone has any advice, it would be much appreciated. I have a feeling this is because I'm doing this outside of the employer_controller, but not sure.
Thanks!
1) If you are not dealing as nested resource then
When you render the new action of Listing controller, you know for which employer (#employer) you want to create the listing.
So render a hidden field for employer_id using a hidden_field or hidden_field_tag
hidden_field_tag 'employer_id', #employer.id()
2) If you are dealing as nested resource and your route looks something like
/employers/:employer_id/listings/new / (Get) && /employers/:employer_id/listings
Then in create action
#employer = Employer.find(params[:employer_id])
#employer.Listing.new(params[:listing]
I think you have the employer id in your session, since they need to login to create the listing. I won't use the param from the view, then its easy for one employer to create a listing as if it was created by another employer, just by changing the id value in your hidden field. Here's what is possibly a better approach:
#employer = Employer.find(current_user.id)
#employer.Listing.new(params[:listing]