rails 两表关联查询_Rails中的单表继承与多态关联:找到适合您的方法

rails 两表关联查询

by Haley Mnatzaganian

通过海利·姆纳扎卡尼安

Rails中的单表继承与多态关联:找到适合您的方法 (Single-table inheritance vs. polymorphic associations in Rails: find what works for you)

If you’ve ever created an application with more than one model, you’ve had to think about what type of relationships to use between those models.

如果创建的应用程序具有多个模型,则必须考虑在这些模型之间使用哪种类型的关系。

As an application’s complexity grows, it can be difficult to decide which relationships should exist between your models.

随着应用程序复杂性的增加,可能很难确定模型之间应该存在哪些关系。

A situation that frequently comes up is when several of your models need to have access to the functionality of a third model. Two methods that Rails gives us to deal with this event are single-table inheritance and polymorphic association.

经常出现的情况是您的多个模型需要访问第三个模型的功能。 Rails给我们提供了两种方法来处理此事件: 单表继承多态关联。

In Single-Table Inheritance (STI), many subclasses inherit from one superclass with all the data in the same table in the database. The superclass has a “type” column to determine which subclass an object belongs to.

在单表继承(STI)中,许多子类都从一个超类继承而来,所有数据都在数据库的同一表中。 超类具有“类型”列,用于确定对象属于哪个子类。

In a polymorphic association, one model “belongs to” several other models using a single association. Each model, including the polymorphic model, has its own table in the database.

多态关联中 ,一个模型使用单个关联“属于”其他几个模型。 每个模型(包括多态模型)在数据库中都有自己的表。

Let’s take a look at each method to see when we would use them.

让我们看一下每种方法,看看何时使用它们。

单表继承 (Single-Table Inheritance)

A great way to know when STI is appropriate is when your models have shared data/state. Shared behavior is optional.

知道何时使用STI的一种好方法是您的模型何时共享数据/状态 。 共享行为是可选的。

Let’s pretend we are creating an app that lists different vehicles that are for sale at a local dealership. This dealership sells cars, motorcycles, and bicycles.

假设我们正在创建一个应用程序,其中列出了要在当地经销商处出售的不同车辆。 该经销店销售汽车,摩托车和自行车。

(I know dealerships don’t sell bicycles, but bear with me for a minute — you’ll see where I’m going with this.)

(我知道经销商不销售自行车,但请耐心等待一分钟-您会看到我要去的地方。)

For each vehicle, the dealership wants to track the price, color, and whether the vehicle was purchased. This situation is a perfect candidate for STI, because we are using the same data for each class.

对于每辆车,经销商都希望跟踪价格,颜色以及是否购买了该车。 这种情况非常适合STI,因为我们为每个类别使用相同的数据。

We can create a superclass Vehicle with the attributes for color, price, and purchased. Each of our subclasses can inherit from Vehicle and can all get those same attributes in one fell swoop.

我们可以创建具有颜色,价格和购买属性的超类Vehicle 。 我们的每个子类都可以从Vehicle继承,并且可以一次获得所有相同的属性。

Our migration to create the vehicles table might look like this:

我们创建车辆表的迁移可能如下所示:

class CreateVehicles < ActiveRecord::Migration[5.1]  def change                               create_table :vehicles do |t|                                   t.string :type, null: false                               t.string :color                                   t.integer :price                                  t.boolean :purchased, default: false                                                          end                           end                       end

It is important that we create the type column for the superclass. This tells Rails that we are using STI and want all the data for Vehicle and its subclasses to be in the same table in the database.

创建超类的type列很重要。 这告诉Rails我们正在使用STI,并且希望Vehicle及其子类的所有数据都在数据库的同一表中。

Our model classes would look like this:

我们的模型类如下所示:

class Vehicle < ApplicationRecordend
class Bicycle < Vehicleend
class Motorcycle < Vehicleend
class Car < Vehicleend

