Elixir和Ecto中的代码复制

问题描述:

我想用较小的模块组成一个模块。Elixir和Ecto中的代码复制

这是一个模块,我现在所拥有的:

defmodule Api.Product do 
    use Ecto.Schema 
    import Ecto.Changeset 
    import Api.Repo 
    import Ecto.Query 

    @derive {Poison.Encoder, only: [:name, :brand, :description, :image, :rating, :number_of_votes]} 
    schema "products" do 
    field :name, :string 
    field :brand, :string 
    field :description, :string 
    field :image, :string 
    field :rating, :integer 
    field :number_of_votes, :integer 
    field :not_vegan_count, :integer 
    end 

    def changeset(product, params \\ %{}) do 
    product 
    |> cast(params, [:name, :brand, :description, :image, :rating, :number_of_votes, :not_vegan_count]) 
    |> validate_required([:name, :description, :brand]) 
    |> unique_constraint(:brand, name: :unique_product) 
    end 

    def delete_all_from_products do 
    from(Api.Product) |> delete_all 
    end 

    def insert_product(conn, product) do 
    changeset = Api.Product.changeset(%Api.Product{}, product) 
    errors = changeset.errors 
    valid = changeset.valid? 
    case insert(changeset) do 
     {:ok, product} -> 
     {:success, product} 
     {:error, changeset} -> 
     {:error, changeset} 
    end 
    end 

    def get_product_by_name_and_brand(name, brand) do 
    Api.Product |> Ecto.Query.where(name: ^name) |> Ecto.Query.where(brand: ^brand) |> all 
    end 

    def get_products do 
    Api.Product |> all 
    end 
end 

但我希望能有比Product其他不同的东西,所有具有多数相同领域Product的除了brand。因此,最好创建一个除brand之外的所有字段的模块,然后包含这些字段的所有模块都将该模块作为字段?

这里是我的模块,所有模块将包括:

defmodule Api.VeganThing do 
    use Ecto.Schema 
    import Ecto.Changeset 
    import Api.Repo 
    import Ecto.Query 

    @derive {Poison.Encoder, only: [:name, :description, :image, :rating, :number_of_votes]} 
    schema "vegan_things" do 
    field :name, :string 
    field :description, :string 
    field :image, :string 
    field :rating, :integer 
    field :number_of_votes, :integer 
    field :not_vegan_count, :integer 
    end 
end 

不会有任何数据库表vegan_things。但是有几个不同的模块具有数据库表格将包含vegan_thing

这是避免代码复制重写Elixir中每个模块的每个字段的好方法吗?

这是我目前的变更:

defmodule Api.Repo.Migrations.CreateProducts do 
    use Ecto.Migration 

    def change do 
    create table(:products) do 
     add :name, :string 
     add :brand, :string 
     add :description, :string 
     add :image, :string 
     add :rating, :integer 
     add :number_of_votes, :integer 
     add :not_vegan_count, :integer 
    end 

    create unique_index(:products, [:name, :brand], name: :unique_product) 
    end 
end 

所以我的基础上的独特性这将是一个vegan_thing字段和字段,只在product。我可以做这样的事吗?

defmodule Api.Repo.Migrations.CreateProducts do 
    use Ecto.Migration 

    def change do 
    create table(:products) do 
     add :name, :string 
     add :vegan_thing, :vegan_thing 
    end 

    create unique_index(:products, [:vegan_thing.name, :brand], name: :unique_product) 
    end 
end 

还是我直接把name领域product?而不是vegan_thing能够将其用作唯一约束?

+0

像['Ecto.Schema.embedded_schema/1'](https://hexdocs.pm/ecto/Ecto.Schema.html#embedded_schema/1)? – mudasobwa

宏可用于这种情况:

defmodule Vegan do 
    defmacro vegan_schema name, fields do 
     quote do 
     schema unquote(name) do 
      unquote(fields) 
      field :name, :string 
      field :description, :string 
      field :image, :string 
      field :rating, :integer 
      field :number_of_votes, :integer 
      field :not_vegan_count, :integer 
     end 
     end 
    end 

    def changeset(struct_or_changeset, params) do 
     struct_or_changeset 
     |> Ecto.Changeset.cast(params, [:name, :description, :rating]) 
     |> Ecto.Changeset.validate_required([:name, :description]) 
    end 
    end 

    defmodule Product do 
    use Ecto.Schema 
    require Vegan 

    @derive {Poison.Encoder, only: [:name, :brand, :description, :image, :rating, :number_of_votes]} 
    Vegan.vegan_schema "products" do 
     field :brand, :string 
    end 

    def changeset(params) do 
     %Product{} 
     |> Vegan.changeset(params) 
     |> Ecto.Changeset.cast(params, [:brand]) 
     |> Ecto.Changeset.validate_required([:brand]) 
    end 
    end 

对于处理Ecto.Changeset等功能,则常规的模块和功能应该是罚款分解出任何重复的代码,如上面的其中Product.changeset/1呼叫的示例Vegan.changeset/2来投射和验证常用字段。

+0

谢谢。我是否将基本变更集放入'Vegan'模块中,并将它放在包含模块中的较小版本中? – BeniaminoBaggins

+1

是的,我已经用一个简单的例子更新了答案。特别是,'Ecto.Changeset.cast'将接受一个模式结构或一个'Changeset'作为第一个参数,允许组合变更函数。 –