Form with multiple different ids, how to save - ruby-on-rails

Don't know how to save a question form that will have 2 ids, event id and user id
User.rb
class User < ApplicationRecord
has_many :questions
has_many :answers
end
Event.rb
class Event < ApplicationRecord
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions
end
Question.rb
class Question < ApplicationRecord
belongs_to :user
belongs_to :event
has_many :answers, dependent: :destroy
accepts_nested_attributes_for :answers
end
Answer.rb
class Answer < ApplicationRecord
belongs_to :user
belongs_to :question
scope :sorted, ->{ order(created_at: :asc) }
end
questions_controller.rb
def new
#question = current_user.questions.build
end
def create
#question = Question.new(question_params)
#question["user_id"] = current_user.id
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
I have the standard form generated with the scaffold, but I cant piece together what I am missing from my limited knowledge and experience in rails on how to get every question that a user makes to be linked to a specific created event and show which user created that particular question (I would assume that each question entry will need a user_id and a event_id column)
<%= form_with(model: question, local: true) do |form| %>
<%= form.label :body %>
<%= form.rich_text_area :body %>
<%= form.submit 'Create Question' %>
<% end %>
Updated for Error:
When I try to create a question, each entry requires an event_id (an event has many questions) and a user_id (I want to show who created that question).
Are my models and controllers setup correctly? When I try to create a question for an event, the error "Event must exist" occurs
Updated with ERD pic ( Not sure if I should just have users or seperate into creators and users )
ERD after reading up on last update
I begun reading up more on data modelling and I came up with this ERD...I am still not very sure on achieving 3NF and setting up the relationships, and how to translate it to rails models but would be great to have comments on my ERD so I can learn.
Creator creates events that users can join. Creator creates questions that users can post answers. Each event has many questions and each question can have many answers.

