【问题标题】:How do I elegantly handle two-sided relations with Rails translations?我如何优雅地处理 Rails 翻译的双边关系?
【发布时间】:2014-10-09 21:04:08
【问题描述】:

我有一个family_tree,有人可以将他们的亲戚添加到树中。

那么发生的情况是,为每个family_tree 条目创建了一个membership 记录。

但是,如果Son 添加了Dad,我们应该能够更新爸爸的家谱以将“儿子”添加到视图中的树中。解决此问题的最佳 Rails 方法是什么?我知道 Rails 会进行很多本地翻译,以及复数形式等。无论如何,我可以利用它来做我想做的事吗?

另外,再次处理这些东西的类/模块是什么?主动支持?

这是我的User 模型:

# == Schema Information
#
# Table name: users
#
#  id                     :integer          not null, primary key
#  email                  :string(255)      default(""), not null
#  encrypted_password     :string(255)      default(""), not null
#  reset_password_token   :string(255)
#  reset_password_sent_at :datetime
#  remember_created_at    :datetime
#  sign_in_count          :integer          default(0), not null
#  current_sign_in_at     :datetime
#  last_sign_in_at        :datetime
#  current_sign_in_ip     :string(255)
#  last_sign_in_ip        :string(255)
#  created_at             :datetime
#  updated_at             :datetime
#  name                   :string(255)
#  confirmation_token     :string(255)
#  confirmed_at           :datetime
#  confirmation_sent_at   :datetime
#  unconfirmed_email      :string(255)
#  invitation_relation    :string(255)
#  avatar                 :string(255)
#
class User < ActiveRecord::Base
  has_one :family_tree, dependent: :destroy
  has_many :memberships, dependent: :destroy
  has_many :nodes, dependent: :destroy
  has_many :participants, dependent: :destroy    
end

FamilyTree.rb

# == Schema Information
#
# Table name: family_trees
#
#  id         :integer          not null, primary key
#  name       :string(255)
#  user_id    :integer
#  created_at :datetime
#  updated_at :datetime
#

class FamilyTree < ActiveRecord::Base
  belongs_to :user
  has_many :memberships, dependent: :destroy
  has_many :members, through: :memberships, source: :user, dependent: :destroy
  has_many :nodes, dependent: :destroy
end

Membership.rb:

# == Schema Information
#
# Table name: memberships
#
#  id             :integer          not null, primary key
#  family_tree_id :integer
#  user_id        :integer
#  created_at     :datetime
#  updated_at     :datetime
#  relation       :string(255)
#

class Membership < ActiveRecord::Base    
  belongs_to :family_tree
  belongs_to :user      
end

Node.rb

# == Schema Information
#
# Table name: nodes
#
#  id             :integer          not null, primary key
#  name           :string(255)
#  family_tree_id :integer
#  user_id        :integer
#  media_id       :integer
#  media_type     :string(255)
#  created_at     :datetime
#  updated_at     :datetime
#  circa          :datetime
#  is_comment     :boolean
#

class Node < ActiveRecord::Base
  belongs_to :family_tree
  belongs_to :user
  belongs_to :media, polymorphic: true, dependent: :destroy
  has_many :comments, dependent: :destroy
  has_many :node_comments, dependent: :destroy    
end

我的_tree.html.erb 看起来像这样(为简洁起见被截断):

      <li class="tree-item-name"><a href="#">Great Grandparents</a>
        <ul>
          <li><% if relative.humanize == "Great Grandfather" || relative.humanize == "Great Grandmother" %>
            <%= link_to image_tag(membership.user.avatar.url, size: "48x48", :class => "img-circle") , family_tree_path(membership.user.family_tree), :target => '_blank' %>
                <%= link_to membership.user.name, family_tree_path(membership.user.family_tree), :target => '_blank'%>
              <% else %>
              None added yet, add them <%= link_to 'here', "#" , class: 'btn invite popupbox','data-popup' => 'invite_friend' %>
            <% end %>
          </li>
        </ul>
      </li>
      <li class="tree-item-name"><a href="#">Grandparents</a>
        <ul>
          <li><% if relative.humanize == "Grandfather" || relative.humanize == "Grandmother" %>
            <%= link_to image_tag(membership.user.avatar.url, size: "48x48", :class => "img-circle") , family_tree_path(membership.user.family_tree), :target => '_blank' %>
                <%= link_to membership.user.name, family_tree_path(membership.user.family_tree), :target => '_blank' %>
              <% else %>
              None added yet, add them <%= link_to 'here', "#" , class: 'btn invite popupbox','data-popup' => 'invite_friend' %>
            <% end %>
          </li>
        </ul>
      </li>