This setup is great because any methods or validations in the Vehicle class are shared with each of its subclasses. We can add unique methods to any of the subclasses as needed. They are independent of each other and their behavior is not shared horizontally.

该设置非常好,因为Vehicle类中的任何方法或验证都与其每个子类共享。 我们可以根据需要向任何子类添加唯一方法。 它们彼此独立,并且它们的行为不是水平共享的。

Additionally, since we know that the subclasses share the same data fields, we can make the same calls on objects from different classes:

另外,由于我们知道子类共享相同的数据字段,因此我们可以对来自不同类的对象进行相同的调用:

mustang = Car.new(price: 50000, color: red)harley = Motorcycle.new(price: 30000, color: black)
mustang.price=> 50000
harley.price=> 30000
增加功能 (Adding functionality)

Now let’s say the dealer decides to collect some more information about the vehicles.

现在,假设经销商决定收集有关车辆的更多信息。

For Bicycles, she wants to know if each bike is a road, mountain, or hybrid bike. And for Cars and Motorcycles, she wants to keep track of the horsepower.

对于Bicycles ,她想知道每辆自行车是公路,山地还是混合动力自行车。 对于CarsMotorcycles ,她想跟踪马力。

So we create a migration to add bicycle_type and horsepower to the Vehicles table.

因此,我们创建了一个迁移,以将bicycle_typehorsepower添加到Vehicles表中。

All of a sudden, our models don’t perfectly share data fields anymore. Any Bicycle object will not have a horsepower attribute, and any Car or Motorcycle will not have a bicycle_type (hopefully — I’ll get to this in a moment).

突然之间,我们的模型不再完美地共享数据字段。 任何“ Bicycle对象都不会具有horsepower属性,任何“ Car或“ Motorcycle都不会具有bicycle_type (希望,我稍后bicycle_type到)。

Yet every bicycle in our table will have a horsepower field, and every car and motorcycle will have a bicycle_type field.

然而,我们餐桌上的每辆自行车都会有一个horsepower场,每辆汽车和摩托车都会有一个bicycle_type场。

This is where things can get sticky. A few issues can arise in this situation:

这是事情变得棘手的地方。 在这种情况下可能会出现一些问题:

  1. Our table will have a lot of null values (nil in Ruby’s case) since objects will have fields that don’t apply to them. These nulls can cause problems as we add validations to our models.

    我们的表将具有很多空值(在Ruby中nil ),因为对象将具有不适用于它们的字段。 当我们向模型添加验证时,这些nulls可能会导致问题。

  2. As the table grows, we can run into performance costs when querying if we don’t add filters. A search for a certain bicycle_type will look at every item in the table— so not only Bicycles, but Cars and Motorcycles also.

    随着表的增长,如果不添加过滤器,我们可能会在查询中遇到性能成本问题。 搜索某个bicycle_type将查看表中的每个项目 ,因此不仅是Bicycles ,还包括CarsMotorcycles

  3. As is, there is nothing stopping a user from adding “inappropriate” data to the wrong model. For example, a user with some know-how could create a Bicycle with a horsepower of 100. We would need validations and good app design to prevent the creation of an invalid object.

    照原样,没有什么可以阻止用户将“不适当的”数据添加到错误的模型中。 例如,具有某些专业知识的用户可以创建horsepower为100的Bicycle 。我们需要进行验证和良好的应用设计,以防止创建无效的对象。

So, as we can see, STI does have some flaws. It is great for applications where your models share data fields and aren’t likely to change.

因此,正如我们所看到的,STI确实存在一些缺陷。 对于您的模型共享数据字段并且不大可能更改的应用程序来说,它非常有用。

STI PROS:

STI优点:

  • Simple to implement

    易于实施
  • DRY — saves replicated code using inheritance and shared attributes

    DRY-使用继承和共享属性保存复制的代码
  • Allows subclasses to have own behavior as necessary

    允许子类根据需要拥有自己的行为

STI CONS:

