Phoenix Web Development
上QQ阅读APP看书,第一时间看更新

Creating our Option schema

We'll need to create an Option schema, just like we have the Poll schema, but we'll need to do some additional legwork to make sure our representation makes sense. We'll also need to go back and fix up one thing we omitted from our Poll schema! First, we want to name our module Vocial.Votes.Option instead of Vocial.Votes.Poll (for obvious reasons). Next, we'll need to alias both Vocial.Votes.Poll and Vocial.Votes.Option. This is because we need the sanity helper for referencing the struct of our Option schema, and also because we'll need to reference the Poll schema as well to define our relationship to that table. We'll also need to create a :votes column on the schema; this one should be an integer with a default value of 0. Let's take a look at the finished file with all of those bells and whistles:

defmodule Vocial.Votes.Option do
use Ecto.Schema
import Ecto.Changeset
alias Vocial.Votes.Option
alias Vocial.Votes.Poll

schema "options" do
field :title, :string
field :votes, :integer, default: 0

belongs_to :poll, Poll

timestamps()
end

def changeset(%Option{}=option, attrs) do
option
|> cast(attrs, [:title, :votes, :poll_id])
|> validate_required([:title, :votes, :poll_id])
end
end

We have our first example here of specifying options for the definitions of our fields via the votes field definition. Here, we're saying that the default value for any Option that gets created is a 0 in the votes column; this will make writing our insert statements a little easier and save us from wacky scenarios where an Option has nil votes or something like that. In addition, notice that we have a new statement here, the belongs_to statement. As mentioned earlier, we designed this as a has_many relationship between Polls and Options. Specifically, here we're saying that an Option belongs_to a Poll, so one thing we quickly need to do is return to our Poll schema and add a has_many statement to tell Ecto that there is another side to the association. We’ll add the following line to the top, near our other alias statement:

alias Vocial.Votes.Option

And we'll add the has_many statement inside of our schema block in the Poll schema:

has_many :options, Option

Let's go back to the IEx session that we tested our Poll schema in and try out our Options schema! First, we'll make sure we get a Poll back from our database by running Repo.all(Poll):

iex(4)> Repo.all(Poll)
[debug] QUERY OK source="polls" db=3.5ms decode=4.7ms
SELECT p0."id", p0."title", p0."inserted_at", p0."updated_at" FROM "polls" AS p0 []
[%Vocial.Votes.Poll{__meta__: #Ecto.Schema.Metadata<:loaded, "polls">, id: 1,
inserted_at: ~N[2017-10-05 20:18:08.931657], title: "Sample Poll",
updated_at: ~N[2017-10-05 20:18:08.933798]}]

We have a Poll with an ID of 1, so let's use that to create our options! We'll create two simple options, yes and no, so let's start by adding the alias for our Option schema:

iex(5)> alias Vocial.Votes.Option
Vocial.Votes.Option

And finally, let's insert changesets for our two new options using a poll_id of 1:

iex(6)> Option.changeset(%Option{}, %{title: "Yes", poll_id: 1}) |> Repo.insert()
[debug] QUERY OK db=0.2ms
begin []
[debug] QUERY OK db=5.6ms
INSERT INTO "options" ("poll_id","title","votes","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" [1, "Yes", 0, {{2017, 10, 5}, {21, 14, 32, 58102}}, {{2017, 10, 5}, {21, 14, 32, 59711}}]
[debug] QUERY OK db=19.4ms
commit []
{:ok,
%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}}
iex(7)> Option.changeset(%Option{}, %{title: "No", poll_id: 1}) |> Repo.insert()
[debug] QUERY OK db=0.3ms
begin []
[debug] QUERY OK db=7.1ms
INSERT INTO "options" ("poll_id","title","votes","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" [1, "No", 0, {{2017, 10, 5}, {21, 15, 3, 797493}}, {{2017, 10, 5}, {21, 15, 3, 797517}}]
[debug] QUERY OK db=6.2ms
commit []
{:ok,
%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}}

This time we took a slightly different route and piped the results of the changeset call directly into the Repo.insert() function via the pipeline operator! We need to verify that this added the options to our Poll object, so let's fetch our Poll out of the database:

iex(11)> poll = Repo.get!(Poll, 1)
[debug] QUERY OK source="polls" db=3.2ms
SELECT p0."id", p0."title", p0."inserted_at", p0."updated_at" FROM "polls" AS p0 WHERE (p0."id" = $1) [1]
%Vocial.Votes.Poll{__meta__: #Ecto.Schema.Metadata<:loaded, "polls">, id: 1,
inserted_at: ~N[2017-10-05 20:18:08.931657],
options: #Ecto.Association.NotLoaded<association :options is not loaded>,
title: "Sample Poll", updated_at: ~N[2017-10-05 20:18:08.933798]}