Сохранение ha sh, извлеченного из xlsx, в базе данных в ruby с использованием roo gem - PullRequest
0 голосов
/ 20 января 2020

Извините за мой плохой английский sh и за любую глупую ошибку, но я учу Ruby с нескольких месяцев. Я пытаюсь прочитать из файла .xlsx usign roo gem и после того, как я сохраню свои строки в базе данных, в уже существующей модели. Вот моя модель Суда:

class Court < ApplicationRecord

  belongs_to :type

  validates :type_id, presence: true
  validates :name, presence: true
  validates :email, presence: true, uniqueness: true
  validates :responsible, presence: true
  validates :address, presence: true
  validates :telephone, presence: true, format: { with: /\A([0-9]*\-?\ ?\/?[0-9]*)\Z/ }

  geocoded_by :address
  after_validation :geocode, :if => :address_changed?

end

# == Schema Information
#
# Table name: courts
#
#  id                  :integer          not null, primary key
#  type_id             :integer
#  name                :string(255)
#  email               :string(255)
#  email_type          :string(255)
#  responsible         :string(255)
#  address             :string(255)
#  telephone           :string(255)
#  latitude            :decimal
#  longitude           :decimal
#  created_at          :datetime         not null
#  updated_at          :datetime         not null
#

, и я создал следующую задачу: excel.rake , чтобы прочитать с рутом все файлы:

namespace :excel do

    desc "Import Courts from Excel"
    task import_courts: :environment do
      xlsx = Roo::Excelx.new(Rails.root.join('app', 'assets', 'excel', 'Tribunali.xlsx'))
      xlsx.parse(headers: true) do |hash|
        puts hash.inspect # Array of Excelx::Cell objects
        @court = Court.new
        @court.hash
        @court.save
      end
    end
end

В файле у меня те же заголовки модели, что, действительно, когда я запускаю задачу, я получаю следующий вывод:

{"Tipo"=>"Tipo", "Nome"=>"Nome", "Indirizzo e-mail"=>"Indirizzo e-mail", "Tipo email"=>"Tipo email", "Responsabile"=>"Responsabile", "Indirizzo"=>"Indirizzo", "Numero Telefonico"=>"Numero Telefonico"}
{"Tipo"=>"AMM", "Nome"=>"Ministero della Giustizia", "Indirizzo e-mail"=>"gabinetto.ministro@giustiziacert.it", "Tipo email"=>"PEC", "Responsabile"=>"Alfonso Bonafede", "Indirizzo"=>"Via Arenula, 70 - 00186 Roma (RM)", "Numero Telefonico"=>"06 68851"}
{"Tipo"=>"AOO", "Nome"=>"Archivio Notarile Distrettuale di Agrigento", "Indirizzo e-mail"=>"archivionotarile.agrigento@giustiziacert.it", "Tipo email"=>"PEC", "Responsabile"=>"Daniela Portera", "Indirizzo"=>"Via S. Vito, 97/103 - 92100 Agrigento (AG)", "Numero Telefonico"=>"09 2220290"}
{"Tipo"=>"AOO", "Nome"=>"Archivio Notarile Distrettuale di Alessandria", "Indirizzo e-mail"=>"archivionotarile.alessandria@giustiziacert.it", "Tipo email"=>"PEC", "Responsabile"=>"Susanna Cesarone Bongiorno", "Indirizzo"=>"Via Ghilini, 42 - 15121 Alessandria (AL)", "Numero Telefonico"=>"01 31254163"}
{"Tipo"=>"AOO", "Nome"=>"Archivio Notarile Distrettuale di Ancona", "Indirizzo e-mail"=>"archivionotarile.ancona@giustiziacert.it", "Tipo email"=>"PEC", "Responsabile"=>"Margherita Regini Santojanni", "Indirizzo"=>"Piazzale Europa, 7 - 60125 Ancona (AN)", "Numero Telefonico"=>"07 12804055"}
{"Tipo"=>"AOO", "Nome"=>"Archivio Notarile Distrettuale di Aosta", "Indirizzo e-mail"=>"archivionotarile.aosta@giustiziacert.it", "Tipo email"=>"PEC", "Responsabile"=>"Antonio Santoro", "Indirizzo"=>"Via Monsignor De Sales, 3 - 11100 Aosta (AO)", "Numero Telefonico"=>"01 65361395"}
{"Tipo"=>"AOO", "Nome"=>"Archivio Notarile Distrettuale di Arezzo", "Indirizzo e-mail"=>"archivionotarile.arezzo@giustiziacert.it", "Tipo email"=>"PEC", "Responsabile"=>"Gianna Baroni Pedone", "Indirizzo"=>"Via Francesco Crispi, 58/4 - 52100 Arezzo (AR)", "Numero Telefonico"=>"05 7523243"}

Но в базе данных я не могу хранить эти данные. Кто-нибудь может мне помочь, пожалуйста? Извините заранее, если я забыл что-то сказать.

1 Ответ

0 голосов
/ 20 января 2020

Я никогда не работал с гемом "roo", но немного посмотрел их документацию и код. Примерно так должно сработать:

# header_names and attribute_names must be provided in the same order
# since we work with them based on index later on (#fetch_values and #zip)
header_names = ['Tipo', 'Nome', '...']
attribute_names = [:type, :name, :'...']

Court.transaction do
  # use headers: false to not return the headers
  xlsx.parse(headers: false).each do |row|
    values = row.fetch_values(*header_names)
    attributes = attribute_names.zip(values).to_h
    Court.create!(attributes)
  end
end

Поскольку имена заголовков в листе и имена атрибутов моделей различаются, вам необходимо преобразовать возвращаемую строку, которая должна быть ядром Ха sh экземпляр.

Вы можете получить значения полей, используя fetch_values (вызывает исключение, если ключ отсутствует) или values_at (возвращает значение по умолчанию, если ключ отсутствует, который, если не установлен, равен nil). Затем zip заголовки модели со значениями и конвертируем все обратно в га sh.

Затем используйте create! для создания Court экземпляр и сохранить его в базе данных. Это вызывает исключение, когда что-то идет не так, откатывая transaction. Вы также можете использовать create, но это не исключение. Это означает, что недействительные записи молча проглатываются и просто не сохраняются.

Следует отметить, что ваш текущий вывод показывает "Tipo"=>"AMM", где "AMM" - тип, который я предполагаю. Однако это не идентификатор, поэтому вам может понадобиться сначала извлечь идентификаторы типа и, возможно, другие идентификаторы, прежде чем пытаться сохранить.

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

attributes_list = xlsx.parse(headers: false).map do |row|
  values = row.fetch_values(*header_names)
  attributes = attribute_names.zip(values).to_h
end

type_ids = Type.where(name: attributes_list.pluck(:type).uniq).pluck(:name, :id).to_h
# fetch other ids if needed.

attributes_list.each do |attributes|
  # remove :type and replace with :type_id (using #fetch to detect missing types)
  attributes[:type_id] = type_ids.fetch(attributes.delete(:type))
end

# create and save Court instances from the attributes
Court.transaction do
  attributes_list.each { |attributes| Court.create!(attributes) }
end
...