模型和控制器名称不匹配时需要哪些路由?

时间:2017-10-23 01:56:55

标签: ruby-on-rails ruby-on-rails-5

我有一个名为Category的模型和另一个名为Articles的模型。类别是"部分"有很多文章,例如新闻和活动。两个类别都使用这些文章,除非它们在我网站的不同部分显示。

现在我正在创建新闻控制器(NewsController),我想访问/news/new添加新闻。同样,同样适用于EventsController和/events/new

我必须在我的路线上使用什么来做这件事?

我的第一次尝试是使用:

resources :categories do
  resources :articles, path: '/news'
end

但这迫使我使用/categories/1/news/new,这有点难看。

如果新闻总是category_id 1而且活动总是2,那么我如何在我的路线上指定这个,所以我可以使用我提到的网址轻松访问它们?

以不同方式解释

我有一个文章模型。我想要一个叫做NewsController的控制器来处理文章,这样/news/new(以及其他路径)就可以使用Article。我还希望有一个名为EventsController的控制器,它也可以处理文章,因此/events也适用于文章。它们之间的区别在于它们具有不同的category_id

这可以通过路线进行吗?

更新

取得了一些进展。

resources :categories do
  resources :articles
end

get 'news/new' => 'articles#new', defaults: {category_id: 1}
get 'events/new' => 'articles#new', defaults: {category_id: 2}

这解决了我想要对/news/new/events/new做些什么,但我错过了其余的路线(编辑,显示,更新等)。此外,这使我使用目前不存在的文章控制器,也会使新闻控制器过时/无用。

我的逻辑可能是错的,它与我刚刚制作的内容有点明显,但也许有了这个更新,我可以更好地说明我想要做的事情。

更新2

我目前正在测试以下内容:

resources :articles, path: '/news', controller: 'news'
resources :articles, path: '/events', controller: 'events'

到目前为止它是有意义的,它是我想要的路线,它使用两个控制器和他们自己的配置,当我访问/news/events时它没有任何错误(尚)。

也可以这样做:

resources :articles, path: '/news', defaults: {category_id: 1}
resources :articles, path: '/events', defaults: {category_id: 2}

但这取决于文章控制器,它可以处理两种类型的类别。这两种解决方案都可以工作(理论上),但是由于各个控制器允许对这两种情况进行更具体的配置,因此我会在第一个解决方案上更多地倾斜。但是,当创建的文章之间没有那么多差异时,第二个更为充分。 defaults属性也没有明确的必要,我只是为了方便而把它放在那里。

3 个答案:

答案 0 :(得分:1)

你的问题是问我认为没有意义的事情,也许你的设计存在缺陷。

如果与类别资源无关,为什么会有新闻资源呢?

类别只是名称空间吗?

如果新闻记录确实总是与您的问题所暗示的第一类相关,则您不能使用ID,因为您无法控制第一类和第一类的ID类别可以有任何ID,在这种情况下,您可以使用顶级新闻资源,并在创建之前在模型中查找第一个类别然后您不必担心丑陋的网址。

如果新闻记录确实与类别相关,那么您必须提供相关的类别ID并嵌套您的路线,但您可以使用以下内容从

中提取相应的网址。

https://gist.github.com/jcasimir/1209730

以下是

  

友情网址

     

默认情况下,Rails应用程序根据主键构建URL -   数据库中的id列。想象一下,我们有一个Person模型和   相关控制器。我们有Bob Martin的人事记录   身份证号码6.他的节目页面的网址为:

     

/人/ 6

     

但是,出于审美或搜索引擎优化的目的,我们希望在网址中使用Bob的名字。该   最后一段,这里的6,被称为" slug"。我们来看几个   如何实施更好的slu ..简单方法

     

最简单的方法是覆盖Person中的to_param方法   模型。每当我们调用这样的路线助手时:

     

person_path(@person)

     

Rails会调用to_param将对象转换为URL的slug。   如果您的模型没有定义此方法,那么它将使用   ActiveRecord :: Base中的实现只返回id。

     

