官术网_书友最值得收藏!

  • Phoenix Web Development
  • Brandon Richey
  • 588字
  • 2021-08-27 18:28:29

Understanding the gotchas of associations

Cool! We now have our Poll, representing a row in the database with an ID of 1. Let's try to access the options on that poll:

iex(12)> poll.options
#Ecto.Association.NotLoaded<association :options is not loaded>

This seems strange. We queried for the Poll and got that back, so why didn't it load our Options association with it? Well, this is actually intentional behavior for Ecto; it's designed to not lazy-load data from the database. Anyone who has ever worked with a lazy loading relational model can tell you just what sorts of problems it tends to introduce over time concerning the performance and maintenance of an application, so to avoid that problem Ecto just straight up doesn't lazy load any associations. Instead, you'll need to tell Ecto that you want to include those associations directly, either via a join statement or via Repo.preload. So let's try our statement again, but this time with a Repo.preload instead:

It is a common scenario to see your tests fail or see Controllers throw error messages in development mode. Any time you get an error message about a changeset not matching the expected value or something going wrong in the display of a changeset, make sure you're not missing a preload statement!
iex(13)> poll = Repo.get!(Poll, 1) |> Repo.preload(:options)
[debug] QUERY OK source="polls" db=4.3ms
SELECT p0."id", p0."title", p0."inserted_at", p0."updated_at" FROM "polls" AS p0 WHERE (p0."id" = $1) [1]
[debug] QUERY OK source="options" db=3.7ms
SELECT o0."id", o0."title", o0."votes", o0."poll_id", o0."inserted_at", o0."updated_at", o0."poll_id" FROM "options" AS o0 WHERE (o0."poll_id" = $1) ORDER BY o0."poll_id" [1]
%Vocial.Votes.Poll{__meta__: #Ecto.Schema.Metadata<:loaded, "polls">, id: 1,
inserted_at: ~N[2017-10-05 20:18:08.931657],
options: [%Vocial.Votes.Option{__meta__: #Ecto.Schema.Metadata<:loaded, "options">,
id: 1, inserted_at: ~N[2017-10-05 21:14:32.058102],
poll: #Ecto.Association.NotLoaded<association :poll is not loaded>,
poll_id: 1, title: "Yes", updated_at: ~N[2017-10-05 21:14:32.059711],
votes: 0},
%Vocial.Votes.Option{__meta__: #Ecto.Schema.Metadata<:loaded, "options">,
id: 2, inserted_at: ~N[2017-10-05 21:15:03.797493],
poll: #Ecto.Association.NotLoaded<association :poll is not loaded>,
poll_id: 1, title: "No", updated_at: ~N[2017-10-05 21:15:03.797517],
votes: 0}], title: "Sample Poll", updated_at: ~N[2017-10-05 20:18:08.933798]}
iex(14)> poll.options
[%Vocial.Votes.Option{__meta__: #Ecto.Schema.Metadata<:loaded, "options">,
id: 1, inserted_at: ~N[2017-10-05 21:14:32.058102],
poll: #Ecto.Association.NotLoaded<association :poll is not loaded>,
poll_id: 1, title: "Yes", updated_at: ~N[2017-10-05 21:14:32.059711],
votes: 0},
%Vocial.Votes.Option{__meta__: #Ecto.Schema.Metadata<:loaded, "options">,
id: 2, inserted_at: ~N[2017-10-05 21:15:03.797493],
poll: #Ecto.Association.NotLoaded<association :poll is not loaded>,
poll_id: 1, title: "No", updated_at: ~N[2017-10-05 21:15:03.797517],
votes: 0}]

Huzzah! We're now getting our poll object out of the database and including the options as part of our preload statement! We have our way of getting the data in and out, we have our way of accessing the data via Elixir data structures, and we understand how to tell Ecto to relate our two separate schemas to each other! Now, imagine that you're writing code in your application and every single time you want to do something with Polls and Options you have to alias both schemas, include appropriate references and changesets for both, link everything together in the right way, include code to handle preloads and joins…

…and then imagine you have to do that every single time. Yikes! That's a ton of extra boilerplate code that we do not want to have to deal with! In addition, that code becomes incredibly brittle, because what if you change those changesets? New columns could get added pretty frequently and, if you have to search and find for the nine separate places you referenced those schemas could get incredibly tricky far too quickly! What's worse, you're even more likely to miss a spot where you needed to go through and change something, so you'd also have a broken application in a way that would be painstakingly difficult to diagnose and repair! We want to avoid that situation, so instead, let's use another recent Ecto concept to tackle this problem: Contexts.

主站蜘蛛池模板: 茂名市| 米泉市| 周宁县| 潮安县| 大安市| 阿城市| 新巴尔虎右旗| 绥宁县| 称多县| 广南县| 延寿县| 湘乡市| 新晃| 丹江口市| 东乌珠穆沁旗| 澄迈县| 新丰县| 二连浩特市| 夏河县| 乌鲁木齐县| 天峨县| 鲁甸县| 元江| 双城市| 莱西市| 湖南省| 孝感市| 龙陵县| 修文县| 聂拉木县| 阿拉尔市| 南宫市| 陵川县| 太仓市| 临潭县| 玛曲县| 六枝特区| 遵义县| 含山县| 泰兴市| 同江市|