我正在使用friendly_id gem来阻止我的模型。因为当我输入相同的数据来检查时,slug必须是唯一的,我会在slug中附加一个长的哈希值。
Explore explore
Explore explore-7a8411ac-5af5-41a3-ab08-d32387679f2b
有没有办法告诉friendly_id提供更好的格式化帖子,例如explore-1
和explore-2
版本: friendly_id 5.0.4
答案 0 :(得分:10)
所以,如果有人在某个时候遇到过这个问题,我会更新我希望在tirdadc的评论中作为评论,但我不能(声誉不够)。那么,你走了:
从理论上讲,Tirdadc的答案是完美的,但不幸的是,在调用slug_candidates这一点时,还没有分配对象的id,所以你需要做一些小技巧。这里有一个完整的方法来获得一个带有对象id的slug:class YourModel < ActiveRecord::Base
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
after_create :remake_slug
# Try building a slug based on the following fields in
# increasing order of specificity.
def slug_candidates
[
:name,
[:name, :id],
]
end
def remake_slug
self.update_attribute(:slug, nil)
self.save!
end
#You don't necessarily need this bit, but I have it in there anyways
def should_generate_new_friendly_id?
new_record? || self.slug.nil?
end
end
所以你在创建对象之后基本上设置了slug,然后在创建对象之后,你将slug nil out并执行一个save,它会重新分配slug(现在id完整了)。在after_create调用中保存对象是否危险?可能,但它似乎对我有用。
答案 1 :(得分:9)
同意,这似乎是非常粗暴的行为。
如果查看friendly_id/slugged.rb
的代码,有两个函数处理冲突解决逻辑:
def resolve_friendly_id_conflict(candidates)
candidates.first + friendly_id_config.sequence_separator + SecureRandom.uuid
end
# Sets the slug.
def set_slug(normalized_slug = nil)
if should_generate_new_friendly_id?
candidates = FriendlyId::Candidates.new(self, normalized_slug || send(friendly_id_config.base))
slug = slug_generator.generate(candidates) || resolve_friendly_id_conflict(candidates)
send "#{friendly_id_config.slug_column}=", slug
end
end
所以,这个想法只是为了修补它。我看到两个选项:
只需修补resolve_friendly_id_conflict
,添加随机后缀。
更改两种方法的逻辑,意图尝试所有候选人,直到slug_generator.generate(candidates)
返回非空的内容。如果所有候选人都提供nil
,那么请回退到resolve_friendly_id_conflict
方法。
使用这种技术,当slug不唯一时,你可以使用slug候选者来附加模型的id
。
理想情况下,如果gem的作者添加了一个配置选项来处理独特的slugs解析(方法符号或proc将生成器和候选者作为参数),或者只是检查模型是否响应某种方法,那将是很好的。
此外,在某些使用案例中,根本不需要独特的段塞分辨率。例如,如果我们只想依靠validates_uniqueness_of :slug
或候选人的唯一性验证。
答案 2 :(得分:7)
如果您想在处理冲突时避免使用slu U中的UUID,我建议使用:scoped
模块。这是文档和示例:
http://norman.github.io/friendly_id/file.Guide.html#Unique_Slugs_by_Scope
尝试使用:scope => :id
,因为无论如何每个ID都是唯一的,看看它是否适合您。
更新:
为了获得您想要的内容,您现在可以在第5版中使用candidates
:
class YourModel < ActiveRecord::Base
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
# Try building a slug based on the following fields in
# increasing order of specificity.
def slug_candidates
[
:name,
[:name, :id],
]
end
end
答案 3 :(得分:3)
今天我遇到了这个问题,虽然其他答案帮助我开始,但我并不满意,因为和你一样,我想让slug像explore
,explore-2
那样按顺序出现, explore-3
。
所以,这就是我修复它的方法:
class Thing < ActiveRecord::Base
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
validates :name, presence: true, uniqueness: { case_sensitive: false }
validates :slug, uniqueness: true
def slug_candidates
[:name, [:name, :id_for_slug]]
end
def id_for_slug
generated_slug = normalize_friendly_id(name)
things = self.class.where('slug REGEXP :pattern', pattern: "#{generated_slug}(-[0-9]+)?$")
things = things.where.not(id: id) unless new_record?
things.count + 1
end
def should_generate_new_friendly_id?
name_changed? || super
end
end
我使用了:slug
的唯一性验证,以防这个模型在并发代码中使用。
在这里你可以看到它有效:
irb(main):001:0> Thing.create(name: 'New thing')
(0.1ms) begin transaction
(0.2ms) SELECT COUNT(*) FROM "things" WHERE (slug REGEXP 'new-thing(-[0-9]+)?$')
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1 [["slug", "new-thing"]]
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE LOWER("things"."name") = LOWER('New thing') LIMIT 1
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE "things"."slug" = 'new-thing' LIMIT 1
SQL (0.4ms) INSERT INTO "things" ("name", "slug") VALUES (?, ?) [["name", "New thing"], ["slug", "new-thing"]]
(115.7ms) commit transaction
=> #<Thing id: 1, name: "New thing", slug: "new-thing">
irb(main):002:0> Thing.create(name: 'New thing')
(0.2ms) begin transaction
(0.9ms) SELECT COUNT(*) FROM "things" WHERE (slug REGEXP 'new-thing(-[0-9]+)?$')
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1 [["slug", "new-thing"]]
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1 [["slug", "new-thing-2"]]
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE LOWER("things"."name") = LOWER('New thing') LIMIT 1
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE "things"."slug" = 'new-thing-2' LIMIT 1
(0.1ms) rollback transaction
=> #<Thing id: nil, name: "New thing", slug: "new-thing-2">
irb(main):003:0> Thing.create(name: 'New-thing')
(0.2ms) begin transaction
(0.5ms) SELECT COUNT(*) FROM "things" WHERE (slug REGEXP 'new-thing(-[0-9]+)?$')
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1 [["slug", "new-thing"]]
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1 [["slug", "new-thing-2"]]
Thing Exists (0.3ms) SELECT 1 AS one FROM "things" WHERE LOWER("things"."name") = LOWER('New-thing') LIMIT 1
Thing Exists (0.3ms) SELECT 1 AS one FROM "things" WHERE "things"."slug" = 'new-thing-2' LIMIT 1
SQL (0.4ms) INSERT INTO "things" ("name", "slug") VALUES (?, ?) [["name", "New-thing"], ["slug", "new-thing-2"]]
(108.9ms) commit transaction
=> #<Thing id: 2, name: "New-thing", slug: "new-thing-2">
irb(main):004:0> Thing.create(name: 'New!thing')
(0.2ms) begin transaction
(0.6ms) SELECT COUNT(*) FROM "things" WHERE (slug REGEXP 'new-thing(-[0-9]+)?$')
Thing Exists (0.0ms) SELECT 1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1 [["slug", "new-thing"]]
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE ("things"."id" IS NOT NULL) AND "things"."slug" = ? LIMIT 1 [["slug", "new-thing-3"]]
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE LOWER("things"."name") = LOWER('New!thing') LIMIT 1
Thing Exists (0.1ms) SELECT 1 AS one FROM "things" WHERE "things"."slug" = 'new-thing-3' LIMIT 1
SQL (0.1ms) INSERT INTO "things" ("name", "slug") VALUES (?, ?) [["name", "New!thing"], ["slug", "new-thing-3"]]
(112.4ms) commit transaction
=> #<Thing id: 3, name: "New!thing", slug: "new-thing-3">
irb(main):005:0>
另外,如果你使用sqlite3适配器,你需要安装sqlite3_ar_regexp
gem(它不会很快,因为SQLite没有REGEXP()而且它会评估Ruby代码)。