If I understood you correctly, you need nested resources to achieve your goal. It means, that questions are nested inside events, like 'parent' event has 'children' questions. At first, change your routes:
resources :events do
resources :questions
end
Run rake routes in terminal and you will see new routes with :event_id parameter. Now on events#index you can add link_to 'Questions about this event', event_questions_path(event) near each event, the link will lead you to events/1/questions (1 is id of the event). In QuestionsController you have new parameter, event_id, which you can use to find needed Event or to assign as foreign key.
Change you form
<%= form_with(model: [#event, #question], local: true) do |form| %>
<%= form.label :body %>
<%= form.rich_text_area :body %>
<%= form.submit 'Create Question' %>
<% end %>
and controller a bit to work with nested routes
def new
#event = Event.find(params[:event_id])
#question = event.questions.build
end
def create
#event = Event.find(params[:event_id])
#question = event.questions.build(question_params)
#question.user_id = current_user.id
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render :show, status: :created, location: #question }
else
format.html { render :new }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
Also, you should remove the line accepts_nested_attributes_for from Event and Question models, since you never use it

Related

Ruby on Rails Create A-B relationship instance when A has_and_belongs_to_many B and vice versa

I have two example classes:
# book.rb
class Book < ApplicationRecord
has_and_belongs_to_many :tag
end
# tag.rb
class Tag < ApplicationRecord
has_and_belongs_to_many :book
end
If I understand correctly, this means that I could eventually have tags with many books and books with many tags. Right now, I want to assign tags to books when I create books.
I have a multiselect dropdown on the books/new page to send these tags to the controller, but I don't know what to do once they reach the controller.
<div>
<%= form.label :tags, style: "display: block" %>
<%= select_tag :tags, options_from_collection_for_select(#tags, :id, :name), multiple: true, prompt: "Select Tags" %>
</div>
Controller looks like this:
def create
#Book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to book_url(#book), notice: "Book was successfully created." }
format.json { render :show, status: :created, location: #book }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
When I make a book with the form, it doesn't have any tags when I inspect the latest book in the rails console.
I tried putting #book.tag.build(tag_ids: book_params["tags"]) into thee create method but that didn't work and I feel like I'm barking up the wrong tree.
If you want to assign existed tags to new book, you can use select method in your form
<%= form.select :tag_ids, Tag.all.map { |p| [p.name, p.id] }, multiple: true, prompt: "Select Tags" %>
Of course you can pass #tags from controller instead of Tag.all
If you use strong params, you need to add this param there, something like
params.require(:book).permit(
# existed params,
tag_ids: []
)
You may add accepts_nested_attributes_for :tags in the Book model. This way, when the form is submitted, #book.save will create the associations for tags from params[:books][:tags]
# book.rb
class Book < ApplicationRecord
has_and_belongs_to_many :tag
accepts_nested_attributes_for :tags
end
Reference: https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Rails passing nested attributes but not saving associated object

OK so I've been at this for 6 plus hours and scoured every variation on this question, but nothing I do works!
I'm building a fairly simple family tree app for a friend. There are two models in question, Person and Relationship. Whenever you build a new Person (after the very first one which is handled separately), it should also build a Relationship which is essentially a join table that has a person_1 attribute, a person_2 attribute, and a relationship_type attribute such as "mother".
When I build a new person, that person is saved to the database just fine, but the associated relationship is not. I can see that all of the necessary params are being passed in, and I've read as much as I can about nested params, but no dice.
My code is a big old mess and I'm a beginner so please ignore any unrelated code weirdness.
My Person model (this centers around the out_relationships resource)
class Person < ApplicationRecord
has_many :out_relationships, :class_name => "Relationship", :foreign_key => "person_1_id"
has_many :in_relationships, :class_name => "Relationship", :foreign_key => "person_2_id"
belongs_to :user, optional: true
accepts_nested_attributes_for :out_relationships
end
My Relationship model:
class Relationship < ApplicationRecord
belongs_to :person_1, :class_name => "Person"
belongs_to :person_2, :class_name => "Person"
end
My form:
<%= form_for #person, :url => create_relative_path, html:{method:'post'} do |form| %>
<%= fields_for :out_relationships do |builder| %>
<div class="form-group">
<%= builder.label :relationship_type, "Relationship to #{#root_person.first_name}" %>
<%= builder.text_field :relationship_type, class: "form-control" %>
<%= builder.hidden_field :person_1, value: #person_1.id %>
<%= builder.hidden_field :person_2, value: #person_1.id %>
</div>
<% end %>
I set person_1 and person_2 to the same value just as a test. That's kind of unrelated to the problem, I think, and shouldn't affect what's happening. Anyways...
My controller:
def new
#root_person = Person.find(params[:id])
#person = Person.new
#user = current_user
#root_person.out_relationships.build
end
# GET /people/1/edit
def edit
end
def create
#root_person = Person.find(params[:id])
#person = Person.new(person_params)
#user = current_user
respond_to do |format|
if #person.save
format.html { redirect_to #person, notice: 'Person was successfully created.' }
format.json { render :show, status: :created, location: #person }
else
format.html { render :new }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
def person_params
params.require(:person).permit(:first_name, :middle_name, :last_name, :birth_date, :birth_city,
:birth_country, :current_city, :current_country, :profession, :maiden_name, :marital_status, :patel_type,
out_relationships_attributes: [:person_1, :person_2, :relationship_type])
end
Anyways, that's more of a mess than I even thought, thanks to anyone still reading, would love a nudge in the right direction!

Rails nested attribute form: error when creating an object with a many-to-many relationship

My project has a many-to-many relationship among the following classes:
class Project < ApplicationRecord
has_many :project_software_processes, inverse_of: :project, :dependent => :delete_all
has_many :software_processes, through: :project_software_processes
accepts_nested_attributes_for :project_software_processes
attr_accessor :project_attributes
end
..
class SoftwareProcess < ApplicationRecord
has_many :project_software_processes
has_many :projects, through: :project_software_processes
end
--
class ProjectSoftwareProcess < ApplicationRecord
belongs_to :software_process
belongs_to :project
end
- -
I am using nested forms to create an instance of project so the user can pick as many software processes as they want:
<div class="field">
<%= f.label :software_processes %>
<%= f.fields_for :project_software_processes do |project_software_process| %>
<%= project_software_process.select(:software_processes_id, #processes.collect { |s| [s.name, s.id] } ) %>
<%= project_software_process.link_to_remove "Remove this SoftwareProcess" %>
<% end %>
<p><%= f.link_to_add "Add a process", :project_software_processes %></p>
</div>
Problem is, when I click on submit I get the following validation message:
Project software processes software process must exist
It took me a bit to understand what the error actually means. I guess it is complaining about the existence of software process class when attempting to create an instance of project software process. However, there are instances of project software process, the user can even select them in the dropdown selects. Not sure if that's the issue though.
Here's some snippets from my project controller:
def create
#project = Project.new(project_params)
#project.user = current_user
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render :show, status: :created, location: #project }
else
#processes = SoftwareProcess.all
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
--
def project_params
params.require(:project).permit(:name, :user_id, project_software_processes_attributes: [:software_process_id, :_destroy])
end
--
Here's what is being sent in the params:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"[FILTERED]", "project"=>{"name"=>"aas", "project_software_processes_attributes"=>{"1484020859023"=>{"software_processes_id"=>"1", "_destroy"=>"false"}, "1484020859693"=>{"software_processes_id"=>"4", "_destroy"=>"false"}}}, "commit"=>"Create Project"}
The issue is on the name of the select method, should be software_process_id rather than software_processes_id. Try changing it to:
<%= project_software_process.select(:software_process_id, #processes.collect { |s| [s.name, s.id] } ) %>
Also, Ryan Bate's software is pretty outdated by now and nested_forms gem is not an exception. I've seen people recommending Cocoon as a suitable alternative. Take a look here
Cheers.

undefined method `first_name' for nil:NilClass - No way this is Nil

User has many comments, comment belongs to many users. How do I fix this error?
undefined method `first_name' for nil:NilClass
when I try to do
<h3>Comments</h3>
<% #guide.comments.each do |comment| %>
<div>
<p><%= comment.user.first_name %></p>
</div>
<% end %>
user.rb
has_many :comments
comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
end
comments migration (I added a user_id column):
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.text :body
t.integer :user_id
t.timestamps
add_foreign_key :comments, :guides
end
end
end
comments controller:
def create
#comment = Comment.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment }
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
long time ago but this could be usefull for someone with the same error.
you should delegate the first_name to the user and allow nil
class Comment < ActiveRecord::Base
belongs_to :user
delegate :first_name, to: :user, allow_nil: true, prefix: true
end
then call it in you view with
<h3>Comments</h3>
<% #guide.comments.each do |comment| %>
<div>
<p><%= comment.user_first_name %></p>
</div>
<% end %>
if there is no user, then it will be display nothing and not raise an exception
Ensure that you user model has a first_name attribute. Then confirm that your comment record (s) actually have a user associated with them. You may not have the user_id column whitelisted in the Comment class, so the user is not set
class Comment
attr_accessible :user_id, ...
end
Or in rails 4 you have strong parameters instead of attr_accessible
How is attr_accessible used in Rails 4?
I'm guessing that you did not set the user id while creating the comment object. can you try the following code?
def create
#comment = current_user.comments.new(comment_params)
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #comment }
else
format.html { render action: 'new' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
Model didn't find comment.user. It may be the comment's user_id havn't been set or the user_id didn't apper in "user" table "id" column. You can print the comment id and comment.user_id and check in DB.
Do you set:
has_many :comments, :dependent => :destroy
Or it could hapeen you delete the user but the user's comments remain, then for these comments, comment.user is null.
Are you sure your user_id is set when you create a comment ?
Maybe this simple line is missing in your controller
#comment.user = current_user
In order to be sure to have a user in your comment, you should add in your Comment model
validates_presence_of :user_id
and in your User model
has_many :comments, :dependent => :destroy
Defining methods in Ruby is very simple. To solve your problem, define
class << nil
def first_name; "John" end
def last_name; "Doe" end
end
And the error will disappear. All the nil objects are now named "John Doe".

Simple Rails Blog - Post Comments are ignoring Post ID [outside comment array in parameters]

I have a simple blog I'm building with Rails, and I'm following the normal rails getting started guide (http://guides.rubyonrails.org/getting_started.html). I'm setting up a form for comments inside my post's show method, but when I save, it's not saving the page_id in the comment record.
= form_for [#post, #post.comments.build], :remote => true do |f|
.field
= f.label :name
= f.text_field :name
.field
= f.label :extra_field, #page.rsvp_extra_field
= f.text_area :extra_field
.actions
= f.submit 'Save'
post.rb
class Post < ActiveRecord::Base
has_many :comments, dependent: :destroy
attr_accessible :comments_attributes, :comment_attributes
accepts_nested_attributes_for :comments, :allow_destroy => true
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
attr_accessible :extra_field, :name, :post_id, :phone
end
I see in the rails console that it's posting it, but putting NULL for post_id. Any thoughts?
EDIT
I didn't change my create method at all:
def create
#comment = Comment.new(params[:comment])
respond_to do |format|
if #comment.save
format.html { redirect_to post_url, notice: 'Comment was successfully created.' }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
EDIT 2
I think my parameters are nested when I don't want them to be... any ideas how to get this "post_id" inside of the "comment" array?
Parameters: {"utf8"=>"✓", "authenticity_token"=>"eqe6C7/ND35TDwtJ95w0fJVk4PSvznCR01T4OzuA49g=",
"comment"=>{"name"=>"test", "extra_field"=>""}, "commit"=>"Save", "post_id"=>"8"}
Because your method create in CommentsController, create object Comment unrelated to the object Post. has_many relation provide 3 methods for related objects:
post.comments.create
post.comments.create!
post.comments.build(eq new method)
Add in your CommentsController this:
...
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
end
...

Resources