如何使用Devise“软删除”用户


67

我目前在Rails项目中使用Devise进行用户注册/身份验证。当用户想要取消其帐户时,该用户对象被销毁,这使我的应用程序处于不希望的状态。

实现“软删除”的最简单方法是什么,即仅删除个人数据并将用户标记为已删除?我仍然想保留所有记录关联。

我认为我必须首先为用户介绍一个新的“已删除”列。但是随后,我在用户的个人资料视图中停留了以下默认代码:

<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete %>.</p>

在哪里可以找到:delete方法?我应该如何覆盖默认的Devise方法?


删除用户该怎么办?应该删除他的所有帖子/活动还是什么?
khelll 2011年

我想保留与其他对象的所有关联,但只删除个人数据(名称/电子邮件地址)并禁用登录。
slhck 2011年

Answers:


103

我建议destroy用户模型上的重写方法可以简单地执行update_attribute(:deleted_at, Time.current)(而不是实际销毁),但是将来与标准API的这种偏离可能会变得繁重,因此,这里介绍了如何修改控制器。

Devise提供了许多默认控制器。定制它们的最佳方法是继承相应的devise控制器来创建自己的控制器。在这种情况下,我们正在谈论Devise::RegistrationsController-通过查看源很容易识别。因此,创建一个新的控制器。

class RegistrationsController < Devise::RegistrationsController
end

现在,我们有了自己的控制器,它完全继承了所有由设备提供的逻辑。下一步是告诉devise使用它而不是默认值。在您的路线中您devise_for有线。应该更改为包括注册控制器。

devise_for :users, :controllers => { :registrations => 'registrations' } 

这似乎很奇怪,但是很有意义,因为默认情况下它是“设计/注册”,而不仅仅是“注册”。

下一步是覆盖destroy注册控制器中的操作。使用时registration_path(:user), :method => :delete-它就是链接的地方。要destroy注册控制器的作用。

目前,设计工作如下。

def destroy
  resource.destroy
  set_flash_message :notice, :destroyed
  sign_out_and_redirect(self.resource)
end

我们可以改用此代码。首先,我们为User模型添加新方法。

class User < ActiveRecord::Base
  def soft_delete
    # assuming you have deleted_at column added already
    update_attribute(:deleted_at, Time.current)
  end
end

# Use this for Devise 2.1.0 and newer versions
class RegistrationsController < Devise::RegistrationsController

  def destroy
    resource.soft_delete
    Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
    set_flash_message :notice, :destroyed if is_navigational_format?
    respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
  end
end

# Use this for older Devise versions
class RegistrationsController < Devise::RegistrationsController
  def destroy
    resource.soft_delete
    set_flash_message :notice, :destroyed
    sign_out_and_redirect(resource)
  end
end

Now you should be all set. Use scopes to filter out deleted users.


Thank you for the answer, I've been able to cook my own solution with your input!
slhck

1
Great thanks for the answer! Please fix the typo: update_attribtue
kravc

2
How can I let the user re-signup at a later time with the same email?
Jonathan Roy

@elsurudo, I found this to do it: stackoverflow.com/questions/8886317/…
Jonathan Roy

2
Make sure to also override the new sessions controller to not allow people to sign in if they've deleted their account.
corbin

91

Adding onto hakunin's answer:

To prevent "soft deleted" users from signing in, override active_for_authentication? on your User model:

def active_for_authentication?
  super && !deleted_at
end

3
+1 for pointing out the correct hook that also kicks out newly deleted users from their current session.
Martin T.

2
This is really the best answer. Here are the docs.
d_ethier

6
Thanks for this, the only part I didn't like was the message being shared with the account not confirmed. See my answer on how to change that message: stackoverflow.com/a/14966003/637094
Leonel Galán

To add to d_either's reference, it seems like you need to also define inactive_message in addition to active_for_authentication? on your User model. This is to provide a message for why the active_for_authentication? would return false.
Joey

1
If you want the same validation message as if the user record was actually not present, you might want to define inactive_message as: def inactive_message return self.active ? super : I18n.t('devise.failure.not_found_in_database') end
Joey

10

You could use acts_as_paranoid for your User model, which sets a deleted_at instead of deleting the object.


I've heard from that one before. How would it interact with Devise? Where would I need to change anything apart from installing acts_as_paranoid?
slhck

@shick ...you should add acts_as_paranoid line to your User model(or any other model you need soft delete).
rubyprince

1
This gem has a lot of forks/versions and I could not find a working one for Rails 3.2.x
Ivailo Bardarov

3
@IvailoBardarov There's a new gem called paranoia for Rails 3: github.com/radar/paranoia
graywh

7

A complete tutorial can be found at Soft Delete a Devise User Account on the Devise wiki page.

Summary:
1. Add a "deleted_at" DATETIME column
2. Override users/registrations#destroy in your routes
3. Override users/registrations#destroy in the registrations controller
4. Update user model with a soft_delete & check if user is active on authentication
5. Add a custom delete message

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.