I'm current using acts_as_paranoid and friendly_id (5.0.1) on a model and when I destroy a model and try to create a new one that will generate the same slug I get:
ERROR: duplicate key value violates unique constraint "index_papers_on_slug"
I need to somehow get the code that checks if a slug already exists check within the scope of all of the objects not just the non-deleted ones.
How can I get friendly_id to use with_deleted when checking if a slug already exists. I should note that I am also using slug history which may be complicating things further.
Upon digging deeper I realized that since I am using history the slug is being fully deleted while the object is just being soft deleted:
DELETE FROM "friendly_id_slugs" WHERE "friendly_id_slugs"."id" = $1 [["id", 9423]]
So, I just need to figure out how to prevent this and I should be okay since it looks like the friendly_id code itself is already using unscoped when trying to find a valid slug.
Adding the following to the model allowed me to overrride the dependent destroy on the slugs
def has_many_dependent_for_slugs; end
The solution comes from a comment on this github issue.
Friendly_id has a module called scoped which allows you to generate unique slugs within a scope. So, probably
class Paper < ActiveRecord::Base
extend FriendlyId
friendly_id :title, :use => :scoped, :scope => :unscoped
end
will resolve the problem.
I just came across this issue too and I figured two different ways to address it.
Solution 1:
Use dependent: false.
friendly_id :title, dependent: false
Solution: 2
Overcoming this problem without overriding the dependent destroy for anyone that wants to avoid that.
The friendly_id gem uses a method called scope_for_slug_generator to set the model scope. That means we could override this method by adding the following to app/config/initializers/friendly_id.rb.
module FriendlyId
def scope_for_slug_generator
scope = if self.class.base_class.include?(Paranoia)
self.class.base_class.unscoped.with_deleted
else
self.class.base_class.unscoped
end
scope = self.class.base_class.unscoped
scope = scope.friendly unless scope.respond_to?(:exists_by_friendly_id?)
primary_key_name = self.class.primary_key
scope.where(self.class.base_class.arel_table[primary_key_name].not_eq(send(primary_key_name)))
end
end
Related
I'm using FriendlyId gem on Ruby on Rails 5.
Is there any way to stop FriendlyId to create a different slug if the current one has already been taken? I'd like the user to have full control over the slug.
Add this method to your Model.
def should_generate_new_friendly_id?
new_record?
end
or modify the content of the method to match your needs.
In order for my Rails 4 app to have vanity urls, I have a polymorphic Slug table (id, url). Then in my Article model I have the following to create the slug:
class Article < ActiveRecord::Base
has_many :slugs, as: :sluggable, dependent: :destroy
after_save :create_slug
def create_slug
return if !url_changed? || url == slugs.last.try(:url)
#re-use old slugs
previous = slugs.where('lower(url) = ?', url.downcase)
previous.delete_all
slugs.create!(url: url)
end
end
The #article.url is currently being set in the controller with:
#article.url = #article.title.parameterize
This works really well unless there is a conflict, meaning an article is being created with a title already in use. In that case Rails error's with:
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint
I'm thinking I need create_slug to check first if the value exists, if not then create the slug however, if the value does exist, append a -1 or -2 etc... to the end of the URL until the URL is unique. Does this sound right? What's the best way to handle/code for this scenario?
The old behavior was implemented in the special module. But at the moment it has not been released yet. So, if you want to restore old behavior you can switch friebdly_id in your Gemfile on Github and add sequentially_slugged to the modules list.
If you don't want to move for any gems then, A simplest thing for you is use the below code for making the URL's better....
def to_param
"#{id}-#{slug}"
end
You can update the method code and make urls what ever you want.
I'm new to rails and currently involved in an internship and I was assigned to use the friendly_id gem for my tournament class, this is part of the code in it:
class Tournament < ApplicationRecord
extend FriendlyId
friendly_id :url_id
...
end
I don't use a slug since I have a url_id attribute that stores my desired url and when I try with the old .../tournaments/1 everything's all good but with .../tournaments/example I get "example is not a valid value for id" with code 103, status 400. Any ideas what the problem might be?
You have to update your controller for Tournaments so that it uses friendly.find method instead of the find.
# Change this:
Tournament.find(params[:id])
# to
Tournament.friendly.find(params[:id])
I have a Company Model, and i am using friendly_id like this
friendly_id :name, use: :slugged
But since there can be many Company with the same name (different branches). I am trying to handle that case by using city attribute from the address of the Company.
But the Company address is stored in a different table Address.
so company.address.city gives me the city of the company.
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, :city]
]
end
I'm aware i can do something like above. But since city is not an attribute of company how can i achieve this?
Update:
Possible solution for this is to create a helper method city which returns the company's city.
But the problem never was that.
The version of friendly_id i am using is 4.0.10.1
and the feature which enables the usage of slug_candidates are available in versions 5 and above.
I tried updating the gem. But it wont get updated as version 5 has dependency on activerecord 4.0 and rails has dependency on activerecord 3.2.13
It's kind of a deadlock. Don't know what to do
class Company < ActiveRecord::Base
.............................
def city
self.address.city
end
end
The context is pretty simple, I have a Course model that extends from FriendlyId as follow:
extend FriendlyId
friendly_id :friendly_name, use: [:slugged, :history]
def friendly_name
slugs = [self.type_name, self.name]
slugs << self.city.name if self.city
slugs << self.structure.name if self.structure
return slugs
end
And if I create a course with same type, name, city and structure I get the following error:
!! #<ActiveRecord::RecordNotUnique: PG::Error: ERROR: duplicate key value
violates unique constraint "index_courses_on_slug"
DETAIL: Key (slug)=(cours-sevillanas-copie-paris-12-la-trianera) already exists.
I don't understand why FriendlyId doesn't add a sequence number at the end of the slug...
Any suggestion is welcomed.
I have tried to return a string instead of an array in the friendly_name method but the error persists.
Edit
Removing :history fixes the problem.
I'have also tried other branches (4.0-stable, 4.1.x) of FriendlyId but it doesn't fixes the problem.
I had the same problem with the :history features: it's because FriendlyId will use a separate table to store slugs, and will not check the existing slug column.
You can create a migration and re-save the whole table to generate missing slugs in the new slugs table.
For example:
def up
MyModel.all.map(&:save)
end