要使此方法成功,所有链接都使用该方法至关重要   ActiveRecord对象而不是调用id。不要这样做:

     

person_path(@ person.id)#Bad!

     

相反,始终传递对象:

     

person_path(@person)

     

Slug Generation

     

相反,在模型中,我们可以覆盖to_param以包含a   该人名的参数化版本:

     

class Person< ActiveRecord :: Base def to_param       [id,name.parameterize] .join(" - ")end end

     

对于ID为6的用户Bob Martin,这将生成一个slug   6- bob_martin。完整的URL将是:

     

/人/ 6,鲍勃 - 马丁

     

ActiveSupport的参数化方法将处理转换   任何对URL无效的字符。对象查找

     

我们需要改变哪些发现者?没有!当我们打电话   Person.find(x),参数x被转换为要执行的整数   SQL查找。看看to_i如何处理混合的字符串   字母和数字:

     
    

" 1" .to_i

         

=> 1

         

" 1与 - 词语" .to_i

         

=> 1

         

" 1-2345" .to_i

         

=> 1

         

" 6,鲍勃 - 马丁" .to_i

         

=> 6

  
     

to_i方法一旦命中就会停止解释字符串   非数字。由于我们的to_param实现始终具有id   前面跟一个连字符,它总是会根据查找   只是id并丢弃其余的slu ..好处/限制

     

我们已经向slug添加了内容,这将改善SEO并使我们的   网址更具可读性。

     

一个限制是用户无法操纵任何URL   有意义的方式知道网址6-bob-martin不允许你这样做   猜猜网址是7-russ-olsen,你还需要知道ID。

     

数字ID仍在URL中。如果这是你想要的东西   为了混淆,那么简单的方案并没有帮助。使用非ID   字段

     

有时你想要一起离开身份证并使用   数据库中用于查找的另一个属性。想象一下,我们有一个标签   具有名称列的对象。这个名字就像红宝石一样   或者铁轨。链接生成

     

创建链接可以再次覆盖to_param:

     

class Tag< ActiveRecord :: Base validates_uniqueness_of:name        def to_param       名称末尾

     

现在,当我们调用tag_path(@tag)时,我们会得到一个像/ tags / ruby​​这样的网址。宾语   查找

     

但查找更难。当请求进入/ tags / ruby​​时   ruby将存储在params [:id]中。典型的控制器会调用   Tag.find(params [:id]),基本上是Tag.find(" ruby​​"),它将失败。   选项1:从控制器查询名称

     

相反,我们可以修改控制器   Tag.find_by_name(PARAMS [:ID])。它会起作用,但它很糟糕   面向对象的设计。我们打破了Tag的封装   类。

     

DRY原则说知识应该有一个单一的知识   系统中的表示。在这个标签的实现中,这个想法   "标签可以通过其名称找到#34;现在已经代表了   模型的to_param和控制器查找。这是一项维护工作   头痛。选项2:自定义查找器

     

在我们的模型中,我们可以定义一个自定义查找器:

     

class Tag< ActiveRecord :: Base validates_uniqueness_of:name        def to_param       名字结束        def self.find_by_param(输入)       find_by_name(输入)end end

     

然后在控制器中调用Tag.find_by_param(params [:id])。这一层   抽象意味着只有模型确切地知道标签是如何的   转换为参数和从参数转换。封装已恢复。

     

但我们必须记住使用Tag.find_by_param而不是Tag.find   到处。特别是如果您将友好ID改装到   现有系统,这可能是一项重大的努力。选项3:   覆盖查找

     

我们可以覆盖查找,而不是实现自定义查找程序   方法:

     

class Tag< ActiveRecord :: Base#... def self.find(输入)       find_by_name(输入)end end

     

当你传入一个名字slug时它会起作用,但是当你传入一个名字slug时会有效   传入数字ID。我们怎么能处理这两个?

     

第一个诱惑是做一些类型转换:

     

class Tag< ActiveRecord :: Base#... def self.find(输入)       if input.is_a?(整数)         超       其他         find_by_name(输入)       结束端

     

