wolfiton

wolfiton

All assoc are correct but i get no assoc _id for categories

Hi everyone,

So the funny things is that I get no associations even though I specified the relationship in my migrations and my schema.

My associations

In create_articles migration i have add :category_id, references(:categories, on_delete: :delete_all)

In the category schema has_many(:articles, Article)

In articles schema I have belongs_to(:category, Category)

The relationship that I want to create is that an article can have only one category.But categories can belong to multiple articles.

Error trace

[info] GET /articles
[debug] Processing with BlogApiWeb.ArticleController.index/2
  Parameters: %{}
  Pipelines: [:browser]
[debug] QUERY ERROR source="articles" db=0.0ms queue=1.5ms
SELECT a0."id", a0."content", a0."published", a0."title", a0."category_id", a0."inserted_at", a0."updated_at" FROM "articles" AS a0 []
[info] Sent 500 in 140ms
[error] #PID<0.520.0> running BlogApiWeb.Endpoint (connection #PID<0.519.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: GET /articles
** (exit) an exception was raised:
    ** (Postgrex.Error) ERROR 42703 (undefined_column) column a0.category_id does not exist

    query: SELECT a0."id", a0."content", a0."published", a0."title", a0."category_id", a0."inserted_at", a0."updated_at" FROM "articles" AS a0
        (ecto_sql) lib/ecto/adapters/sql.ex:629: Ecto.Adapters.SQL.raise_sql_call_error/1
        (ecto_sql) lib/ecto/adapters/sql.ex:562: Ecto.Adapters.SQL.execute/5
        (ecto) lib/ecto/repo/queryable.ex:177: Ecto.Repo.Queryable.execute/4
        (ecto) lib/ecto/repo/queryable.ex:17: Ecto.Repo.Queryable.all/3
        (blog_api) lib/blog_api/blog.ex:23: BlogApi.Blog.list_articles/0
        (blog_api) lib/blog_api_web/controllers/article_controller.ex:9: BlogApiWeb.ArticleController.index/2
        (blog_api) lib/blog_api_web/controllers/article_controller.ex:1: BlogApiWeb.ArticleController.action/2
        (blog_api) lib/blog_api_web/controllers/article_controller.ex:1: BlogApiWeb.ArticleController.phoenix_controller_pipeline/2
        (phoenix) lib/phoenix/router.ex:288: Phoenix.Router.__call__/2
        (blog_api) lib/blog_api_web/endpoint.ex:1: BlogApiWeb.Endpoint.plug_builder_call/2
        (blog_api) lib/plug/debugger.ex:122: BlogApiWeb.Endpoint."call (overridable 3)"/2
        (blog_api) lib/blog_api_web/endpoint.ex:1: BlogApiWeb.Endpoint.call/2
        (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:42: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy) /home/dan/Codes/blog_api/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) /home/dan/Codes/blog_api/deps/cowboy/src/cowboy_stream_h.erl:320: :cowboy_stream_h.execute/3
        (cowboy) /home/dan/Codes/blog_api/deps/cowboy/src/cowboy_stream_h.erl:302: :cowboy_stream_h.request_process/3
        (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

Thanks in advance

Marked As Solved

Kurisu

Kurisu

def create_article(attrs \\ %{}) do
    %Article{}
    |> Article.changeset(attrs)
    |> Ecto.Changeset.cast_assoc(:category, with: &Category.changeset/2)
    |> Repo.insert()
  end

I think you want to that when you want users to create an article and a category at the same time or update an existing category at the same time. And you need to preload an empty struct or an existing record for the association following you to create a new or update an existing one.

<div class="field">
  <%= label f, :category, class: "label" %>
  <div class="control">
    <%= select f, :category_id, @categories %>
  </div>
  <p class="help is-danger"><%= error_tag f, :category %></p>
</div>

But given this part of your form I can assume that the category to associate to the new article already exists and just has to be selected. In this case you just have to update your article schema with an additional changeste like below:

def create_changeset(article, attrs) do
    article
    |> cast(attrs, [:title, :content, :published, :category_id])
    |> validate_required([:title, :content, :published, :category_id])
    |> foreign_key_constraint(:category_id, message: "Category not found!")
    |> unique_constraint(:title)
  end

So I just add one more field in the cast and validate_required functions: category_id which will be submited by the form. The foreign_key_constraint validator will throw an error if by some mean the user attempt to submit an invalid category id. So you use this changeset only to create new article unless you want to allow user to change also the category when updating an article. If you don’t want them to be able to uppdate the category of existing articles you just use your initial changeset for article update.

Also Liked

wolfiton

wolfiton

My migration for articles

defmodule BlogApi.Repo.Migrations.CreateArticles do
  use Ecto.Migration

  def change do
    create table(:articles) do
      add :title, :string
      add :content, :string
      add :published, :boolean, default: false, null: false
      add :category_id, references(:categories, on_delete: :delete_all)
      timestamps()
    end
  end
end

My article schema

defmodule BlogApi.Blog.Article do
  use Ecto.Schema
  import Ecto.Changeset
  alias BlogApi.Categories.Category

  schema "articles" do
    field :content, :string
    field :published, :boolean, default: false
    field :title, :string
    belongs_to(:category, Category)
    timestamps()
  end

  @doc false
  def changeset(article, attrs) do
    article
    |> cast(attrs, [:title, :content, :published])
    |> validate_required([:title, :content, :published])
    |> unique_constraint(:title)
  end
end

My schema for categories

defmodule BlogApi.Categories.Category do
  use Ecto.Schema
  import Ecto.Changeset
  alias BlogApi.Blog.Article

  schema "categories" do
    field :name, :string
    field :slug, :string

    has_many(:articles, Article)
    timestamps()
  end

  @doc false
  def changeset(category, attrs) do
    category
    |> cast(attrs, [:name, :slug])
    |> validate_required([:name, :slug])
    |> unique_constraint(:name)
  end
end
Kurisu

Kurisu

This line in article’s changeset specifies which fields will be casted for insertion. So I assume you set the category_id later in some context function with Ecto.Changeset.put_change before insertion. If not Postgresql will throws an error unless the category_id column can be null.

Also the problem may come from the submited form? You need to ensure user send a category id. Please show the part of the form where users fill or select a category.

Edit:
I think there are two ways to insert resource such as article that belongs to a parent (category in this case).

  • the category is already known before rendering the new article form. In that case generally the category id is present in the url. See nested resources. In this case you don’t cast the id in your changeset but you still need to set it before insertion.
  • the category id is provided directly from the submitted form. In this case you just need to cast as you do for the other fields. Ecto.Changeset.foreign_key_constraint can be used as validator to show appropriate error message when the id sent is not valid. Also in the form if you decide to use a select, you need to format the collection of categories to be a valid enum type. For example a list like [{value_1, id_1}, {value_2, id_2}, ... ]. Values are the options that users will read in the select, and the ids are what your changeste will cast.

I hope this will help you in fixing your issue.

Where Next?

Popular in Questions Top

greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41989 114
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New

We're in Beta

About us Mission Statement