I have problems with my Rails Block. After I implemented a comment-section I am not able to create posts anymore. The console gives me a rollback transaction. So I did
p = Post.new
p.valid? # false
p.errors.messages
It seems I have some validation problems with user :user=>["must exist"]. But before I implemented comments it did work. Can someone help me out?
User.rb
class User < ApplicationRecord
has_many :posts
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 5}
validates :body, presence: true
has_attached_file :image #, :styles => { :medium => "300x300>", :thumb => "100x100>" }
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
end
Post-migrate
class CreatePosts < ActiveRecord::Migration[5.1]
def change
create_table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
end
end
Post_controller
class PostsController < ApplicationController
def index
#posts = Post.all.order("created_at DESC")
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body, :theme)
end
end
When you are creating a post you need to assign a user to that post in the create method under your posts controller. You could try something like this.
def create
if current_user
#post.user_id = current_user.id
end
## More create method stuff
end
By default, in a belongs_to association a user is required to create the post otherwise you will not be able to create the post. Since, from the looks of it, you do not have anything that assigns the user to that post in the create method.
Related
I'm renewing a small library app and my search filter (pg_search) doesn't work with a model that is referenced with another (in this case, model Book as User references, for each user to have it's own set of books).
But if i delete the references, the search works... In the case that if the books were available to every user but that's not the purpose.
What am i missing?
Thanks in advance for any help.
Book.rb
class Book < ApplicationRecord
belongs_to :user
include PgSearch::Model
pg_search_scope :search_by_full_name, against: [:title, :author],
using: { tsearch: { prefix: true } }
validates :title, presence: true
validates :author, presence: true
end
User.rb
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :books
end
books_controller.rb
class BooksController < ApplicationController
def index
if params[:term].present?
#books = Book.search_by_full_name(params[:term])
else
#books = Book.all
end
end
def show
#book = Book.find(params[:id])
end
def new
#user = User.find(params[:user_id])
#book = Book.new
end
def create
#user = User.find(params[:user_id])
#book = Book.new(book_params)
#book.user = #user
if #book.save
redirect_to user_books_path
flash[:notice] = 'Success. Your book was added to the Library'
else
render "new"
flash[:notice] = 'Book not created. Please try again'
end
end
def edit
#user = User.find(params[:user_id])
#book = Book.find(params[:id])
end
def update
#book = Book.find(params[:id])
#book.update(book_params)
redirect_to user_book_path
end
def destroy
#book = Book.find(params[:id])
#book.destroy
redirect_to user_books_path
end
private
def book_params
params.require(:book).permit(:title, :author, :photo, :comments)
end
end
_search_book.html.erb
<%= form_tag user_books_path(current_user.id, #book), method: :get do %>
<%= text_field_tag 'term', params[:term], placeholder: "Enter title or author" %>
<%= submit_tag 'Search!' %>
<% end %>
It's solved... I was using current_user on the views instead of applying all the method on the controller and on the view leave it with only the model instance.
Thanks everyone
I have been told to move a method "Top" from Controller to Model, but when I try to call it, it doesn't work anymore.
I am using Rails 6
This is my Controller:
class UsersController < ApplicationController
before_action :authenticate_user!
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#posts = #user.posts.ordered_by_most_recent
end
def edit
#user = User.find(params[:id])
end
def following
#title = 'Following'
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = 'Followers'
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
def top
#userst = User.joins(:followers).order('COUNT(followings.follower_id) DESC').group('users.id').limit(10)
end
end
and this would be my Model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, authentication_keys: [:username]
validates :fullname, presence: true, length: { maximum: 20 }
validates :username, presence: true, length: { maximum: 20 }
validates_uniqueness_of :username
has_many :posts
has_many :active_followings, class_name: 'Following',
foreign_key: 'follower_id',
dependent: :destroy
has_many :passive_followings, class_name: 'Following',
foreign_key: 'followed_id',
dependent: :destroy
has_many :following, through: :active_followings, source: :followed
has_many :followers, through: :passive_followings, source: :follower
mount_uploader :photo, FileUploader
mount_uploader :coverimage, FileUploader
# Follows a user.
def follow(other_user)
following << other_user
end
# Unfollows a user.
def unfollow(other_user)
following.delete(other_user)
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
end
All code here makes sense to me, so I only had to create a file called top.html.erb like this to render the Top:
<article class="timeline new-initial">
<h3>Top:</h3>
<ul class="posts">
<%= render #userst %>
</ul>
</article>
Now, to be honest, I am lost, I am not sure how to move this method to the User model in the right way to read it in the view section.
This seems like a job for a scope.
Model:
scope :top, -> { joins(:followers).order('COUNT(followings.follower_id) DESC').group('users.id').limit(10) }
Controller:
def top
#userst = User.top
end
So I have a post.rb model where I can display who wrote the post in the show post view, and that works wonderfully.
However, I've been trying to add comments, which belong to a post, and also belong to a user.
I can't seem to get all the comments or the names of the users who posted the coments to show up in the post show.html.erb.
I guess my question is, how do I integrate controller and model information in between controller and model views? I know if it exists in the model and controller and view of the same type, it is accessible, but cross-linking or sharing controller model information is hard for me.
I want to be able to display the user that made the comment, along with the body of the comment in the show view of the post, and not the comment.
comment.rb
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
validates :body, :presence => true
end
post.rb
class Post < ApplicationRecord
belongs_to :user, optional: true
validates :user, presence: true;
validates :title, presence: true,
length: { minimum: 5}
extend FriendlyId
friendly_id :title, use: :slugged
validates :title, :slug, presence: true
# comments
has_many :comments
end
user.rb
class User < ApplicationRecord
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :first_name, presence: true
validates :last_name, presence: true
validates :school_name, presence: true, inclusion: { in: %w(Harvard Yale),
message: "%{value} is not a valid choice" }
validates :graduation_year, presence: true, inclusion: { in: %w(2017 2018 2019 2020 2021 2022 2023),
message: "%{value} is not a valid choice currently" }
def superadmin?
self.role.name == "Superadmin"
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_action :authenticate_user!
# GET /posts/:post_id/comments
# GET /posts/:post_id/comments.xml
def index
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you get all the comments of this post
#comments = post.comments
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #comments }
end
end
# GET /posts/:post_id/comments/:id
# GET /comments/:id.xml
def show
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you retrieve the comment thanks to params[:id]
#comment = post.comments.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #comment }
end
end
# GET /posts/:post_id/comments/new
# GET /posts/:post_id/comments/new.xml
def new
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you build a new one
#comment = post.comments.new(params[:comment])
#comment.user = current_user
end
# GET /posts/:post_id/comments/:id/edit
def edit
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you retrieve the comment thanks to params[:id]
#comment = post.comments.find(comment_params[:id])
end
# POST /posts/:post_id/comments
# POST /posts/:post_id/comments.xml
def create
#1st you retrieve the post thanks to params[:post_id]
post = Post.friendly.find(params[:post_id])
#2nd you create the comment with arguments in params[:comment]
#comment = post.comments.create(comment_params)
#comment.user = current_user
respond_to do |format|
if #comment.save
#1st argument of redirect_to is an array, in order to build the correct route to the nested resource comment
format.html { redirect_to([#comment.post, #comment], :notice => 'Comment was successfully created.') }
#the key :location is associated to an array in order to build the correct route to the nested resource comment
format.xml { render :xml => #comment, :status => :created, :location => [#comment.post, #comment] }
else
format.html { render :action => "new" }
format.xml { render :xml => #comment.errors, :status => :unprocessable_entity }
end
end
end
In the show method of your post_controller, you'll need to do something like:
def show
#post = Post.find_by(id: params[:id]) # or however you find your post
....
end
Then, in your views/posts/_show.html.erb (if you're using erb), you'll do something like:
<% #post.comments.each do |comment| %>
... show some comment stuff
<%= comment.user.name %>
<% end %>
I'm trying to allow users to create projects...and as soon as a user creates a project...they will automatically be following that project. (I have my app setup to allow a user to follow a project from a 'follow' button on the project profile). I would like the project creator to automatically be following the new project without having to click the 'follow' button. I rearranged my code as per Bilal's answer...but now clicking 'create project' simply refreshes the 'new' view (no project gets posted). I assumed this has to do with the Pundit authorizations but perhaps someone can clarify why the 'create' action is no longer working...
My Projects Model:
class Project < ActiveRecord::Base
belongs_to :owner, :foreign_key=>'user_id', :class_name=>'User'
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
validates :title, presence: true
validates :background, presence: true
validates :projectimage, presence: true
mount_uploader :projectimage, ProjectimageUploader
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
after_update :crop_projectimage
def crop_projectimage
projectimage.recreate_versions! if crop_x.present?
end
def private?
self.is_private == true
end
def public?
self.is_private == false
end
end
Relationships Model:
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "Project"
validates :follower_id, presence: true
validates :followed_id, presence: true
enum role: [:admin, :collaborator, :visitor]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :visitor
end
end
My Projects Controller:
class ProjectsController < ApplicationController
before_filter :authenticate_user!, only: [:create, :new, :edit, :update, :delete, :followers]
# CREATES REDIRECT & ALERT MESSAGE WHEN PUNDIT SEES SOMEONE IS NOT AUTHORIZED (via :not_authorized_in_project below)
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def new
#project = Project.new
end
def show
#project = Project.find(params[:id])
authorize #project, :visit?
# #user = User.where(:id => #project.user_id).first
rescue Pundit::NotAuthorizedError
flash[:warning] = "You are not authorized to access this page."
redirect_to project_path || root_path
end
def index
#projects = policy_scope(Project).all
end
def create
#project = current_user.own_projects.build(project_params)
#project.followers << current_user
if #project.save
if params[:project][:projectimage].present?
render :crop
else
flash[:success] = "You've successfully created a Project..."
redirect_to #project
end
else
render 'new'
end
end
def update
#project = Project.find(params[:id])
if #project.update_attributes(project_params)
if params[:project][:projectimage].present?
render :crop
else
flash[:success] = "Project Created"
redirect_to #project
end
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Project destroyed"
redirect_to users_path
end
def followers
#title = "Following this Project"
#project = Project.find(params[:id])
#project = #project.followers.paginate(page: params[:page])
render 'show_follow_project'
end
private
def project_params
params.require(:project).permit(:title, :background, :is_private, :projectimage, :user_id, :crop_x, :crop_y, :crop_w, :crop_h)
end
def user_not_authorized
flash[:warning] = "You are not authorized to access this page."
redirect_to project_path(#project) || root_path
end
end
My User Model:
class User < ActiveRecord::Base
has_many :own_projects, :class_name=>'Project'
has_many :projects
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_projects, through: :relationships, source: :followed
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
def following?(some_project)
relationships.find_by_followed_id(some_project.id)
end
def follow!(some_project)
self.relationships.create!(followed_id: some_project.id)
end
def unfollow!(some_project)
relationships.find_by_followed_id(some_project.id).destroy
end
Pundit Project Policy:
class ProjectPolicy < Struct.new(:user, :project)
class Scope < Struct.new(:user, :scope)
# SCOPE & RESOLVE METHOD USED TO RESTRICT PROJECTS INDEX TO PUBLIC & THOSE YOU'RE AN ADMIN/COLLAB ON
def resolve
followed_project_ids = user.followed_projects.map(&:id)
public_project_ids = Project.where(:is_private=>false).map(&:id)
Project.where(:id=>followed_project_ids + public_project_ids)
end
end
def update?
user.project_admin? || user.project_collaborator?
end
# METHOD USED IN PROJECTS_CONTROLLER (SHOW) TO RESTRICT VISITING PRIVATE PROJECT PROFILES TO ADMINS & COLLABS
def visit?
user.project_admin?(project) || user.project_collaborator?(project)
end
end
It's never a good idea to use current_user in a model, see this for reference.
Any easy and efficient place to set this thing would be the controller itself. So, you can write the following code:
def create
#project = current_user.own_projects.build(project_params)
#project.followers << current_user
if #project.save
if params[:project][:projectimage].present?
render :crop
else
flash[:success] = "You've successfully created a Project..."
redirect_to #project
end
else
render 'new'
end
end
As the title suggests, I am building a Q&A application (Like ask.fm) in Ruby on Rails, and I am having some trouble with sending the question to a specific user.
I have 3 models, a User model (from Devise), A Question model with this attribute: content:text and a Answer model with this attribute: content:text
Here are their models
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
class Question < ActiveRecord::Base
has_one :answer
belongs_to :user
end
class User < ActiveRecord::Base
has_many :questions
has_many :answers
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
And here are the migrations that I created
This one adds a user_id to the question (So I can check which user sent the question, btw this works fine)
class AddUserIdToQuestion < ActiveRecord::Migration
def change
add_column :questions, :user_id, :integer
end
end
Here I tried to add a receiver (the user that would get the question) but I wont work, when I create a Question It will be equal to 'nil', when I check it out in the rails console (Check the controller to see what I did)
class AddReceiverToQuestion < ActiveRecord::Migration
def change
add_column :questions, :receiver, :integer
end
end
Here is the question controller
class QuestionsController < ApplicationController
def new
#question = Question.new
end
def create
#question = Question.new(question_params)
#question.user_id = current_user.id
if #question.save
redirect_to root_path
else
render 'new'
end
end
private
def question_params
params.require(:question).permit(:content, :user_id)
end
end
I also have a user profile page where I have a form show up, here is the controller for that one
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#user_questions = #user.questions
#question = Question.new
end
def create
#user = User.find(params[:id])
#question = Question.new(question_params)
#question.receiver = #user.id #This does not work
if #question.save
redirect_to root_path
else
render 'new'
end
end
private
def question_params
params.require(:question).permit(:content, :user_id, :receiver)
end
end
I really hope some of you know what I could do here, thank you :)