【问题标题】:Dynamic Models in Phoenix FrameworkPhoenix 框架中的动态模型
【发布时间】:2018-04-01 20:54:45
【问题描述】:

有没有办法在 Phoenix 中动态创建和使用模型?我有一个存储有关客户表的元数据的应用程序:他们设置了一些字段(列名和类型),然后将 CSV 文件发送给我以进行解析和存储。从存储的元数据中,我想生成一个模型,以便我可以使用 Ecto 管理客户端表并对其执行查询。

我来自 Django 背景,我可以使用内置的 ORM 和 type() 函数即时构建模型,然后使用它们而无需在应用程序中生成迁移或其他模型代码。

在 Python 中,我会(大致)这样做:

class MetaModel(models.Model):
    table_name = models.CharField(...)
    model_name = models.CharField(...)
    field_defs = JSONField(...)

    def resolve_fields(self):
        # takes values from `field_defs` and converts them into
        # django field instances

     def get_model(self):
         class Meta:
             app_label = 'dynamic'
             db_table = self.table_name
         fields = self.resolve_fields()
         attrs = {'__module__': 'dynamic', 'Meta': Meta}
         attrs.update(fields)
         model = type(self.model_name, (models.Model,), attrs)
         return model

     def create_table(self):
         with connection.schema_editor() as se:
             model = self.get_model()
             se.create_model(model)

这样,我就可以在数据库中创建表,然后利用 ORM 处理客户提供的数据。

我知道我可以使用原始 SQL 并只使用 Ecto 来运行命令和查询,但我想让它更加编码并依赖 Ecto 的内部结构,而不是编写和维护一堆 SQL 模板.

任何建议(即使是“不,你不能那样做”)都非常有帮助。谢谢!

【问题讨论】:

    标签: elixir phoenix-framework ecto


    【解决方案1】:

    是的,Module.create/3 可以。有一些注意事项:您需要为每个模块选择一个唯一的名称,并且模块的代码将保存在内存中,直到 VM 重新启动,因此您可能希望限制调用此函数的次数。

    这是一个您可以构建的基本实现。它允许您传递模块名称、表名称以及字段名称和类型对的列表。

    defmodule A do
      def go(module, table, fields) do
        Module.create(module, quote do
          use MyApp.Web, :model
          schema unquote(table) do
            unquote(for {name, type} <- fields do
              quote do
                field unquote(name), unquote(type)
              end
            end)
          end
        end, Macro.Env.location(__ENV__))
      end
    end
    
    A.go MyPost, "posts", [
      {:title, :string},
      {:content, :string},
    ]
    
    # MyPost is now just like any other Schema module in Phoenix.
    
    IO.inspect MyApp.Repo.all(MyPost)
    

    输出:

    [debug] QUERY OK source="posts" db=2.8ms queue=0.1ms
    SELECT p0."id", p0."title", p0."content" FROM "posts" AS p0 []
    [%MyPost{__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
      content: "Hello from Joe", id: 1, title: "Hello"},
     %MyPost{__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
      content: "Hello from Mike", id: 2, title: "Hello"},
     %MyPost{__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
      content: "Hello from Robert", id: 3, title: "Hello"}]
    

    【讨论】:

    • 这真的很酷,你将如何重新定义给定的模块?
    • 知道了,你可以覆盖具有相同名称/原子的模块
    • 你好@Dogbert 我的解决方案出现错误stackoverflow.com/questions/50479750/… 知道为什么吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-18
    • 2015-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多