如何在视图页中显示对 ActiveRecord 中的记录所做的更新的列表

How to show in a view page a list of updates made to a record in ActiveRecord

提问人:Poppy 提问时间:3/28/2022 更新时间:3/28/2022 访问量:80

问:

我正在用 Ruby 制作一个简单的联系人列表应用程序。 在每个联系人的显示视图中,我想显示基本信息(first_name、last_name、电子邮件和电话),以及该联系人是否有任何更改或更新。 例如,如果他们将电子邮件地址从 [email protected] 更改为 [email protected],并且此更改是在 2022 年 3 月 27 日进行的,我希望此信息的列表也显示在节目页面上。

我想这与包括 ActiveModel::D irty 或回调有关,但不太确定如何实现其中任何一个!

这是我的 Contact.rb 模型文件

# frozen_string_literal: true

class Contact < ApplicationRecord
  before_validation :normalize_first_name, on: :create
  before_validation :normalize_last_name, on: :create
  # after_save :print_changes
  # before_update :check_for_changes
  # after_update :check_for_previous_changes

  validates_presence_of :first_name, :last_name, :email, :phone
  validates :phone, numericality: { only_integer: true }
  validates :email, uniqueness: true
  validates :email,
            format: { with: /\A(.+)@(.+)\z/, message: 'Email invalid' },
            uniqueness: { case_sensitive: false },
            length: { minimum: 4, maximum: 95 }

  private

  def normalize_first_name
    self.first_name = first_name.downcase.titleize
  end

  def normalize_last_name
    self.last_name = last_name.downcase.titleize
  end

  # def check_for_changes
  #   puts changes # => {"name"=>["Nimish Gupta", "Nimish Mahajan"], "updated_at"=>[Tue, 20 Nov 2018 00:02:14 PST -08:00, Tue, 20 Nov 2018 00:06:15 PST -08:00]}
  #   puts previous_changes # => {} At this point this will be empty beacuse changes are not made to DB yet
  # end

  # def check_for_previous_changes
  #   # but you can make use of previous_changes method to know what change has occurred.
  #   puts previous_changes # => {"name"=>["Nimish Gupta", "Nimish Mahajan"], "updated_at"=>[Tue, 20 Nov 2018 00:06:15 PST -08:00, Tue, 20 Nov 2018 00:08:07 PST -08:00]}
  # end
end

这是联系人控制器

class ContactsController < ApplicationController
  before_action :set_contact, only: %i[show edit update destroy]

  # GET /contacts or /contacts.json
  def index
    @contacts = Contact.all
    @contact = Contact.new
  end

  # GET /contacts/1 or /contacts/1.json
  def show
  end

  # GET /contacts/new
  def new
    @contact = Contact.new
  end

  # GET /contacts/1/edit
  def edit; end

  # POST /contacts or /contacts.json
  def create
    @contact = Contact.new(contact_params)

    respond_to do |format|
      if @contact.save
        format.html { redirect_to contact_url(@contact), notice: 'Contact was successfully created.' }
        format.json { render :show, status: :created, location: @contact }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @contact.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /contacts/1 or /contacts/1.json
  def update
    respond_to do |format|
      if @contact.update(contact_params)
        format.html { redirect_to contact_url(@contact), notice: 'Contact was successfully updated.' }
        format.json { render :show, status: :ok, location: @contact }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @contact.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /contacts/1 or /contacts/1.json
  def destroy
    @contact.destroy

    respond_to do |format|
      format.html { redirect_to contacts_url, notice: 'Contact was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_contact
    @contact = Contact.find(params[:id])
  end

  # Only allow a list of trusted parameters through.
  def contact_params
    params.require(:contact).permit(:first_name, :last_name, :email, :phone)
  end
end

以及我希望显示更改的“显示”页面

<p id="notice"><%= notice %></p>

<h1>This is the show page for each contact</h1>
<p>
  <strong>First name:</strong>
  <%= @contact.first_name %>
</p>

<p>
  <strong>Last name:</strong>
  <%= @contact.last_name %>
</p>

<p>
  <strong>Email:</strong>
  <%= @contact.email %>
</p>

<p>
  <strong>Phone:</strong>
  <%= @contact.phone %>
</p>

<%= link_to 'Edit', edit_contact_path(@contact) %> |
<%= link_to 'Back', contacts_path %>

<h1>Contact history</h1>

<%# <%= check_for_changes(@contact) %>


<table>
  <thead>
    <tr>
      <th>Field</th>
      <th>Updated</th>
      <th>Previous entry</th>
    </tr>
  </thead>

  <tbody>
    <%# <% @contacts.each do |contact| > %>
      <tr>
        <td>Email</td>
        <td>27/03/2022</td>
        <td>[email protected]</td>
        <%# <td><%= contact.first_name ></td> %>
        <%# <td><%= contact.last_name ></td> %>
        <%# <td><%= contact.email ></td> %>
      </tr>
    <%# <% end > %>
  </tbody>
</table>
Ruby-on-Rails ActiveRecord 回调

评论

0赞 dbugger 3/28/2022
查看 papertrail gem github.com/paper-trail-gem/paper_trail

答:

0赞 Siim Liiser 3/28/2022 #1

ActiveModel::Dirty仅允许您跟踪所做的更改的同一请求中的更改。一旦联系人与新更改一起存储到数据库中并且请求结束,就没有内置方法来获取旧值了。

您必须自己构建解决方案。您将需要一个模型来存储所做的所有更改,这些更改属于您的联系人。从现在开始,我们称之为。ContactChange

为了在更改联系人时创建联系人更改,可以使用这些方法并将其放入回调中,也可以创建服务对象来更新联系人和创建联系人更改。ActiveModel::Dirtyafter_save

或者,如果您只想要一个开箱即用的解决方案,那么有一些宝石。例如,纸质记录

评论

0赞 Poppy 3/29/2022
非常感谢您指向宝石paper_trail指针!我一直在遵循非常有用的教程,它完全符合我在这里的需求(对于任何可能有帮助的人!driftingruby.com/episodes/auditing-with-paper-trail