我通过 :title 而不是 :id 获得帖子
routes.rb:
Rails.application.routes.draw do
#RSS
get 'feed' => 'posts#feed'
get 'archive' => 'posts#archive'
devise_for :users, controllers: { omniauth_callbacks: "callbacks" }
root to: "posts#index"
resources :posts, param: :title do
resources :comments, only: [:new, :create, :destroy]
resources :images do
resources :comments, only: [:new, :create]
end
resources :links do
resources :comments, only: [:new, :create]
end
resources :photos, only: [:new, :create,:destroy]
resources :songs, only: [:new, :create, :destroy]
end
post_controller:
class PostsController < ApplicationController
before_action :authenticate_user!, only: [:new, :create, :show]
respond_to :html, :json, :rss, :atom
def index
if params[:search].blank?
@posts = Post.includes(:comments, :photos).all
else
@search = Post.search do
fulltext params[:search]
end
@posts = @search.results
end
respond_with(@posts)
end
def show
set_post
end
def new
@post = Post.new
end
def edit
@post = Post.find_by(title: params[:title])
end
def create
@post = Post.new(post_params)
@post.errors.add(:base, :invalid) unless @post.save
respond_with(@post)
end
def update
set_post # this @post dont get post becouse browser pass wrong title and rails dont find it
if @post.valid? # here error becouse @post hasnt find
@post.update(post_params)
else
@post.errors.add(:base, :invalid)
end
respond_with(@post)
end
def destroy
set_post
@post.destroy
respond_with(@post)
end
def feed
@posts = Post.all.reverse
respond_with(@posts)
end
def archive
@posts_by_year = Post.limit(300).all.order("created_at DESC").
group_by {|post| post.created_at.beginning_of_year}
end
private
def set_post
#fix N+1 queries and find by posts title
@post = Post.includes(:comments, :photos, :links).find_by(title: params[:title])
end
def post_params
params.require(:post).permit(:title, :content)
end
end
当我创建新帖子时,如果我在帖子标题中包含点,我会从 Rails 错误中得到。因此,我在这种情况下使用验证格式:方法。
帖子.rb:
class Post < ActiveRecord::Base
#overwrite the to_param for changing routes from posts/id to posts/title
def to_param
title
end
has_many :comments, dependent: :destroy, as: :commentable
has_many :images, dependent: :destroy
has_many :links, dependent: :destroy
has_many :photos, dependent: :destroy
has_many :songs, dependent: :destroy
validates :content, presence: true
validates :title, presence: true, length: { maximum: 100 }
validates :title, format: { without: /./,
message: "must be without dot" }
searchable do
text :title, :content
end
end
在此之后,当我更新帖子时,我的验证格式方法有效,我收到验证消息"必须没有点"。井。我删除了帖子标题输入字段中的点并提交表单。现在浏览器发送到服务器以前的帖子标题值与点。
Started PATCH "/posts/my%20test%20post%20title%20with%20dot." for 127.0.0.1 at 2017-01-26 11:57:34 +0300
ActionController::RoutingError (No route matches [PATCH] "/posts/my%20test%20post%20title%20with%20dot."):
因此,rails 无法按标题找到带有点的帖子,我收到错误。我能解决这个问题吗?也许在这种情况下:title而不是:id是个坏主意?
我假设你正在使用form_for
? Rails 将在帖子对象上设置标题,然后运行验证。 当它失败时,它将显示编辑视图,然后form_for
将使用 to_param
方法来设置 url,这将使用帖子更新的标题。 当您尝试再次更新时,它将使用 url 中的标题来尝试查找帖子,但无法这样做,因为数据库中没有具有该标题的帖子。
你应该使用类似 friendly_id 的东西,但如果你真的想自己滚动,那么一个简单的实现是有一个根据标题设置的 slug 列after_validation
但显然你必须确保它是独一无二的,所以我个人会切换到友好的 id 或其他处理 slugs 的宝石。