Rails: Comment Model - Connected to a Micropost_id and User_id - ruby-on-rails

EDIT
The Routing Error has disappeared thanks to carlosramireziii who helped changed the Form to
<%= form_for (#micropost) do |f| %>
<%= fields_for :comments do |ff| %>
<%= ff.text_area :content %>
<% end %>
<div class="CommentButtonContainer">
<%= f.submit "Comment" %>
</div>
<% end %>
But the issue now is that the post will not save, any suggestions?
I am currently making a comments model that is connected to a:
micropost_id :integer and user_id :integer
The problem that I continue to receive is when I post something I get this in return:
Routing Error
No route matches [POST] "/microposts/comments"
This is my routes.eb
Project::Application.routes.draw do
resources :microposts do
resources :comments
end
This is the Comment Form
<%= form_for([#micropost, #micropost.comments.new]) do |f| %>
<%= f.text_area :content %>
<div class="CommentButtonContainer">
<%= f.submit "Comment" %>
</div>
<% end %>
This is the Comment Template
<%= div_for comment do %>
<div class='UserCommentContainer'>
<div class='UserComment'>
<div class='UserName sm'>
Anonymous
</div>
<div class='UserCommentText'>
<%= comment.content %>
</div>
</div>
</div>
<% end %>
And finally this is what is inside the Micropost
<div id='CommentContainer' class='Condensed2'>
<div class='Comment'>
<%= render "comments/form" %>
</div>
<div id='comments'>
<%= render #micropost.comments %>
</div>
</div>
Everything else related to the comments model and controller I have posted below, I have been mulling around this for a long time and could really use help, thank you!
Comments Model
class Comment < ActiveRecord::Base
attr_accessible :content
belongs_to :micropost
validates :content, presence: true, length: { maximum: 140 }
validates :user_id, presence: true
validates :micropost_id, presence: true
default_scope order: 'comments.created_at DESC'
end
Micropost Model
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :comments
validates :user_id, presence: true
end
User Model
class User < ActiveRecord::Base
has_many :microposts
has_many :replies, :through => :microposts, :source => :comments
end
Comments Controller
class CommentsController < ApplicationController
def create
#comment = #micropost.comments.new(params[:comment])
if #comment.save
redirect_to #user
else
redirect_to #user
end
end
end
User Controller
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#micropost = Micropost.new
#comment = #micropost.comments.new
#microposts = #user.microposts.paginate(page: params[:page])
end
end

I believe the problem is that you are trying to save a new comment on a micropost that hasn't been saved yet. Since you have your comments routes nested underneath the microposts routes, the micropost needs to exist before you can create a new comment.
If you want to create both objects in the same form, you need to use nested model attributes.
Micropost
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :comments
accepts_nested_attributes_for :comments
validates :user_id, presence: true
end
Form
<%= form_for(#micropost) do |f| %>
<%= f.fields_for :comments do |ff %>
<%= ff.text_area :content %>
<% end %>
<div class="CommentButtonContainer">
<%= f.submit "Comment" %>
</div>
<% end %>

Related

Rails nested form fields_for doubled in edit

I'm trying to create a form where users can see a list of their friends and add or remove friends from lists (sort of like facebook's groups). For the create list view, I can get the form working properly, but for the edit view I'm having trouble with check_box syntax and fields_for.
routes.rb
resources :friendships
resources :friend_lists
class User < ActiveRecord::Base
has_many :friendships, dependent: :destroy
has_many :friends, through: :friendships
has_many :friend_lists
has_many :flist_memberships, class_name: 'FlistMembership', foreign_key: 'member_id', dependent: :destroy
...
end
class FriendList < ActiveRecord::Base
belongs_to :user
has_many :flist_memberships
has_many :members, through: :flist_memberships
accepts_nested_attributes_for :members, allow_destroy: true
accepts_nested_attributes_for :flist_memberships, allow_destroy: true
end
class FlistMembership < ActiveRecord::Base
belongs_to :friend_list
belongs_to :member, class_name: 'User'
end
class FriendListsController < ApplicationController
def new
#friend_list = current_user.friend_lists.build
#friends = current_user.friends.paginate(page: params[:page])
#friend_list.flist_memberships.build
end
def create
#friend_list = current_user.friend_lists.build(friend_list_params)
respond_to do |format|
format.html {
if #friend_list.save
flash[:success] = "Friends list created!"
redirect_to friendships_path
else
render 'friend_lists/new'
end
}
end
end
def edit
#friend_list = FriendList.find(params[:id])
#friends = current_user.friends
end
def update
#friend_list = FriendList.find(params[:id])
if #friend_list.update_attributes(friend_list_params)
flash[:success] = "Friend list updated!"
redirect_to friend_list_path(#friend_list)
else
render 'edit'
end
end
private
def friend_list_params
params.require(:friend_list).permit(:name, flist_memberships_attributes: [:id, :member_id, :friend_list_id])
end
end
new.html.erb
<%= form_for #friend_list do |f| %>
<%= f.label :name, "Name for this friends list:" %>
<%= f.text_field :name %>
<% #friends.each do |friend| %>
<%= f.fields_for :flist_memberships do |m| %>
<%= m.check_box :member_id, {}, friend.id %>
<%= m.label :member_id, friend.name %>
<% end %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
edit.html.erb
<%= form_for #friend_list do |f| %>
<%= f.label :name, "Name for this friends list:" %>
<%= f.text_field :name %>
<%= f.fields_for :flist_memberships do |m| %>
<%= m.collection_check_boxes(:member_id, #friends.all, :id, :name) %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
new.html.erb renders and saves the selected items correctly, but I can't figure out how to write the edit form so that it doesn't iterate through the collection once for each item in the collection (both with collection_check_boxes and the form as written in new.html.erb). Trying
<%= f.fields_for :flist_memberships do |m| %>
<%= m.check_box :id %>
<%= m.label :id, :name %>
<% end %>
in the edit form just passes the string "id" as params rather than pulling the id of each member in the collection, and I'm not sure how to pull the individual item's name/id without iterating over the collection, which goes back to the multiple renders problem. I'm using cocoon elsewhere in the project, but for this I'd rather present the user a list of all options and allow them to check a box for each one, rather than having to manually add each item on the list. If there is a way to do it with cocoon, I'd be happy to hear it.

Rails ActiveRecord 'slightly' complex Association

I would like to structure a music web-application's database to use the following structure.
Song(Attributes: name, year, etc(any other relevant attributes))
has many Performances
has many Performers (through Performances)
has many Artists (through Performers)
Performance(Foreign key:Song_id, Attributes: enum version: [:official, :remix, :cover])
belongs to Song
has many Performers
has many Artists (through Performers)
Performers (Foreign keys: Performance_id, Artist_id, Attributes: enum role: [:main_artist, :collaborating_artist, :featuring_artist, :versus ])
has one Song (through Performers)
belongs to Performances
belongs to Artist
Artist(Attributes: name, DOB, etc.(any other relevant attributes))
has many Performers
has many Performances (through Performers)
has many Songs (through Performances)
So that upon saving a Song, i can have all the rest of the other tables populated too, that is via the associations, however, so far i had the following implementations:
Song model:
class Song < ActiveRecord::Base
#Added to make the db more logical and ended up complicated
has_many :performances
has_many :performers, :through => :performances
has_many :artists, :through => :performers
accepts_nested_attributes_for :artists, allow_destroy: true
accepts_nested_attributes_for :performers, allow_destroy: true
accepts_nested_attributes_for :performances, allow_destroy: true
validates :name, :presence => true, uniqueness: true
validates :year, :presence => true
end
Performance model:
class Performance < ActiveRecord::Base
enum version: [:official, :remix, :cover]
belongs_to :song
has_many :performers
has_many :artists, :through => :performers
end
Performer model:
class Performer < ActiveRecord::Base
enum role: [:main_artist, :collaborating_artist, :featuring_artist, :versus]
belongs_to :artist
belongs_to :performance
has_one :song, :through => :performers #this upsets the back hairs!
end
Artist model:
class Artist < ActiveRecord::Base
#Added to make the db more logical and ended up complicated
has_many :performers
has_many :performances, :through => :performers
has_many :songs, :through => :performances
validates :name, :presence => true, uniqueness: true
end
Song controller
class SongsController < ApplicationController
before_action :find_song, only: [:show, :edit, :update, :destroy]
def index
...
end
def new
#song = Song.new
#song.performers.build
#song.performances.build
#genres = Genre.all #.map{|c| [ c.name, c.id ] }
#countries = Country.all.map{|c| [ c.name, c.id ] }
end
def create
#song = Song.new(song_params)
if #song.save
redirect_to #song, notice: 'Successfully added a song.'
else
render 'new'
end
end
def show
...
end
def update
if #song.update(song_params)
redirect_to #song, notice: 'Successfully updated the song.'
else
render 'edit', notice: 'Unable to save the changes'
end
end
def edit
...
end
def destroy
...
end
private
def song_params
params.require(:song).permit(:name, :year, artists_attributes: [ :id, :name, :DOB], performances_attributes: [:id, :version, :song_id], performers_attributes: [:id, :performance_id, :artist_id])
end
def find_song
#song = Song.find(params[:id])
end
end
new.html.erb
<div class="content">
<%= form_for #song, html: { multipart: true } do |f| %>
<% if #song.errors.any? %>
<div>
<%= #song.errors.count %>
Prevented this song from saving
<ul>
<% #song.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name, class: "input-field" %>
</div>
<div class="song-version">
<div class="field">
<%= f.fields_for :performances do |performances_for_form| %>
<%= render 'performances_fields', f: performances_for_form %>
<% end %>
</div>
</div>
<div class="performers-fields">
<h2>Artist(s)</h2>
<div class="field">
<%= f.fields_for :performers do |performers_for_form| %>
<%= render 'performers_fields', f: performers_for_form %>
<% end %>
</div>
</div>
<div class"btn">
<%= f.submit %>
</div>
<% end %>
</div>
Performance partial
<fieldset>
<div class="field">
<%= f.label :version %>
<%= f.select :version, Performance.versions.keys %>
</div>
</fieldset>
Performer partial
<fieldset>
<h2>Artists Details</h2>
<div class="field">
<%= f.fields_for :artists do |artists_for_form| %>
<%= render 'artist_fields', f: artists_for_form %>
<% end %>
</div>
<div class="field">
<%= f.label :artists_role %>
<%= f.select :role, Performer.roles.keys %>
</div>
</fieldset>
Artist partial
<fieldset>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :DOB %>
<%= f.datetime_select :DOB, :ampm => true, :minute_step => 15 %>
</div>
</fieldset>
When I fill out all the data on the form, upon saving, only the Song's attributes and Performances attributes are saved to the database, while the Performers & Artists are never saved, neither is the connection established, my questions are:
Is this structure logically correct? If not what is it that is being incorrectly done.
Is the new, create methods of songs_controller wrongly implemented and thus, the reason for not being able to save the data appropriately?
What right and or best practices could be employed developing this type of web-application with this kind of database structure?
Or is there any other better or succinct database structure that would better be followed rather than this?

How to save tag with many to many relationship in ActiveRecord

I'm trying to create a video with many-to-many tags. So, each tag would be each row in Tag table. I'm not sure I have to do it myself or Rails has some magic that could do that?
Here's my model.
class Video < ActiveRecord::Base
belongs_to :user
has_many :video_tags
has_many :tags, through: :video_tags
end
class Tag < ActiveRecord::Base
end
class VideoTag < ActiveRecord::Base
belongs_to :video
belongs_to :tag
end
Here's my form
<%= form_for(#video, html: { class: "directUpload" }, multipart: true) do |f| %>
<% if #video.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#video.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #video.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :path %><br>
<%= f.file_field :path%>
</div>
<div class="field">
<%= f.label :tags %><br>
<%= f.text_field :tags %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And here's my Controller
class VideosController < ApplicationController
def show
#videos = Video.where(user_id: params[:user_id])
end
def new
#video = Video.new
#s3_direct_post = S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: 201, acl: :public_read)
end
def create
#video = Video.new(video_params)
#video.user_id = 1
if #video.save
redirect_to #video
end
end
private
# Use callbacks to share common setup or constraints between actions.
# Never trust parameters from the scary internet, only allow the white list through.
def video_params
params.require(:video).permit(:title, :path, :tags)
end
end
But then I got this error which I thought I must have missed something. I just want to have tag separated by space.
Started POST "/videos" for ::1 at 2015-04-07 00:21:11 -0400
Processing by VideosController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"authenticity_token",
"video"=>{"title"=>"asdfasdf",
"path"=>"//s3.amazonaws.com/uploads/token/57203191c21df0cacf98f3fa9340f4.mp4",
"tags"=>"test "}, "commit"=>"Create Video"}
Completed 500 Internal Server Error in 3ms
NoMethodError (undefined method `each' for "test ":String):
app/controllers/videos_controller.rb:14:in `create'
My following answer will help you
https://stackoverflow.com/questions/22611372/rails-4-paperclip-and-polymorphic-association
you can use accept_nested_attributes_for and,
<%= f.fields_for :tags, #_your_tags do |t| %>
instead of
<div class="field">
<%= f.label :tags %><br>
<%= f.text_field :tags %>
</div>
Your association would be like this:
class Video < ActiveRecord::Base
belongs_to :user
has_many :video_tags
has_many :tags, through: :video_tags
end
class VideoTag < ActiveRecord::Base
belongs_to :video
belongs_to :tag
end
class Tag < ActiveRecord::Base
has_many :video_tags
has_many :videos, through :video_tags
end
I found this wiki pretty helpful. And yes rails has a special way to handle this. These are called nested models or you can also user nested_form gem

Comment attribute won't save in Rails?

I'm trying to save the comment text section of a comment and it won't save for some reason.
I'm checking my server outputs, and the comment attribute is set, but when actually saved, it turns out as NIL.
Originally it has "comment"=>{"comment"=>"hello dude"}, "commit"=>"Sbmit Comment"
But in the saving it saves NIL.
Here's my form for comments
<div class="container">
<% if user_signed_in? %>
<%= form_for([#answer, #comment]) do |f| %>
<p>
<%= f.label :comment %>
<%= f.text_area :comment, :cols => "50", :rows => "30"%>
</p>
<p>
<%= f.submit "Submit Comment" %>
</p>
<% end %>
<% else %>
<p> <em>You must be signed in to comment</em> </p>
<% end %>
</div>
Here's my comments controller
class CommentsController < ApplicationController
def create
#answer = Answer.find(params[:answer_id])
#comment = #answer.comments.new(params[:comments])
#comment.writer = current_user.username
#comment.save
redirect_to question_path(#answer)
end
end
And here's my model.
class Comment < ActiveRecord::Base
belongs_to :answer
attr_accessible :anonymous, :comment, :writer, :votes
end
Heres my answers model
class Answer < ActiveRecord::Base
has_many :comments, dependent: :destroy
belongs_to :question
attr_accessible :anonymous, :answer, :commenter, :votes, :comments_attributes
end
Any ideas?
EDIT: I've used params[:comment], however, it says I cannot mass assign attributes to answer, even though answer has attr_accessible: :comments_attributes
In this line #comment = #answer.comments.new(params[:comments]) change :comments to :comment
You are trying to save with params[:comments] in your controller but what is being passed would respond to params[:comment]

Undefined local variable or method for Class after pagination

I have numerous paginations in my application; they all look alike; all but one work fine. The one broken one breaks at the very last step. The details are below. I have 3 classes- Micropost, Category, Selection. I have a link that points to details_controller, which renders a view /selections/detailslist which in turn renders the partial /selections/details. The code successfully gets to the last view and breaks in the third line, with the error message "undefined local variable or method `micropost' for #<#< Class: ...." Any help here would be greatly appreciated.
class Micropost < ActiveRecord::Base
attr_accessible :content, :category_content, :selection_content, :category_id, :selection_id, :user_id
belongs_to :user
belongs_to :category
belongs_to :selection
validates :user_id, presence: true
validates :category_content, presence: true
validates :selection_content, presence: true
end
class Category < ActiveRecord::Base
attr_accessible :content, :id, :count, :c_count
has_many :selections
has_many :microposts
end
class Selection < ActiveRecord::Base
attr_accessible :content, :count, :category_id
belongs_to :category
has_many :microposts
end
view with link
<li>
<span class="selection">
<%= selection.content %>
&nbsp &nbsp
( <%= selection.count %> )
&nbsp &nbsp
<%= link_to "Details", controller: :details, param1: selection.id, param2: selection.category_id %>
</span>
</li>
details_controller
class DetailsController < ApplicationController
require 'will_paginate/array'
respond_to :html, :js
def index
##category = Category.find_by_id(params[:param2])
#selection = Selection.find_by_id(params[:param1])
#microposts = #selection.microposts.paginate(page: params[:page])
render '/selections/detailslist'
end
end
view: /selections/detailslist
<% #category = Category.find_by_id(#selection.category_id) %>
<h3> <%= #category.content %> </h3>
<br>
<%= #selection.content %>
&nbsp
( <%= #selection.count %> )
<br>
<ol class="selections">
<%= render partial: 'selections/details', collection: #microposts %>
</ol>
<%= will_paginate #microposts %>
view: selections/details
<li>
<span class="selection">
<% user = User.find_by_id(micropost.user_id) %>
<%= user.name %>
</span>
</li>
error message:
undefined local variable or method `micropost' for #<#< Class:0x007fed1811ba90>:0x007fed17bc9fb0>

Resources