Создание таблиц и моделей на лету (динамически) в Ruby on Rails 3 - PullRequest
8 голосов
/ 09 апреля 2011

Потерпи меня на этом.

В приложении, над которым я работаю, пользователи могут загружать в систему файлы CSV с любыми заголовками и любыми столбцами в данных.CSV затем используется для генерации таблицы в базе данных и данных, записанных в нее, затем к ней можно получить доступ через систему для различного использования, поиска, сортировки обновлений и т. Д.

Старая (и теперь несуществующая система)) был на PHP и обрабатывал это нормально, хотя и довольно запутанно с большим количеством необработанного sql для создания таблиц, и рамки поддерживали magic-модели (если таблица существовала, то существовал и объект без определения класса в файле модели)

Новая версия написана на RoR3, и мне еще предстоит найти способ сделать это.Мне удалось разобраться в создании таблицы, вызвав инструменты миграции внутри модели (не очень Rails-у, я знаю, но должен ...), но я не могу найти способ связать новую таблицу после ее создания сзаписывать данные, строить отношения или что-то еще.

Я надеюсь либо:

a) у кого-то здесь есть лучший способ сделать это, чем создавать таблицы и модели нана лету (предупреждение здесь, эти файлы могут содержать 100'000 записей и различных полей, поэтому один параметр таблицы не работает так хорошо), т.е. лучший дизайн базы данных для этой проблемы.

или

b) может рассказать мне, как решить проблему с моделью.

Я посмотрел на драгоценный камень Dr Nic's Magic Model для RoR, но, похоже, он не работает в RoR3, если я не делаю что-тонеправильно

Извините за стену текста, с нетерпением ждем любых предложений

Заранее спасибо

Ответы [ 2 ]

9 голосов
/ 22 июня 2012

ОК, я думаю, что у меня есть решение, но если это хорошо, это совсем другое дело.

По сути, вы создаете таблицу на лету, выполняя SQL через Rail ActiveRecord.Затем используйте модель и измените ее имя (Model.table_name).

Примерно так:

    // SQL Create table
    sql = "CREATE TABLE my_table (id int(11) NOT NULL AUTO_INCREMENT, code varchar(3) NOT NULL)"
    ActiveRecord::Base.connection.execute(sql)

Затем с моделью вы можете изменить имя таблицы на лету, например:

MyModel.table_name = "my_table"
records = MyModel.all

Хорошо, одна из ваших проблем - логика и ассоциации моделей.Вы ограничены, но, возможно, вы можете обойти это.

Не совсем лучшие практики, я думаю, но если вам это нужно.Я думаю, что это может сработать!

5 голосов
/ 03 февраля 2017

Я реализовал приложение, которое используется для загрузки файла CSV и преобразования файла в модель активной записи.Вы можете проверить этот репозиторий.

Посетите https://github.com/Athul92/Salary_Sheet_Comparison/blob/master/app/models/makemodel.rb

Вот небольшая идея о том, как это было достигнуто:

   class Makemodel < ActiveRecord::Migration
     def self.import(file,project_name,file_name,start_row,end_row,unique,last_column)
       spreadsheet = open_spreadsheet(file)
       header = spreadsheet.row(start_row.to_i)
       i=0
       header.count.times do
         unless header[i].nil?
           header[i]= header[i].gsub(/[^0-9A-Za-z]/, '').downcase
         end
         i+=1
       end
       name = "#{project_name.downcase}"+"#{file_name.downcase}"
       create_table name.pluralize do |t|
         header.each do |head|
           t.string head
         end
       end
       model_file = File.join("app", "models", name.singularize+".rb")
       model_name = name.singularize.capitalize
       File.open(model_file, "w+") do |f|
         f << "class #{model_name} < ActiveRecord::Base\nend"
       end

       ((start_row.to_i+1)..end_row.to_i).each do |i|
         row = Hash[[header, spreadsheet.row(i)].transpose]
         #should find a logic to find the model class that is being created
         product = Object.const_get(name.capitalize).new
         product.attributes = row.to_hash
         product.save!
       end
     end

     def self.open_spreadsheet(file)
       case File.extname(file.original_filename)
         when ".csv" then Csv.new(file.path, nil, :ignore)
         when ".xls" then Roo::Excel.new(file.path)
         when ".xlsx" then Roo::Excelx.new(file.path)
         else raise "Unknown file type: #{file.original_filename}"
       end
     end
   end

Есть также некоторые глюки с этим этотрудно добавить ассоциацию, а также проверки

...