For my application, I am currently listing my projects related to a user on the user's page. For each project listed, I want to render the comments made for each project. I am able to post comments to each project to the database via the form, but unable to render the comments related to the comments. Nothing renders. I have been playing around with the users_controller thinking it is there with no success. How do I fix it?
I have created models and controllers for Users, Projects, and Comments. Comments belong to Projects and Projects belong to Users.
schema.rb
create_table "comments", :force => true do |t|
t.integer "user_id"
t.integer "project_id"
t.text "content"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
user.rb
has_many :projects
has_many :comments
project.rb
has_many :comments
belongs_to :user
comment.rb
belongs_to :project
routes.rb
resources :users
resources :projects do
resources :comments
end
resources: comments
view/users/_projects.html.erb
<%= render #projects %>
users_controller.rb
def comments
#user = User.find(params[:id])
#projects = #user.projects.newest.page(params[:comments_page]).per_page(10)
#project = Project.new
#comments = Project.find(params[:id]).comments.newest.page(params[:comments_page]).per_page(2)
end
view/projects/_project.html.erb
<%= project.content %>
<%= render 'comments/form', project:project %>
<%= render #comments %>
<%= will_paginate #comments, :param_name => 'comments_page' %>
view/comments/_comment.html.erb
<%= comment.content %>
In your project.html.erb file try using a loop like so:
<% project.comments.each do |c| %>
#do something
<% end %>
Related
UPDATE: Problem solved, thanks to Sebastian and Gabriel for the helpful pointers.
The relevant changes to my code are as follows:
app/controllers/pomodoro_cycles_controller.rb
def pomodoro_collections
{
pomodoro_collection_0: Pomodoro.offset(0).first(100),
pomodoro_collection_1: Pomodoro.offset(100).first(100)
}
end
app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p>
<%= pomodoro.id %>
<%= pomodoro.color %>
</p>
<% end %>
<% end %>
NOTA BENE:
The #first method in ActiveRecord returns an Array, so the keys in my original Hash were nested Arrays. Instead, the following was sufficient to return an Array of Pomodoro objects:
Pomodoro.offset(0).first(100)
DESCRIPTION OF ORIGINAL PROBLEM
Rails 5, PostgreSQL
PROBLEM: I cannot access Pomodoro.all from within PomodoroCycleController
I have two scaffolds: Pomodoro and PomodoroCycle, and I want to access the full list of Pomodoros within the PomdoroCycle controller.
The following code is kept simple, in order to make as clear as possible what I'm trying to do. If I can do these things, then I'll be able to do much more, but one step at a time.
Regarding the db migration files, I have already run bundle exec rails db:migrate
I want to display a full list of Pomodoros in the PomodoroCycle Show View (later to be displayed in Index), but I don't know what is missing.
From app/controllers/pomodoro_cycles_controller.rb
def show
#pomodoros_collections = pomodoro_collections
end
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
From app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |collection| %>
<p><%= collection %></p>
<% end %>
However, this displays nothing in the browser.
app/models/pomodoro_cycle.rb
class PomodoroCycle < ApplicationRecord
has_many :pomodoros
end
app/models/pomodoro.rb
class Pomodoro < ApplicationRecord
belongs_to :pomodoro_cycle
end
Updated db/migrate/20180103032759_create_pomodoro_cycles.rb:
class CreatePomodoroCycles < ActiveRecord::Migration[5.1]
def change
create_table :pomodoro_cycles do |t|
t.string :activity
t.integer :iteration
t.integer :matrix_side_length
t.datetime :created_at
t.datetime :completed_at
t.string :category_labels, array:true, default: []
t.string :category_colors, array:true, default: []
t.string :username
t.timestamps
end
create table :pomodoros do |t|
t.belongs_to :pomodoro_cycle, index: true
t.datetime :completed_at
t.timestamps
end
add_index :pomodoros, :pomodoro_cycle_id
end
end
Untouched db/migrate/20180103054425_create_pomodoros.rb
class CreatePomodoros < ActiveRecord::Migration[5.1]
def change
create_table :pomodoros do |t|
t.boolean :status
t.string :category
t.string :color
t.datetime :completed_at
t.string :username
t.timestamps
end
end
end
First of all, as #SebastianPalma pointed out in the comments, the syntax is wrong
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
should be:
def pomodoro_collections
{
pomodoro_collection_0: [Pomodoro.offset(0).first(100)],
pomodoro_collection_1: [Pomodoro.offset(100).first(100)]
}
end
Make the keys in the hash symbols
Then to display each Pomodoro put something like:
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p><%= pomodoro.id %></p> #Or the attribute you want to display
<% end %>
<% end %>
Hope this help
I want to create a Rails app that allows "users" to follow other users. I am semi-new to more complex relationships and am attempting to set up has_many through for the first time. I want friends to be able to follow other users.
Here is my join table:
class Following < ApplicationRecord
belongs_to :user
belongs_to :follower, class_name: "User"
end
Here is my users table:
class User < ApplicationRecord
has_many :followings
has_many :followers, through: :followings
end
Here is my schema:
create_table "followings", force: :cascade do |t|
t.integer "user_id"
t.integer "follower_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I don't know how to set up a form to actually create the relationship. In a users view, I have this, but it doesn't work.
<%= form_for #following do |f| %>
<%= f.hidden_field :follower_id, :value => #user %>
<%= f.select :user_id, #users.collect { |u| [u.name, u.id] } %>
<%= f.submit %>
<% end %>
As I said, I am very new to this type of relationship. I need help. I don't know how to link records through a form.
I am following this tutorial: https://teamtreehouse.com/library/what-is-a-hasmany-through-association-in-ruby-on-rails
I am assuming you have a current_user method that returns the logged in user - like what Devise provides. If not you need to setup authentication first.
Create a nested route:
# config/routes.rb
resources :users, only: [] do
resources :followings, only: [:create, :destroy], shallow: true
end
Add a validation to Following to avoid duplicates:
class Following < ApplicationRecord
belongs_to :user
belongs_to :follower, class_name: "User"
validates_uniqueness_of :user_id, scope: 'follower_id'
end
Add a utility method to User to see if he is following another user:
class User < ApplicationRecord
has_many :followings
has_many :followers, through: :followings
def following?(user)
followings.exist?(user: user)
end
def find_following(user)
followings.find_by(user: user)
end
end
We can then add Follow and Unfollow buttons (they are actually forms) to the /users/show.html.erb view.
<% if current_user.following?(#user) %>
<%= button_to "Unfollow", current_user.find_following(#user), method: :delete %>
<% else %>
<%= button_to "Follow", [#user, #user.followings.new] %>
<% end %>
Note that we don't need any form params since we are using a nested route (POST /users/:user_id/followings) to pass the user id (who gets followed) and we are getting the current user from the session.
We can then setup our controller:
class FollowingsController < ApplicationController
# POST /users/:user_id/followings
def create
#user = User.find(params[:user_id])
#following = Following.new(user: #user, follower: current_user)
if #following.save
redirect_to #user, success: "You are now following #{ #user.name }"
else
redirect_to #user, error: "Could not create following"
end
end
# DELETE /followings/:id
def destroy
#following = Following.find(params[:id])
#following.destroy
redirect_to #following.user, success: "You are no longer following #{ #user.name }"
end
end
I have a bunch of 'kid' objects saved already and I want to create a parent object which is linked to the kids via a 'relative' model.
This object gives me a many-to-many, through relatives.
To be clear: the user visits the 'parents' page, clicks create parents and is presented with a form that lets them name the parent and add up to four children to this parent (by creating 'relatives'), each of these 'relations' is also named - that's an important part. So, I could name the relation 'step son' or 'son', for instance.
Here's the code I have so far:
class Kid < ActiveRecord::Base
has_many :relatives
has_many :parents, through: :relatives
end
class Parent < ActiveRecord::Base
has_many :relatives
has_many :kids, through: :relatives
accepts_nested_attributes_for :relatives,
:reject_if => lambda { |a| a[:content].blank? },
:allow_destroy => true
end
class Relative < ActiveRecord::Base
belongs_to :parent
belongs_to :kid
end
class ParentsController < ApplicationController
before_action :set_parent, only: [:show, :edit, :update, :destroy]
before_action :lookup_kids, only: [:new, :edit]
# GET /parents
# GET /parents.json
def index
#parents = Parent.all
end
# GET /parents/1
# GET /parents/1.json
def show
end
# GET /parents/new
def new
#parent = Parent.new
4.times { #parent.relatives.build }
end
# GET /parents/1/edit
def edit
end
# POST /parents
# POST /parents.json
def create
#parent = Parent.new(parent_params)
parent_params[:relatives_attributes].each do |k,r|
#parent.relatives.build(r.except(:_destroy))
end
respond_to do |format|
if #parent.save
format.html { redirect_to #parent, notice: 'Parent was successfully created.' }
format.json { render :show, status: :created, location: #parent }
else
format.html { render :new }
format.json { render json: #parent.errors, status: :unprocessable_entity }
end
end
end
# cut for brevity.
private
# Use callbacks to share common setup or constraints between actions.
def set_parent
#parent = Parent.find(params[:id])
end
def parent_params
params.require(:parent).permit(:name,
relatives_attributes: [:parent_id, :kid_id, :relationship, :_destroy])
end
def lookup_kids
#kids = Kid.all #for this nursery.
end
end
<%= form_for(#parent) do |f| %>
<% if #parent.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#parent.errors.count, "error") %> prohibited this parent from being saved:</h2>
<ul>
<% #parent.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<h4>Kids:</h4>
<%= f.fields_for :relatives do |r| %>
<%= r.label :kid %>
<%= r.collection_select :kid_id,
#kids, :id, :name, include_blank: true%>
<%= r.label :relationship %>
<%= r.text_field :relationship %>
<%= r.check_box :_destroy %>
<%= r.label :_destroy, "Remove" %>
<br/>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
ActiveRecord::Schema.define(version: 20151030113634) do
create_table "kids", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "parents", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "relatives", force: :cascade do |t|
t.string "relationship"
t.integer "parent_id"
t.integer "kid_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "relatives", ["kid_id"], name: "index_relatives_on_kid_id"
add_index "relatives", ["parent_id"], name: "index_relatives_on_parent_id"
end
When I get to 'create' in the parents controller, I can see the right parameters are getting through but the relationship records aren't being saved. SHouldn't this happen automatically?
I've tried looping through the :relatives_attributes but that doesn't seem to work with 'build'.
How am I suppsed to get the 'relatives' records to save?
EDIT: adding parameters posted:
parent"=>{
"name"=>"Dad",
"relatives_attributes"=>{
"0"=>{"kid_id"=>"2", "relationship"=>"Son", "_destroy"=>"0"},
"1"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"},
"2"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"},
"3"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"}}}
Edit: I've updated this to show my latest edit - note the 'parent_params[:relatives_attributes].each do |k,r|' in the controller. This now saves the kid records but the only problem is, it also saves the fields that are blank! So I have 'relative' records with null values for kid records. How can I stop it saving empty fields (or creating empty relative records)?
The answer was to build each sub-record of relative, like so:
parent_params[:relatives_attributes].each do |k,r|
#parent.relatives.build(r.except(:_destroy))
end
Before calling #parent.save.
However, I'm still having issues getting rid of the blank records. So if anyone has an answer to that problem, please comment here - or if there's a better or more traditional way of doing this, hit me up. Follow up question here: Why is this reject_if in my model not rejecting blank records?
You are almost there, depending upon how your form submission is, you most likely need an accepts_nested_attribute_for in your Relative associative class as well:
class Relative
belongs_to :parent
accepts_nested_attributes_for :parent
belongs_to :kid
accepts_nested_attributes_for :kid
end
If this doesn't work, then please submit your params that are passed into the controller and we can adjust accordingly
I have a model "votes" which belongs_to two models by polymorphous association, and has the attributes user_id and comment_id. Previously, I had a voting system in place for users that would create a new vote for a specific user every time a button was pressed:
<%= form_for [#user, #vote] do |f| %>
<input type="hidden" id="user_id" name="user_id" value="#{#user.id}" />
<%= f.submit ": )", :onclick => 'alert("Voted up!")' %>
<% end %>
and #user.votes.count would return the number of times the button was pressed. However, I switched to a different method:
View:
<%= link_to "voteuser", vote_user_path(#user.id), method: :post, :class => "btn btn-small" %>
Controller:
def vote
#user = User.find(params[:id])
Vote.create!(user_id: #user.id)
redirect_to #user
end
Routes:
Website::Application.routes.draw do
root 'home_page#home'
get "votes/new"
get 'users/random'
post 'users/vote/:id' => 'users#vote', as: 'vote_user'
get 'users/users/random' => 'users#random'
resources :users
get "all/allusers"
get "all/users/new" => 'users#new'
get 'all/all/allusers' => 'all#allusers'
end
and a Vote is still created, with a user_id equal to the current User.id, but now #user.votes.count returns 0, so the application isn't registering that the vote belongs to the user. How can I remedy this?
Vote Model:
class Vote < ActiveRecord::Base
belongs_to :voteable, polymorphic: true
end
Votes Schema:
create_table "votes", force: true do |t|
t.integer "thing_id"
t.integer "comment_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "voteable_id"
t.string "voteable_type"
end
User Model:
class User < ActiveRecord::Base
has_many :votes, as: :voteable
end
User Schema:
create_table "users", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
end
It should be Vote.create!(voteable_id: params[:id], voteable_type: 'User') for users or in short
Vote.create!(vote: User.find(params[:id])) # Vote.create!(vote: Comment.find(params[:id]))
check if you retrieve the user id in your controller and you can in your view
<%= form_for [#user, #vote] do |f| %>
<%= f.hidden_field :user_id, value: f.model.user.id %>
<%= f.submit ": )", :onclick => 'alert("Voted up!")' %>
<% end %>
on your controller you can simply do:
def vote
Vote.create!(user_id: params[:id])
redirect_to #user
end
but you use the polymorphous association so you have to specify the user_id and the user_type.
So i'm trying to record the number of times a link is clicked but can't get over the last hurdle.
I have the following so far:
config/routes.rb
resources :papers do
resources :articles do
resources :clicks
end
end
click.rb
class Click < ActiveRecord::Base
belongs_to :article, counter_cache: true
validates :ip_address, uniqueness: {scope: :article_id}
end
clicks_controller.rb
class ClicksController < ApplicationController
def create
#article = Article.find(params[:article_id])
#click = #article.clicks.new(ip_address: request.ip)
#click.save
end
end
article.rb
class Article < ActiveRecord::Base
has_many :clicks
end
schema.rb
create_table "clicks", force: true do |t|
t.integer "article_id"
t.string "ip_address"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "articles", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.text "title"
t.string "url"
t.integer "paper_id"
t.integer "clicks_count"
end
index.html.erb -- articles
<% #articles.each do |article| %>
<div class="articles col-md-4">
<%= link_to article.url, target: '_blank' do %>
<h4><%= article.title %></h4>
<h5><%= article.paper.name.upcase %></h5>
<h6><%= article.created_at.strftime("%d %B %y") %></h6>
<% end %>
Firstly, does this setup look correct, does anyone see where i may have gone wrong?
Secondly, what i don't know how to set up my view so that when the existing link is clicked the click is registered and the count goes up?
Thanks
Solved with the following.
clicks_controller.rb
Original:
def create
#article = Article.find(params[:article_id])
#click = #article.clicks.new(ip_address: request.ip)
#click.save
end
end
Amended:
def create
#article = Article.find(params[:article_id])
#click = #article.clicks.new(ip_address: request.ip)
#click.save
redirect_to #article.url
end
end
index.html.erb -- articles
Original:
<%= link_to article.url, target: '_blank' do %>
Amended:
<%= link_to paper_article_views_path(article.id, article), method: :post, target: '_blank' do %>
Also, i edited the original question to include the routes.rb file.
In my opinion, you should do 2 things :
1) Set all the methods for "clicks" into a Model
For example, you can remove your ClicksController and add this :
class Article
def create_click(ip_address)
self.clicks.create({ :ip_address => ip_address })
end
end
A little note with this code : you have a uniqueness validation in your code. Indeed, when a click already exists for an article and an ip address, the create method will return false. Do not use create! instead, or it will raise an exception.
2) Add a filter :
You can simply add a filter in your ArticlesController. At each show, it will create a click instance for the viewed article
class ArticlesController
before_filter :create_click, :only => [ :show ]
def create_click
#article.create_click(ip_address)
end
end