那可以工作,但是检查类型是违背Ruby的原则。   写is_a?应该总是让你问'#34;有更好的方法吗?"

     

是的,基于这些事实:

Databases give the id of 1 to the first record
Ruby converts strings starting with a letter to 0
     

class Tag< ActiveRecord :: Base#... def self.find(输入)       如果input.to_i!= 0         超       其他         find_by_name(输入)       结束端

     

或者,用三元缩写:

     

class Tag< ActiveRecord :: Base#... def self.find(输入)       input.to_i == 0? find_by_name(输入):超级结束

     

我们的目标已实现,但我们已经引入了一个可能的错误:如果一个名字   以数字开头,它看起来像一个ID。如果它是我们可以接受的   在业务领域,我们可以添加名称无法启动的验证   一个数字:

     

class Tag< ActiveRecord :: Base#... validates_format_of:name,   :没有=> / ^ \ d / def self.find(输入)       input.to_i == 0? find_by_name(输入):超级结束

     

现在一切都应该很棒!使用FriendlyID Gem

     

实施另外两种方法是否真的很痛苦?或者更多   说真的,你打算实现这种功能吗?   您的应用程序的多个模型?那么它可能值得检查   out FriendlyID gem:https://github.com/norman/friendly_id设置

     

宝石即将达到4.0版本。在撰写本文时,你   想要使用测试版。在您的Gemfile中:

     

gem" friendly_id","〜> 4.0.0.beta8"

     

然后从命令行运行bundle。简单用法

     

模型中的最低配置是:

     

class Tag< ActiveRecord :: Base扩展FriendlyId friendly_id:name   端

     

这将允许您使用名称列或id进行查找   找到,就像我们之前做的那样。 Dedicated Slug

     

但是图书馆在维护专用slu g方面做得很好   专栏给你。例如,如果我们处理文章,我们就是   不想一遍又一遍地生成slu ..更重要的是,我们会   想要将slug存储在数据库中直接查询。

     

库默认为名为slug的String列。如果你有   在列中,您可以使用:slugged选项自动生成和   slu the:

     

class Tag< ActiveRecord :: Base扩展FriendlyId friendly_id   :name,:use => :slu end end

     

用法

     

你可以在这里看到它:

     
    

t = Tag.create(:name =>" Ruby on Rails")

         

=> #

         

Tag.find 16

         

=> #

         

Tag.find" ruby​​-on-rails"

         

=> #

         

t.to_param

         

=> "红宝石上导轨"

  
     

我们可以透明地使用带有ID或slug的.find。当对象   转换为链接的参数,我们将得到没有ID的slug   数。我们获得良好的封装,易于使用,改进的SEO和简单   阅读网址。

答案 1 :(得分:0)

如果您确定只有两个类别,为什么不简单地在文章中添加一个布尔值?

赞:article.event =如果是事件类别则为true,如果是新闻则为false

然后,您可以为两个类别的文章类添加范围

class Article
  scope :events, -> { where(event: true) }
  scope :news, -> { where(event: false) }
end

创建控制器,例如:

class EventsController < ApplicationController

  def index
    @articles = Article.events
  end

  def create
    @article.new(params)
    @article.event = true
    @article.save
  end

  ...
end

和路线:resources :events

答案 2 :(得分:0)

您应该尝试使用动态细分:http://guides.rubyonrails.org/routing.html#route-globbing-and-wildcard-segments

将一些slug属性添加到Category,它应该是唯一的并为其添加索引。

# routes
resources :articles, except: [:index, :new]
get '*category_slug/new', to: 'articles#new'
get '*category_slug', to: 'articles#index'

# controller
class ArticlesController < ApplicationController

  def index
    @category = Category.find_by slug: params[:category_slug]
    @articles = @category.articles
  end

  def new
    @category = Category.find_by slug: params[:category_slug]
    @article = @category.articles.build
  end

  ...
end

请务必在form_for @article

的隐藏字段中添加某个类别