如何为db字段创建顺序唯一ID

时间:2017-01-05 06:15:44

标签: ruby-on-rails ruby ruby-on-rails-4

我的表格Slug与字段url是唯一的。

如果我创建@slug = Slug.url = "foo"

当我去保存时,如果“foo”的Slug.url已经存在,我想尝试一个“foo-1”的Slug.url如果它也存在,请尝试“foo-2”foo- 3,foo-4等等......直到找到一个不存在且可以在db中创建的值...在我的rails模型中用什么方法可以解决这个问题?

由于

更新

我的最新代码如下:

  def set_url
    self.url = self.title.parameterize
    # Ensure the url is available
    qUrl = self.url.split('-').shift
    slugs = Slug.where("url like '#{qUrl}%'")
    if slugs.exists?
      c = slugs.count + 1
      self.url = self.url + "-" + c.to_s
    end
  end

这段代码的问题在于qUrl正在拾取误报,任何时候标题都以“为什么”找到slugs这个词开头。在这里会有一些帮助,想出一些更可靠和优雅的东西。感谢

5 个答案:

答案 0 :(得分:4)

我建议使用friendly_id gem。如果你看一下documentation in the slugged object,你会发现它有一个机制来处理唯一性,方法是将一个uuid附加到slug名称。

或者,有许多other slug generating gems可能更适合您的环境。

答案 1 :(得分:2)

以下是使用相同Slug模型的多个模型的Rails项目的完整设置说明,并在根目录配置了slug-URL路由:

1。 Slug模型

使用friendly_idsequentially_slugged模块,该模块在保存记录时(特别是在before_validation回调期间生成来自唯一列的按顺序排序的段塞。

在新目录中启动新的Rails项目:

rails new .

friendly_id添加到Gemfile(并在之后运行bundle install):

gem 'friendly_id'

使用迁移生成slugs表架构(之后运行rake db:migrate),db/migrate/20170114211811_create_slugs.rb

class CreateSlugs < ActiveRecord::Migration
  def change
    create_table :slugs do |t|
      t.string :url
      t.string :title
    end
    add_index :slugs, :url, unique: true
    add_reference :slugs, :item, polymorphic: true, index: true
  end
end

使用Slugfriendly_id创建app/models/slug.rb模型:

class Slug < ActiveRecord::Base
  extend FriendlyId
  friendly_id :title,
    use: :sequentially_slugged,
    slug_column: :url
  belongs_to :item, polymorphic: true
end

2。 Slug Route + Controller

您的用例要求单个根路由(例如/this-is-my-x)。

config/routes.rb

Rails.application.routes.draw do
  get ':url' => 'slugs#show'
end

要通过此单个Slug路线提供多个模型,您可以让Slug Controller将其render操作委派给与所选Slug相关模型相对应的视图。

app/controllers/slugs_controller.rb

class SlugsController < ApplicationController
  def show
    @item = Slug.find_by(url: params[:url])
    render "#{@item.item_type.pluralize.underscore}/show"
  end
end

3。可插入的模型+视图

最后,开始使用多个模型中的单个Slug。

生成Article架构+模型:

rails generate model article title:string

app/models/article.rb

class Article < ActiveRecord::Base
  has_one :slug, as: :item
  before_validation do
    if slug.try(:title) != title
      self.slug = Slug.create(title: title)
    end
  end
end

app/views/articles/show.html.erb

<h1>Articles#show</h1>
<p>Title is <%=@item.title%>, ID is <%=@item.id%></p>

然后为博客,标签等创建更多模型+视图

4。测试

rails console中创建一些文章:

(1..10).each { Article.create title: 'foo' }

测试你的slug(运行rails server,并访问localhost:3000/foo-7):

Articles#show
Title is foo, ID is 7

全部完成!

答案 2 :(得分:1)

我的建议是使用before_save回调(我没有尝试过代码,但在概念上回答):

Slug模型

before_save :check_and_generate_unique_slug

private

def check_and_generate_unique_slug
  is_unique = Slug.where(url: self.url).count > 0 ? false : true

  unless is_unique
    #if say previous url was foo-1, self.url.last.to_i gives 1
    new_url = "foo-#{self.url.last.to_i + 1}" #here it's foo-2
    self.url = new_url
    self.check_and_generate_unique_slug
  else
    self.url = new_url
  end
end

注意即使最初的网址是&#34; foo&#34;,"foo".last.to_i也会给0,下一个网址会是 foo - #{0 + 1} = foo-1

修改

更好的方式

def check_and_generate_unique_slug
  urls = Slug.pluck(:url).uniq
  #=> ["foo", "foo-1", "foo-3", "foo-2"]

  latest_url_code = urls.map{|a| a.last.to_i}.uniq.sort.last
  #=> urls.map{|a| a.last.to_i}.uniq.sort  gives  [0, 1, 2, 3]
  #=> last will give 3

  self.url = "foo-#{latest_url_code + 1}"
end

修改

如评论last could be a 2 digit

中所述

代码可以修改为:"foo-xxx".split("-").last.to_i

答案 3 :(得分:1)

试试这个

render() {
  return (
    {this.state.inputs.map(
      (inputName, i) => <input type="text" name={inputName} key={i} />
    )}
    <button ... onClick= { this.addInput }></button>
  );
}

function addInput() {
  let temp = this.state.inputs.slice();
  let size = this.state.inputs.length+1;
  this.setState({inputs: temp.push("price"+length)});
}

答案 4 :(得分:1)

试试这个:

class Slug < ActiveRecord::Base     

  before_validation :set_slug, :on => :create

  protected 

  def set_slug
    tmp = "#{ title.downcase.gsub(/[^a-z0-9]+/i, '-') }"
    i = 0
    self.url = loop do
      tmp.concat("-#{ i }") if i > 0
      i += 1
      break tmp unless Slug.exists?(url: tmp)
    end
  end

end

在控制台中试试。

tmp1 = Slug.create!(title: 'hello world')
tmp1.url #=> "hello-world"

tmp2 = Slug.create!(title: 'Hello World')
tmp2.url #=> "hello-world-1"