使用 PostgreSQL 的模式和 Rails 创建多租户应用程序

发布于 2024-08-31 12:49:16 字数 1737 浏览 5 评论 0原文

我已经弄清楚了,

我正在学习如何在 Rails 中创建多租户应用程序,该应用程序根据用于查看应用程序的域或子域来提供来自不同模式的数据。

我已经回答了一些问题:

  1. 如何让 subdomain-fu 也能与域一起使用? 有人问了同样的问题,这将引导您此博客
  2. 什么数据库,它的结构如何?这是一个很棒的Guy Naor 的演讲,很好 有关 PostgreSQL 和架构的问题
  3. 我已经知道我的模式都将具有相同的结构。它们所持有的数据会有所不同。那么,如何为所有架构运行迁移?这是答案< /a>.

这三点涵盖了我需要了解的很多一般内容。然而,在接下来的步骤中,我似乎有很多实现方法。我希望有更好、更简单的方法。

最后,对于我的问题

当新用户注册时,我可以轻松创建架构。但是,加载其余架构已有的结构的最佳和最简单的方法是什么?以下是一些可能会给您带来更好想法的问题/场景。

  1. 我是否应该将其传递给一个 shell 脚本,将公共模式转储到临时模式中,并将其导入回我的主数据库(非常像 Guy Naor 在他的视频中所说的那样)?这是我从 freenode 上有用的 #postgres 获得的快速摘要/脚本。虽然这可能会起作用,但我必须在 Rails 之外做很多事情,这让我有点不舒服……这也让我想到了下一个问题。
  2. 有没有办法直接从 Ruby on Rails 执行此操作?就像创建一个 PostgreSQL 模式一样,然后只需将 Rails 数据库模式(schema.rb - 我知道,这很混乱)加载到该 PostgreSQL 模式中。
  3. 是否有一个 gem/插件已经具备这些功能? 像“create_pg_schema_and_load_rails_schema(the_new_schema_name)”这样的方法。如果没有,我可能会努力制作一个,但我怀疑它对所有移动部件的测试效果如何(特别是如果我最终使用 shell 脚本来创建和管理新的 PostgreSQL 模式)。

谢谢,我希望那不会太长!

Stuff I've already figured out

I'm learning how to create a multi-tenant application in Rails that serves data from different schemas based on what domain or subdomain is used to view the application.

I already have a few concerns answered:

  1. How can you get subdomain-fu to work with domains as well? Here's someone that asked the same question which leads you to this blog.
  2. What database, and how will it be structured? Here's an excellent talk by Guy Naor, and good question about PostgreSQL and schemas.
  3. I already know my schemas will all have the same structure. They will differ in the data they hold. So, how can you run migrations for all schemas? Here's an answer.

Those three points cover a lot of the general stuff I need to know. However, in the next steps I seem to have many ways of implementing things. I'm hoping that there's a better, easier way.

Finally, to my question

