我有两个模型 - Organization
和User
组织有很多用户,也属于一个所有者(所有者也是User
模型)。
我有组织的注册表单,用户可以为组织输入name
,为name
输入password
,user
是user
的嵌套对象形式为organization
)。注册成功后,我需要Organization
与关联的owner
(User
模型),此user
应属于此组织。基本上,创建的用户应该是owner
,并且在注册后应该在has_many :users
关联列表中。
我已成功使用用户作为所有者关联创建组织,但我希望用户在创建后也能处于has_many :users
关联。在Rails中,我只想在这种情况下为用户添加after_create
回调和设置organization_id
,但由于ecto没有回调,我该怎么办呢?我已经阅读了Ecto.Multi
,但我不确定它是否正确(对我来说看起来有点矫枉过正)并且不确定它是如何用于这种情况的。
以防万一,这是我的模型代码和注册表单。
defmodule HappyClient.Organization do
use HappyClient.Web, :model
schema "organizations" do
field :name, :string
belongs_to :owner, HappyClient.User, foreign_key: :owner_id
has_many :users, HappyClient.User
timestamps()
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:name])
|> validate_required([:name])
end
def registration_changeset(struct, params) do
struct
|> changeset(params)
|> cast_assoc(:owner, required: true, with: &HappyClient.User.registration_changeset/2)
end
end
用户模型
defmodule HappyClient.User do
use HappyClient.Web, :model
schema "users" do
field :email, :string
field :name, :string
field :password_hash, :string
field :is_admin, :boolean, default: false
field :is_operator, :boolean, default: false
belongs_to :organization, HappyClient.Organization
field :password, :string, virtual: true
timestamps()
end
@required_fields ~w(email name)a
@optional_fields ~w(is_admin is_operator)a
def changeset(struct, params \\ %{}) do
struct
|> cast(params, @required_fields)
|> validate_required([:email, :name])
|> validate_format(:email, ~r/@/)
|> assoc_constraint(:organization)
end
def registration_changeset(struct, params) do
struct
|> changeset(Map.merge(params, %{"is_admin" => true}))
|> cast(params, [:password], [])
|> validate_required([:password])
|> validate_length(:password, min: 6, max: 100)
|> hash_password
end
defp hash_password(changeset) do
case changeset do
%Ecto.Changeset{valid?: true,
changes: %{password: password}} ->
put_change(changeset,
:password_hash,
Comeonin.Bcrypt.hashpwsalt(password))
_ ->
changeset
end
end
end
注册表格
<h1>Registration</h1>
<%= form_for @changeset, organization_path(@conn, :create), fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>There are some errors</p>
</div>
<% end %>
<div class="form-group">
<%= text_input f, :name, placeholder: "Organization Name", class: "form-control" %>
<%= error_tag f, :name %>
</div>
<div class="form-group">
<%= inputs_for f, :owner, fn of -> %>
<%= text_input of, :name, placeholder: "Name" %>
<%= error_tag of, :name %>
<%= text_input of, :email, placeholder: "Email" %>
<%= error_tag of, :email %>
<%= password_input of, :password, placeholder: "Password" %>
<%= error_tag of, :password %>
<% end %>
</div>
<%= submit "Create Organization", class: "btn btn-primary" %>
<% end %>
答案 0 :(得分:0)
似乎Ecto.Multi
并不难用于我的情况。
这是我所做的,而且效果很好
changeset = %Organization{} |> Organization.registration_changeset(params)
multi = Multi.new
|> Multi.insert(:organization, changeset)
|> Multi.run(:user, fn %{organization: organization} ->
organization
|> Repo.preload(:users)
|> Changeset.change
|> Changeset.put_assoc(:users, [organization.owner])
|> Repo.update
end)
case Repo.transaction(multi) do
{:ok, %{organization: organization}} ->
conn
|> put_flash(:info, "#{organization.name} created!")
|> redirect(to: organization_path(conn, :show))
{:error, :organization, changeset, %{}} ->
render(conn, "new.html", changeset: changeset)
end