Phoenix框架中的动态模型 [英] Dynamic Models in Phoenix Framework

查看:68
本文介绍了Phoenix框架中的动态模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Is there any way to dynamically create and use models in Phoenix? I have an application that stores metadata about clients' tables: they set a handful of fields (column names and types) and then send me CSV files to parse and store. From the stored metadata, I'd like to generate a model so that I can use Ecto to manage the client table and perform queries against it.

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

I'm coming from a Django background where I can use the built in ORM and type() function to build models on the fly and then use them without having to generate migrations or other model code in the application.

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

In Python, I would (roughly) do:

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处理客户端提供的数据.

With that, I'm able to create the table in the database and then leverage the ORM to work with client supplied data.

我知道我可以使用原始SQL来做到这一点,只需使用Ecto来运行命令和查询,但是我想使其更加统一,并依靠Ecto的内部结构,而不是编写和维护大量的SQL模板

I know I can do it with raw SQL and just use Ecto to run the commands and queries, but I'd like to make it more codified and rely on the internals of Ecto rather than writing and maintaining a bunch of SQL templates.

任何建议(即使是不,你也不能那样做")非常有帮助.谢谢!

Any advice (even a "nope, you can't do that") is super helpful. Thanks!

推荐答案

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

Yes, it's possible with Module.create/3. There are some caveats: you need to choose a unique name for each module and the module's code will live in memory until the VM is restarted so you might want to restrict the number of times you call this function.

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

Here's a basic implementation you can build off of. It allows you to pass a module name, a table name, and a list of field name and type pairs.

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"}]

这篇关于Phoenix框架中的动态模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