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
能够将其用作唯一约束?
宏可用于这种情况:
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
来投射和验证常用字段。
谢谢。我是否将基本变更集放入'Vegan'模块中,并将它放在包含模块中的较小版本中? – BeniaminoBaggins
是的,我已经用一个简单的例子更新了答案。特别是,'Ecto.Changeset.cast'将接受一个模式结构或一个'Changeset'作为第一个参数,允许组合变更函数。 –
像['Ecto.Schema.embedded_schema/1'](https://hexdocs.pm/ecto/Ecto.Schema.html#embedded_schema/1)? – mudasobwa