STI缺点:

  • Doesn’t scale well: as data grows, table can become large and possibly difficult to maintain/query

    无法很好地扩展:随着数据的增长,表可能会变得很大,并且可能难以维护/查询
  • Requires care when adding new models or model fields that deviate from the shared fields

    添加新模型或偏离共享字段的模型字段时需要格外小心
  • (conditional) Allows creation of invalid objects if validations are not in place

    (有条件的)如果验证不到位,则允许创建无效的对象
  • (conditional) Can be difficult to validate or query if many null values exist in table

    (有条件)可能难以验证或查询表中是否存在许多空值

多态关联 (Polymorphic Associations)

With polymorphic associations, a model can belong_to several models with a single association.

使用多态关联,一个模型可以belong_to一个关联的多个模型。

This is useful when several models do not have a relationship or share data with each other, but have a relationship with the polymorphic class.

当几个模型没有关系或彼此共享数据,但与多态类有关系时,此功能很有用。

As an example, let’s think of a social media site like Facebook. On Facebook, both individuals and groups can share posts.

例如,让我们考虑一下Facebook之类的社交媒体网站。 在Facebook上,个人和团体都可以共享帖子。

The individuals and groups are not related (other than both being a type of user), and so they have different data. A group probably has fields like member_count and group_type that don’t apply to an individual, and vice-versa).

个人和组没有关联(除了都是用户类型以外),因此它们具有不同的数据。 群组可能具有诸如member_countgroup_type类的字段,不适用于个人,反之亦然。

Without polymorphic associations, we would have something like this:

没有多态关联,我们将有这样的事情:

class Post  belongs_to :person  belongs to :groupend
class Person  has_many :postsend
class Group  has_many :postsend

Normally, to find out who owns a certain profile, we look at the column that is the foreign_key. A foreign_key is an id used to find the related object in the related model’s table.

通常,要找出谁拥有某个配置文件,我们查看一下foreign_key列。 foreign_key是用于在相关模型的表中查找相关对象的ID。

However, our Posts table would have two competing foreign keys: group_id and person_id. This would be problematic.

但是,我们的Posts表将具有两个相互竞争的外键: group_idperson_id 。 这将是有问题的。

When trying to find the owner of a post, we would have to make a point to check both columns to find the correct foreign_key, rather than relying on one. What happens if we run into a situation where both columns have a value?

在尝试查找帖子的所有者时,我们必须指出一个要点,即同时检查两列以找到正确的foreign_key,而不要依赖一个。 如果我们遇到两个列都有值的情况,会发生什么?

A polymorphic association addresses this issue by condensing this functionality into one association. We can represent our classes like this:

多态关联通过将此功能浓缩为一个关联来解决此问题。 我们可以这样表示我们的类:

class Post  belongs_to :postable, polymorphic: trueend
class Person  has_many :posts, as :postableend
class Group  has_many :posts, as :postableend

The Rails convention for naming a polymorphic association uses “-able” with the class name (:postable for the Post class). This makes it clear in your relationships which class is polymorphic. But you can use whatever name for your polymorphic association that you like.

用于命名多态关联的Rails约定使用带有类名的“ -able”(对于Post类,为:postable )。 这样可以在您的关系中明确指出哪个类是多态的。 但是,您可以为自己的多态关联使用任何名称。

To tell our database we’re using a polymorphic association, we use special “type” and “id” columns for the polymorphic class.

为了告诉数据库我们正在使用多态关联,我们对多态类使用特殊的“类型”和“ id”列。

The postable_type column records which model the post belongs to, while the postable_id column tracks the id of the owning object:

postable_type列记录帖子所属的模型,而postable_id列跟踪拥有对象的ID:

haley = Person.first=> returns Person object with name: "Haley"
article = haley.posts.firstarticle.postable_type=> "Person"
article.postable_id=> 1 # The object that owns this has an id of 1 (in this case a      Person)
new_post = haley.posts.new()# Automatically fills in postable_type and postable_id using haley object

