2017-09-23 106 views
0

我有两个型号 - OrganizationUser 组织有许多用户,也属于一个所有者(老板是User模型太)。在父对象的多个关联保存一个模型一次

我报名形式组织,在那里用户可以为组织和namepassworduser进入nameuser是在形式上organization嵌套对象)。成功注册后,我需要有一个Organization及相关的ownerUser模型),并且此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

它似乎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 
相关问题