I have a User and Role Schema.
User - has_many role
Role - belongs to user
Now I’m creating a user with simple data
def create_user(attrs) do
%User{}
|> User(attrs)
|> Repo.insert()
|> case do
{:error, _changeset} = changeset ->
changeset
{:ok, user} ->
user
end
end
but now I have to create a user on one condition. So Role has a number with only that number I can create this user. So when I’m creating a user my api should look like this
http://localhost:4000/api/v1/user/1234526/role
So I thought of using cast_assoc and use the role changeset in the user changeset and while creating the user I just fetch the role number and create the user.
I just want to know if this approach is right or we can do something else also?
Hmm, can’t you first create the Role and then put that number into the User attrs
?
how? Sorry didn’t understand
I thought cast_assoc will give me the changeset with the role’s number.
It is not usual to have this, it is usually the opposite. And it is the belongs_to which holds the link information.
Can You expand on this? I don’t understand too. Is that some constraint? If so, You should show the changeset.
This is just for example.
Association is correct. I just changed the context of it.
So I just want to use another changeset which is in this case is role in the user changeset. Does api help?
Not really… because it should look like.
/api/vx/users/:id/roles
Note the plural form. It is not specified which method it is (GET or POST)
This form indicates user has_many roles, not role has_many users.
I read this like
- list all roles for user with id (GET)
- or create a new role for user with id (POST)
okay I have another example
I have a order → has_many line_item
and line_item → belongs to order
here is the changeset for line_item
def line_item_changeset(order_item, attrs \\ %{}) do
order_item
|> cast(attrs, [
:cost_price,
:price,
:quantity,
:order_id
])
end
and here is the order one
def order_changeset(order, attrs \\ %{}) do
order
|> cast(attrs, [
:number,
:state,
:completed_at,
])
end
Now Order can have many line_item.
But while creating a line_item. I want to create a api like this.
Post API
http://localhost:4000/orders/1234526/line_item
I have changed the context. I hope this help
If You create a resource with mix phx.gen.json You might see it’s the plural form which is used.
If You want to use the singular form, You might have to map your routes manually.
If You use this, it means create a line_item for a given order, but the order should already exists.
And this example is the opposite of User/Role example, because it’s the order which has many line_items.
Yeah. But now how do I create this api. Can you give me some idea on that
I would start like this…
$ mix phx.new maxtonx --no-html --no-webpack --no-dashboard
$ cd maxtonx
$ mix phx.gen.json Shop Order orders
...
Add the resource to your :api scope in lib/maxtonx_web/router.ex:
resources "/orders", OrderController, except: [:new, :edit]
...
$ mix phx.gen.context Shop LineItem line_items order_id:references:orders
Then I would add the line_item_controller.ex and update the router…
scope "/api", MaxtonxWeb do
pipe_through :api
resources "/orders", OrderController, except: [:new, :edit] do
resources "/line_items", LineItemController, except: [:new, :edit]
end
end
and will get those routes.
$ mix phx.routes
Compiling 19 files (.ex)
Generated maxtonx app
order_path GET /api/orders MaxtonxWeb.OrderController :index
order_path GET /api/orders/:id MaxtonxWeb.OrderController :show
order_path POST /api/orders MaxtonxWeb.OrderController :create
order_path PATCH /api/orders/:id MaxtonxWeb.OrderController :update
PUT /api/orders/:id MaxtonxWeb.OrderController :update
order_path DELETE /api/orders/:id MaxtonxWeb.OrderController :delete
order_line_item_path GET /api/orders/:order_id/line_items MaxtonxWeb.LineItemController :index
order_line_item_path GET /api/orders/:order_id/line_items/:id MaxtonxWeb.LineItemController :show
order_line_item_path POST /api/orders/:order_id/line_items MaxtonxWeb.LineItemController :create
order_line_item_path PATCH /api/orders/:order_id/line_items/:id MaxtonxWeb.LineItemController :update
PUT /api/orders/:order_id/line_items/:id MaxtonxWeb.LineItemController :update
order_line_item_path DELETE /api/orders/:order_id/line_items/:id MaxtonxWeb.LineItemController :delete
websocket WS /live/websocket Phoenix.LiveView.Socket
longpoll GET /live/longpoll Phoenix.LiveView.Socket
longpoll POST /live/longpoll Phoenix.LiveView.Socket
websocket WS /socket/websocket MaxtonxWeb.UserSocket
Still a lot to go, but api looks fine.
Excellent replies by others. I’ll only add that if you want to add both a User
and a Role
at the same time (which doesn’t make much sense to me, roles should be in the DB before users) then you should use Ecto.Multi
to do so.