A polymorphic association is just a combination of two or more belongs_to associations. Because of this, you can act the same way you would when using two models that have a belongs_to association.

多态关联只是两个或多个belongs_to关联的组合。 因此,您可以使用与使用两个具有belongs_to关联的模型相同的方式进行操作。

Note: polymorphic associations work with both has_one and has_many associations.

注意:多态关联与has_one和has_many关联一起使用。

haley.posts# returns ActiveRecord array of posts
haley.posts.first.content=> "The content from my first post was a string..."

One difference is going “backwards” from a post to access its owner, since its owner could come from one of several classes.

一种区别是从帖子“向后”访问其所有者,因为其所有者可能来自多个类别之一。

To do that quickly, you need to add a foreign key column and a type column to the polymorphic class. You can find the owner of a post using postable:

要快速做到这一点,您需要向多态类添加外键列和类型列 。 您可以使用postable查找帖子的所有者:

new_post.postable=> returns Person object
new_post.postable.name=> "Haley"

Additionally, Rails implements some security within polymorphic relationships. Only classes that are part of the relationship can be included as a postable_type:

另外,Rails在多态关系中实现了一些安全性。 只有属于关系的类才可以作为postable_type包括在内:

new_post.update(postable_type: "FakeClass")=> NameError: uninitialized constant FakeClass
警告 (Warning)

Polymorphic associations come with one huge red flag: compromised data integrity.

多态关联带有一个巨大的危险信号: 数据完整性受损

In a normal belongs_to relationship, we use foreign keys for reference in an association.

在正常的belongs_to关系中,我们使用外键在关联中进行引用。

They have more power than just forming a link, though. Foreign keys also prevent referential errors by requiring that the object referenced in the foreign table does, in fact, exist.

但是,它们不仅仅具有链接的功能。 外键还通过要求确实存在外表中引用的对象来防止引用错误。

If someone tries to create an object with a foreign key that references a null object, they will get an error.

如果有人尝试使用引用空对象的外键创建对象,则会收到错误消息。

Unfortunately, polymorphic classes can’t have foreign keys for the reasons we discussed. We use the type and id columns in place of a foreign key. This means we lose the protection that foreign keys offer.

不幸的是, 由于我们讨论原因 ,多态类不能具有外键。 我们使用typeid列代替外键。 这意味着我们将失去外键提供的保护。

Rails and ActiveRecord help us out on the surface, but anyone with direct access to the database can create or update objects that reference null objects.

Rails和ActiveRecord可以帮助我们从表面上解决问题,但是直接访问数据库的任何人都可以创建或更新引用空对象的对象。

For example, check out this SQL command where a post is created even though the group it is associated with doesn’t exist.

例如,即使该帖子所关联的组不存在,也请签出此SQL命令在何处创建帖子。

Group.find(1000)=> ActiveRecord::RecordNotFound: Couldn't find Group with 'id'=1000
# SQLINSERT INTO POSTS (postable_type, postable_id) VALUES ('Group', 1000)=> # returns success even though the associated Group doesn't exist

Thankfully, proper application setup can prevent this from being possible. Because this is a serious issue, you should only use polymorphic associations when your database is contained. If other applications or databases need to access it, you should consider other methods.

幸运的是,正确的应用程序设置可以防止这种情况的发生。 因为这是一个严重的问题,所以仅在包含数据库时才应使用多态关联。 如果其他应用程序或数据库需要访问它,则应考虑其他方法。

Polymorphic association PROS:

多态关联PROS:

  • Easy to scale in amount of data: information is distributed across several database tables to minimize table bloat

    易于扩展数据量:信息分布在多个数据库表中,以最大程度地减少表膨胀
  • Easy to scale number of models: more models can be easily associated with the polymorphic class

    易于缩放的模型数量:可以轻松地将更多模型与多态类关联
  • DRY: creates one class that can be used by many other classes

    DRY:创建一个可以被许多其他类使用的类

