Why does ecto migration have text but schema doesn't as a primitive type?

Hi everyone,

Why would an Ecto migration have a text type but the schema doesn’t?

When I try to seed my DB, I get an error

** (Ecto.ChangeError) value `%WolfBlog.Blog.Post{__meta__: #Ecto.Schema.Metadata<:built, 
"posts">, body: "Absinthe can really make working with Phoenix and Graphql much easier.
The big advantage is that you can also test the code.", id: nil, inserted_at: nil, title: nil, 
updated_at: nil}` for `WolfBlog.Blog.Post.body` in `insert` does not match type :string

My post schema

defmodule WolfBlog.Blog.Post do
  use Ecto.Schema
  import Ecto.Changeset
  alias WolfBlog.Blog.Post

  schema "posts" do
    field :body, :string
    field :title, :string

    timestamps()
  end

  @doc false
  def changeset(%Post{} = post, attrs) do
    post
    |> cast(attrs, [:title, :body])
    |> validate_required([:title, :body])
    |> unique_constraint(:title)
  end
end

My migration

defmodule WolfBlog.Repo.Migrations.CreatePosts do
  use Ecto.Migration

  def change do
    create table(:posts) do
      add :title, :string
      add :body, :string

      timestamps()
    end

    create unique_index(:posts, [:title])
  end
end

My alter table migration to add text primitive to body

defmodule WolfBlog.Repo.Migrations.BodyStringToBodyText do
  use Ecto.Migration

  def change do
    alter table(:posts) do # alter will modify the posts table
      modify :body, :text # modifies the body column from string to text
    end
  end
end

How can I make the body field from the schema have a text type?

Thanks in advance

1 Like

you don’t need, the schema types are supposed to map to “elixir types”, just keep the field on the schema as :string that it will work.

1 Like

I have it as string on the body and it doesn’t work.

It gives this error.

WolfBlog.Blog.Post.body` in `insert` does not match type :string

so, what are you trying to do when inserting it? and what is the database you’re using? if possible post what you’re doing in the seed script.

Yes the full code is part of my series on absinthe and is open source
Seeder

defmodule WolfBlog.Seeder do
  def power_up() do

    alias WolfBlog.Blog.Post
    alias WolfBlog.Repo

    absinthe_title = %Post{title: "Absinthe is great"}

    absinthe_body = %Post{body: "Absinthe can really make working with Phoenix and Graphql much easier.The big advantage is that you can also test the code."}

    _article = %Post{title: absinthe_title , body: absinthe_body} |> Repo.insert() #Bring the values from title and body and create
                                                                  # an article that get's added to the DB

    :ok
  end
end

This is not a string…

2 Likes

Can you explain why you don’t consider it a string?

Thanks @NobbZ

Because it is a struct.

What you are trying is to insert the following (simplified):

%Post{title: %Post{}, body: %Post{}}

Neither :title nor :body are strings.

4 Likes

Elixir is immutable… so when you do:

absinthe_title = %Post{title: "Absinthe is great"}
absinthe_body = %Post{body: "Absinthe can really make working with Phoenix and Graphql much easier.The big advantage is that you can also test the code."}

you’re creating two different %Post{}, one with only the body and the other with only the title. try doing:

%Post{}
|> Post.changeset(%{title: "title", body: "body"})
|> Repo.insert()
3 Likes

You are correct on this

The weird thing is that I can use this code, if I have the body as string also on the migration.

The migration tells the database how to store data.

The schema type describes how to convert elixir data types to something the database understands.

They are only loosely related in the sense, that the column type A is a requirement for a given schema type B to be able to convert elixir types.

The schema type :string does know how to convert elixir strings (aka binaries) into a column type of text or varchar or other “text like” column types.

1 Like

I got it

My mistake was that I am too tired today

defmodule WolfBlog.Seeder do
  def power_up() do

    alias WolfBlog.Blog.Post
    alias WolfBlog.Repo

    absinthe_title =  "Absinthe is great"

    absinthe_body =  "Absinthe can really make working with Phoenix and Graphql much easier.The big advantage is that you can also test the code."

    _article = %Post{title: absinthe_title , body: absinthe_body} |> Repo.insert() #Bring the values from title and body and create
                                                                  # an article that get's added to the DB

    :ok
  end
end

You guys were right

This works perfectly

I was really doing structs instead of strings.

Thanks for the help @NobbZ @cevado

1 Like

Thanks i know that part, but somehow after I written a lot of English today in a very long article, I couldn’t see that mistake Thanks again for the right direction.

Can i quote you guys in the article for your help on finding my mistake in the seeder file?

@NobbZ @cevado

The draft for the article is here https://dev.to/wolfiton/absinthe-journey-with-wolfiton-or-how-to-bring-the-absinthe-tutorial-up-to-date-part-4-the-door-3kki-temp-slug-7728002?preview=c9897d35f9de7af29b4e4df573f01fb2feb20a9ee86d73c166a9bac5850c1671b0c178accca7b02f65f527c40e7453badb1be9ce59b9138e32e69b14

yep :+1:

Of course!

5 posts were split to a new topic: How to cite your names

The differentiation exists because databases have multiple string types for storing, including a limited string type (string in ecto) and longer type (text in ecto). Basically it gets back to the desires of DBAs striving for optimizing databases and only storing as much as you need. Most of it doesn’t matter until you get to scale or hire a proper DBA. But when speed matters it becomes important. :slight_smile:

https://dev.mysql.com/doc/refman/8.0/en/string-types.html