【问题讨论】:

  • 能否请您:删除所有不相关的代码(设计、attr_accessible、Carrierwave、其他关联等);明确新的家庭关系是如何建立的(邀请令牌的作用是什么?)。
  • 此外,如果可能且适当,请从低级细节中抽象出来作为邀请标记。提出关于关联的问题。
  • 最后一件事:您提到了翻译。你什么意思? Rails 改变关联名称的能力?你想知道它在视图中自动生成字符串吗?
  • @tompave 我清理了它。当我说翻译时,是的,我最初是在考虑屈折变化。本质上,应用程序最好根据 current_user 的性别自动知道关系的另一方是什么。例如。如果我是男性,那么对于我的叔叔来说,我永远是一个侄子。如果我是女性,我永远是侄女。同样,如果我是男性,我将是祖父母的孙子和父母的儿子。这就是我的意思。
  • threebit.net/tutorials/nestedset/tutorial1.html 阅读树实现背后的理论。很棒的嵌套集可以在一个查询中构建整个树。它使跨级别的遍历变得微不足道。此外,树结构和导航是一个已解决的问题。如果您构建一个自定义解决方案,您将花费您的青春努力使其满足您当前和未来的需求:-)

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


【解决方案1】:

我会使用你在问题中定义的相同关系,除了这部分:

class Membership < ActiveRecord::Base    
  belongs_to :family_tree
  belongs_to :user_one, class_name: 'User'
  belongs_to :user_two, class_name: 'User' # I actually have no idea how to call them!
  belongs_to :relation # to hold values likes 'Son', 'Dad', etc.
  # The model Relation would be as simple as a name and internal reference, nothing else.
  # (internal_reference is here to solve the translation problems and other stuff you will understand with the following code)

使用回调after_create 来撤销创建的成员资格:

def create_reverse_membership
  user_one_is_female = user_one.gender == 'female'
  user_two_is_female = user_two.gender == 'female'
  son_or_daughter = user_one_is_female ? :daughter : :son
  father_or_mother = user_two_is_female ? :mother : :father

  case relation.internal_reference.to_sym
  when :son
    relation = Relation.find_by_internal_reference(father_or_mother)
    membership = Membership.where(relation_id: relation.id, user_one: user_two.id, user_two: user_one.id).first
    if membership.present?
      # This means the reverse membership already exists, do not call Membership.create here because it would cause and endless loop with the callback
    else
      membership = Membership.create(relation_id: relation.id, user_one: user_two, user_two: user_one)
    end
  when :father
    # almost same logic but with `son_or_daughter`
  when :mother
  else

  end
end

英语不是我的母语,这段代码可能缺乏一致性(连贯性、逻辑性)。

希望这会有所帮助!

【讨论】:

  • internal_reference 来自哪里?那是Rails方法吗?如果有,有链接吗?
  • 这不是 Rails 方法,它是存储在数据库中的值,由您(应用程序管理员)控制。因此,您可以使用相同的关系,使用 internal_reference,并在需要时将其翻译成多种语言。此外,它并不完全依赖于这个词,而不是使用dad = Relation.where(name: 'Dad').first,而是使用 internal_reference,即使对于“爸爸”、“父亲”、“爸爸”(法语)等,它也总是相同的。
  • relation_name 是怎么来的?还是您的意思是relation.name?即self.relation.name 其中selfMembership 类的一个实例?另外,这种方式是否意味着当我创建Relation 模型时,我必须为每种关系类型预先填充一堆记录?即son, daughter, father, mother, grandfather, etc.?
  • 对此还有什么想法吗?请参阅我之前的问题。还在吗?
  • 很抱歉@marcamillion 我没有看到你之前的评论。 relation_name 是个错误,我将其替换为 relation.internal_reference,其中 relation 是 Relation 类的一个实例。是的,这意味着您必须使用您命名的关系预先填充数据库。 (对于这种情况,frozen_record gem 可以帮助您)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-26
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多