web-dev-qa-db-fra.com

Création d'une application multi-locataire à l'aide des schémas et des Rails de PostgreSQL

Stuff j'ai déjà compris

J'apprends à créer une application à locataires multiples dans Rails, qui sert les données de différents schémas en fonction du domaine ou du sous-domaine utilisé pour afficher l'application.

J'ai déjà quelques préoccupations répondues:

  1. Comment pouvez-vous obliger subdomain-fu à travailler avec des domaines également? Voici quelqu'un qui a posé la même question qui vous amène à ce blog .
  2. Quelle base de données et comment sera-t-elle structurée? Voici un excellent discours de Guy Naor , et une bonne question sur PostgreSQL et les schémas .
  3. Je sais déjà que mes schémas auront tous la même structure. Ils différeront dans les données qu'ils détiennent. Alors, comment pouvez-vous exécuter des migrations pour tous les schémas? Voici une réponse .

Ces trois points couvrent beaucoup de choses générales que je dois savoir. Cependant, dans les prochaines étapes, il me semble avoir de nombreuses façons de mettre en œuvre les choses. J'espère qu'il existe un moyen meilleur et plus facile.

Enfin, à ma question

Lorsqu'un nouvel utilisateur s'inscrit, je peux facilement créer le schéma. Cependant, quel serait le moyen le plus simple et le meilleur de charger la structure déjà présente dans le reste des schémas? Voici quelques questions/scénarios qui pourraient vous donner une meilleure idée.

  1. Dois-je le transmettre à un script Shell qui vide le schéma public dans un schéma temporaire et le réimporte dans ma base de données principale (à peu près comme ce que Guy Naor a écrit dans sa vidéo)? Voici un quick summary/script que j'ai reçu de l'utilitaire #postgres on freenode . Bien que cela fonctionne probablement, je vais devoir faire beaucoup de choses en dehors de Rails, ce qui me met un peu mal à l'aise ... ce qui m'amène également à la question suivante.
  2. Existe-t-il un moyen de faire cela directement depuis Ruby on Rails ? Comme pour créer un schéma PostgreSQL, chargez simplement le schéma de base de données Rails (schema.rb - je sais, c'est déroutant) dans ce schéma PostgreSQL.
  3. Y a-t-il un plugin/gem qui a déjà ces choses? Des méthodes telles que "create_pg_schema_and_load_Rails_schema (the_new_schema_name)". S'il n'y en a pas, je vais probablement en créer un, mais je doute de la qualité de son test avec toutes les pièces mobiles (surtout si je finis par utiliser un script Shell pour créer et gérer de nouveaux schémas PostgreSQL).

Merci et j'espère que ça n'a pas été trop long!

45
Ramon Tayag

Mise à jour du 5 décembre 2011

Merci à Brad Robertson et à son équipe, voici le Appartement gem . C'est très utile et soulage beaucoup de choses.

Cependant, si vous bricolez des schémas, je suggère fortement de savoir comment cela fonctionne réellement. Familiarisez-vous avec la procédure pas à pas de Jerod Santo , vous saurez ainsi ce que le joyau de l'appartement fait de plus ou moins.

Mise à jour du 20 août 2011 à 11h23 GMT + 8

Quelqu'un a créé un article blog et parcourt plutôt bien tout ce processus.

Mise à jour du 11 mai 2010 à 11h26 GMT + 8

Depuis la nuit dernière, j'ai pu utiliser une méthode qui crée un nouveau schéma et y charge schema.rb. Je ne sais pas si ce que je fais est correct (semble bien fonctionner jusqu'à présent), mais c'est un pas de plus au moins. S'il y a un meilleur moyen s'il vous plaît faites le moi savoir.


  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
12
Ramon Tayag

Changer la ligne 38 à:

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

Je présume que postgres tente de rechercher les noms de tables existants lors du chargement de schema.rb et que vous avez défini le chemin search_path de manière à ne contenir que le nouveau schéma, il échoue. Bien entendu, cela suppose que vous avez toujours le schéma public dans votre base de données.

J'espère que cela pourra aider.

3
Brendan Hay

Y a-t-il un gem/plugin qui a déjà ces choses?

pg_power fournit cette fonctionnalité pour créer/supprimer des schémas PostgreSQL dans la migration, comme ceci:

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

En outre, il prend soin de vider correctement les schémas dans le fichier schema.rb.

0
Sergey Potapov