When a new user signs up, I can easily create the schema. However, what would be the best and easiest way to load the structure that the rest of the schemas already have? Here are some questions/scenarios that might give you a better idea.

  1. Should I pass it on to a shell script that dumps the public schema into a temporary one, and imports it back to my main database (pretty much like what Guy Naor says in his video)? Here's a quick summary/script I got from the helpful #postgres on freenode. While this will probably work, I'm gonna have to do a lot of stuff outside of Rails, which makes me a bit uncomfortable.. which also brings me to the next question.
  2. Is there a way to do this straight from Ruby on Rails? Like create a PostgreSQL schema, then just load the Rails database schema (schema.rb - I know, it's confusing) into that PostgreSQL schema.
  3. Is there a gem/plugin that has these things already? Methods like "create_pg_schema_and_load_rails_schema(the_new_schema_name)". If there's none, I'll probably work at making one, but I'm doubtful about how well tested it'll be with all the moving parts (especially if I end up using a shell script to create and manage new PostgreSQL schemas).

Thanks, and I hope that wasn't too long!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

很酷不放纵 2024-09-07 12:49:17

2011 年 12 月 5 日更新

感谢 Brad Robertson 和他的团队,有了公寓宝石。它非常有用,可以完成很多繁重的工作。

但是,如果您要修改模式,我强烈建议您了解它的实际工作原理。熟悉 Jerod Santo 的演练< /a> ,这样你就会知道 Apartment gem 或多或少在做什么。

更新于 2011 年 8 月 20 日 11:23 GMT+8

有人创建了 博客文章 并很好地完成了整个过程。

更新于 2010 年 5 月 11 日 11:26 GMT+8

自昨晚以来,我已经能够找到一种方法来创建新架构并将 schema.rb 加载到其中。不确定我所做的是否正确(到目前为止似乎工作正常),但至少更近了一步。如果有更好的方法请告诉我。

module SchemaUtils
  def self.add_schema_to_path(schema)
    conn = ActiveRecord::Base.connection
    conn.execute "SET search_path TO #{schema}, #{conn.schema_search_path}"
  end

  def self.reset_search_path
    conn = ActiveRecord::Base.connection
    conn.execute "SET search_path TO #{conn.schema_search_path}"
  end

  def self.create_and_migrate_schema(schema_name)
    conn = ActiveRecord::Base.connection

    schemas = conn.select_values("select * from pg_namespace where nspname != 'information_schema' AND nspname NOT LIKE 'pg%'")

    if schemas.include?(schema_name)
      tables = conn.tables
      Rails.logger.info "#{schema_name} exists already with these tables #{tables.inspect}"
    else
      Rails.logger.info "About to create #{schema_name}"
      conn.execute "create schema #{schema_name}"
    end

    # Save the old search path so we can set it back at the end of this method
    old_search_path = conn.schema_search_path

    # Tried to set the search path like in the methods above (from Guy Naor)
    # [METHOD 1]: conn.execute "SET search_path TO #{schema_name}"
    # But the connection itself seems to remember the old search path.
    # When Rails executes a schema it first asks if the table it will load in already exists and if :force => true. 
    # If both true, it will drop the table and then load it. 
    # The problem is that in the METHOD 1 way of setting things, ActiveRecord::Base.connection.schema_search_path still returns $user,public.
    # That means that when Rails tries to load the schema, and asks if the tables exist, it searches for these tables in the public schema.
    # See line 655 in Rails 2.3.5 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
    # That's why I kept running into this error of the table existing when it didn't (in the newly created schema).
    # If used this way [METHOD 2], it works. ActiveRecord::Base.connection.schema_search_path returns the string we pass it.
    conn.schema_search_path = schema_name

    # Directly from databases.rake. 
    # In Rails 2.3.5 databases.rake can be found in railties/lib/tasks/databases.rake
    file = "#{Rails.root}/db/schema.rb"
    if File.exists?(file)
      Rails.logger.info "About to load the schema #{file}"
      load(file)
    else
      abort %{#{file} doesn't exist yet. It's possible that you just ran a migration!}
    end

    Rails.logger.info "About to set search path back to #{old_search_path}."
    conn.schema_search_path = old_search_path
  end
end

Update Dec 5, 2011

Thanks to Brad Robertson and his team, there's the Apartment gem. It's very useful and does a lot of the heavy lifting.

However, if you'll be tinkering with schemas, I strongly suggest knowing how it actually works. Familiarize yourself with Jerod Santo's walkthrough , so you'll know what the Apartment gem is more or less doing.

Update Aug 20, 2011 11:23 GMT+8

Someone created a blog post and walks though this whole process pretty well.

Update May 11, 2010 11:26 GMT+8

Since last night I've been able to get a method to work that creates a new schema and loads schema.rb into it. Not sure if what I'm doing is correct (seems to work fine, so far) but it's a step closer at least. If there's a better way please let me know.

module SchemaUtils
  def self.add_schema_to_path(schema)
    conn = ActiveRecord::Base.connection
    conn.execute "SET search_path TO #{schema}, #{conn.schema_search_path}"
  end

  def self.reset_search_path
    conn = ActiveRecord::Base.connection
    conn.execute "SET search_path TO #{conn.schema_search_path}"
  end

  def self.create_and_migrate_schema(schema_name)
    conn = ActiveRecord::Base.connection

    schemas = conn.select_values("select * from pg_namespace where nspname != 'information_schema' AND nspname NOT LIKE 'pg%'")

    if schemas.include?(schema_name)
      tables = conn.tables
      Rails.logger.info "#{schema_name} exists already with these tables #{tables.inspect}"
    else
      Rails.logger.info "About to create #{schema_name}"
      conn.execute "create schema #{schema_name}"
    end

    # Save the old search path so we can set it back at the end of this method
    old_search_path = conn.schema_search_path

    # Tried to set the search path like in the methods above (from Guy Naor)
    # [METHOD 1]: conn.execute "SET search_path TO #{schema_name}"
    # But the connection itself seems to remember the old search path.
    # When Rails executes a schema it first asks if the table it will load in already exists and if :force => true. 
    # If both true, it will drop the table and then load it. 
    # The problem is that in the METHOD 1 way of setting things, ActiveRecord::Base.connection.schema_search_path still returns $user,public.
    # That means that when Rails tries to load the schema, and asks if the tables exist, it searches for these tables in the public schema.
    # See line 655 in Rails 2.3.5 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
    # That's why I kept running into this error of the table existing when it didn't (in the newly created schema).
    # If used this way [METHOD 2], it works. ActiveRecord::Base.connection.schema_search_path returns the string we pass it.
    conn.schema_search_path = schema_name

    # Directly from databases.rake. 
    # In Rails 2.3.5 databases.rake can be found in railties/lib/tasks/databases.rake
    file = "#{Rails.root}/db/schema.rb"
    if File.exists?(file)
      Rails.logger.info "About to load the schema #{file}"
      load(file)
    else
      abort %{#{file} doesn't exist yet. It's possible that you just ran a migration!}
    end

    Rails.logger.info "About to set search path back to #{old_search_path}."
    conn.schema_search_path = old_search_path
  end
end
女皇必胜 2024-09-07 12:49:17

将第 38 行更改为:

conn.schema_search_path = "#{schema_name}, #{old_search_path}"

我假设 postgres 在加载 schema.rb 时尝试查找现有表名,并且由于您已将 search_path 设置为仅包含新架构,因此它失败。当然,这是假设您的数据库中仍然有公共模式。

希望有帮助。

Change line 38 to:

conn.schema_search_path = "#{schema_name}, #{old_search_path}"

I presume that postgres is trying to lookup existing table names when loading schema.rb and since you've set the search_path to only contain the new schema, it fails. This of course, is presuming you still have the public schema in your database.

Hope that helps.

感悟人生的甜 2024-09-07 12:49:17

是否有一个 gem/插件已经具备这些功能?

pg_power 提供了在迁移中创建/删除 PostgreSQL 模式的功能,如下所示:

def change
  # Create schema
  create_schema 'demography'

  # Create new table in specific schema
  create_table "countries", :schema => "demography" do |t|
    # columns goes here
  end

  # Drop schema
  drop_schema 'politics'
end

它还正确处理将模式转储到 schema.rb 文件中。

Is there a gem/plugin that has these things already?

pg_power provides this functionality to create/drop PostgreSQL schemas in migration, like this:

def change
  # Create schema
  create_schema 'demography'

  # Create new table in specific schema
  create_table "countries", :schema => "demography" do |t|
    # columns goes here
  end

  # Drop schema
  drop_schema 'politics'
end

Also it takes care about correctly dumping schemas into schema.rb file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文