Polymorphic association CONS

多态关联CONS

  • More tables can make querying more difficult and expensive as the data grows. (Finding all posts that were created in a certain time frame would need to scan all associated tables)

    随着数据的增长,更多的表会使查询变得更加困难和昂贵。 (查找在特定时间范围内创建的所有帖子都需要扫描所有关联的表)
  • Cannot have foreign key. The id column can reference any of the associated model tables, which can slow down querying. It must work in conjunction with the type column.

    不能有外键。 id列可以引用任何关联的模型表,这可能会降低查询速度。 它必须与type列一起使用。

  • If your tables are very large, a lot of space is used to store the string values for postable_type

    如果表非常大,则会使用大量空间来存储postable_type的字符串值

  • Your data integrity is compromised.

    您的数据完整性受到损害。

如何知道使用哪种方法 (How to know which method to use)

STI and polymorphic associations have some overlap when it comes to use cases. While not the only solutions to a “tree-like” model relationship, they both have some obvious advantages.

在用例方面,STI和多态关联有一些重叠。 虽然不是“树状”模型关系的唯一解决方案,但它们都有明显的优势。

Both the Vehicle and Postable examples could have been implemented using either method. However, there were a few reasons that made it clear which method was best in each situation.

VehiclePostable示例都可以使用这两种方法来实现。 但是,出于多种原因可以弄清楚哪种方法在每种情况下都是最佳的。

Here are four factors to consider when deciding whether either of these methods fits your needs.

在确定这两种方法是否适合您的需求时,需要考虑以下四个因素。

  1. Database structure. STI uses only one table for all classes in the relationship, while polymorphic associations use a table per class. Each method has its own advantages and disadvantages as the application grows.

    数据库结构。 STI对关系中的所有类仅使用一个表,而多态关联对每个类使用一个表。 随着应用程序的增长,每种方法都有其自身的优缺点。

  2. Shared data or state. STI is a great option if your models have many shared attributes. Otherwise a polymorphic association is probably the better choice.

    共享的数据或状态。 如果您的模型具有许多共享属性,则STI是一个不错的选择。 否则,多态关联可能是更好的选择。

  3. Future concerns. Consider how your application might change and grow. If you’re considering STI but think you’ll add models or model fields that deviate from the shared structure, you might want to rethink your plan. If you think your structure is likely to remain the same, STI will generally be faster for querying.

    未来的担忧。 考虑您的应用程序可能如何更改和增长。 如果您正在考虑STI,但认为您将添加偏离共享结构的模型或模型字段,则可能需要重新考虑您的计划。 如果您认为您的结构可能保持不变,则STI 通常可以更快地进行查询。

  4. Data integrity. If data is not going to be contained (one application using your database), polymorphic association is probably a bad choice because your data will be compromised.

    数据的完整性。 如果不包含数据(使用数据库的一个应用程序),则多态关联可能是一个不好的选择,因为数据将受到损害。

最后的想法 (Final Thoughts)

Neither STI nor polymorphic associations are perfect. They both have pros and cons that often make one or the other more fit for associations with many models.

STI和多态关联都不是完美的。 它们都有优点和缺点,通常使一个或多个更适合与许多模型关联。

I wrote this article to teach myself these concepts just as much as to teach them to anyone else. If there is anything incorrect or any points you think should be mentioned, please help me and everyone else out by sharing in the comments!

我写这篇文章是为了向自己教授这些概念,就像向其他任何人教它们一样。 如果有任何不正确的地方或您认为应该提及的任何观点,请分享评论,以帮助我和其他所有人!

If you learned something or found this helpful, please click on the ? button to show your support!

如果您学到了什么或发现有帮助,请单击“ ?”。 按钮表示您的支持!

翻译自: https://www.freecodecamp.org/news/single-table-inheritance-vs-polymorphic-associations-in-rails-af3a07a204f2/

rails 两表关联查询

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值