umbra

umbra

So once again Im stuck, I think that I have some problem with “database frameworks”, Im struggling really bad with Ecto, mainly with associations. Maybe the main issue here is my database design, who may be bad, even if this is the case, I need to be able to deal with it anyways.

My business logic is the following: I have a User(provided by Pow) which have many Expenses and Tags, and the Expense has many Tag. (Every User have his own Tags).

Im tryig to create a new Expense, I need to assoc a list of Tags and a User to it.
Params: %{“notes” => “I am a note!”, “tag_id” => [“2”, “3”], “value” => “12345”}
I have no clue on how to do it.

Pastebin links to the code:
Migrations
Schemas
Context and Assocs
Controller and Form

Thank you

Showing Posts 1 to 7

stefanchrobot

stefanchrobot

I think Ecto should allow you to insert everything in one go and do some work for you. But if things get to complex or magical, remember that Repo.transaction is your friend and you can always do things more manually. Something like:

Repo.transaction(fn ->
  with {:ok, schema1} <- Schema1.create_changeset(params1) |> Repo.insert(),
       {:ok, schema2} <- Schema2.create_changeset(schema1, params2) |> Repo.insert(),
       # ...
    # Transaction returns {:ok, schemaN}
    schemaN
  else
    # Transaction returns {:error, changeset}
    {:error, changeset} -> Repo.rollback(changeset)
  end
end)

or if you want to make things composable, you can use Multi:

Mulit.new()
|> Multi.insert(:schema1, Schema1.create_changeset(params1))
|> Multi.run(:schema2, fn repo, %{schema1: schema1} ->
  schema1
  |> Schema2.create_changeset(params2)
  |> repo.insert()
end)
# ...
|> Repo.transaction()
|> case do
  {:ok, %{schema1: schema1, ...}} -> {:ok, schemaN}
  {:error, _failed_operation, failed_value, _changes_so_far} -> {:error, failed_value}
end

At this level things translate into straightforward SQL inserts/updates. If you go this way, I’d use pattern matching to destructure the params in the controller:

def create(conn, %{"schema1" => %{"attr1" => ..., "schema2" => %{ ... } = params2} = params1}) do
  case Context.create_thing(params1, params2) do
    # ...
  end
end

The reason to do so would be to support params with both string keys (this is how data is coming in via Phoenix) and atom keys (more convenient to use in tests or IEx).

umbra

umbra OP

I cant see how the associations are being done

I need to create a new Record and associate it with one existing User and one or many Tags(tag_id), only then, insert.

Im wondering if using Ecto.Query isnt a better idea instead of using those more “magic” functions

umbra

umbra OP

Params look like this:
%{"expense" => %{"note" => "Im a note", "tag_id" => ["4", "5"], "value" => "717891"}

I dont know how to deal with the multiple values from tag_id

Souldn it be like:

`%{"expense" => %{
     "note" => "Im a note", 
     "tags" => %{
          %{"tag" => %{"id" => "4"},
          %{"tag" => %{"id" => "5"},
     }, 
     "value" => "717891"
}`

schema

schema "expenses" do
    field :value, :integer
    field :note, :string
    field :inserted_at, :naive_datetime
    has_many :tags, Finapp.Records.Tag
    belongs_to :user, Finapp.Users.User
 end

form

<div class="field">
        <%= label f, :tag_id, "Categorias" %>
        <%= multiple_select f, :tag_id, Enum.map(@tags, &{&1.name, &1.id}), class: "ui fluid search dropdown tag-menu" %>
        <%= error_tag f, :tag_id %>
</div>
cmo

cmo

Doesn’t look like your database structure allows it. You might like to draw out the tables on paper and enter some data in there to see how it looks.

You might want to read ecto docs on many to many associations.

umbra

umbra OP

So, the relation between Expense and Tag should be many to many? I thought about that, but, I would end up with an “expense_tag” table with expense_id, tag_id and a user_id, since every user have his own tags.

The structure would be like:
a user has many expenses and many tags,
an expense has many tags

cmo

cmo

I don’t think there would be a user_id in that table as you have user_id on the tags table.

User has user_id, Tags are associated with a user_id (not associated with an expense so no expense_id column), Expenses have multiple tags, so you need an expense_tags table which is just expense_id and tag_id, no?

umbra

umbra OP

Well, I did it, still cant figure out how to make this work with multiple_select. Its really frustrating, it is so trivial with plain sql, Im struggling too much with Ecto, I think Im done…

Thank you for your attention

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
psy-q
I’m trying to set up Emacs with elixir-ls via lsp-mode and credo via Flycheck. This should mostly be preconfigured as Flycheck picks up c...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews