I am trying to build a database that has two principal tables build from csv input and link them together, but the associations are seemingly not being made – that is querying Language to get the family they belong returns nil every time.
I am either missing how Sequel works, or how should associations be construed in general.
My input data is:
language_families_csv
| level | family_code | parent_code | meta_comment |
|---|---|---|---|
| 1 | INE | Indo-European | |
| 2 | BAT | INE | Baltic |
| 3 | wes-BAT | BAT | Western Baltic |
| 3 | eas-BAT | BAT | Eastern Baltic |
| 2 | HYX | INE | Armenian |
| 2 | CEL | INE | Celtic |
| 2 | SLA | INE | Slavic |
| 3 | ZLE | SLA | East Slavic |
| 3 | ZLS | SLA | South Slavic |
| 4 | eas-ZLS | ZLS | Eastern South Slavic |
| 4 | wes-ZLS | ZLS | Western South Slavic |
| 3 | ZLW | SLA | West |
| 4 | leh-ZLW | ZLW | Lechitic |
| 4 | cze-ZLW | ZLW | Czech-Slovak |
| 4 | WEN | ZLW | Sorbian |
languages_csv
| language_code | parent_family | meta_comment |
|---|---|---|
| ENG | GMW | English |
| FRA | ROA | French |
| GER | GMW | German |
| SPA | ROA | Spanish |
| POL | leh-ZLW | Polish |
| CES | cze-ZLW | Czech |
| RUS | eas-ZLS | Russian |
| UKR | eas-ZLS | Ukrainian |
| BEL | eas-ZLS | Belarusian |
| CSB | leh-ZLW | Kashubian |
| POX | leh-ZLW | Polabian |
| SLK | cze-ZLW | Slovak |
My schema:
module Language_to_LanguageFamily
def self.included(base)
base.one_to_many :languages, key: :parent_family, class: :Language
base.many_to_one :parent_family, class: :LanguageFamily, key: :family_code, primary_key: :family_code
end
end
### Language_Families ###
MyDB.create_table :Language_Families do
primary_key :language_family_id
Integer :level, index: true, null: false
String :family_code, size: 7, index: true, null: false, unique: true
String :meta_comment, text: true
# with a self-referential association to the primary key
foreign_key :parent_code, :Language_Families, key: :language_family_id
end
class LanguageFamily < Sequel::Model
plugin :timestamps, update_on_create: true, force: true
plugin :json_serializer
plugin :validation_helpers
# self-referrential association
one_to_many :children, class: :LanguageFamily, key: :parent_code
many_to_one :parent, class: :LanguageFamily, key: :parent_code
# external associations
include Language_to_LanguageFamily
end
### Languages ###
MyDB.create_table :Languages do
primary_key :language_id
String :language_code, size: 7, index: true, null: false, unique: true
String :meta_comment, text: true
# ↓ optional association
foreign_key :parent_family, :Language_Families, key: :family_code, null: true, default: nil
end
class Language < Sequel::Model
plugin :timestamps, update_on_create: true, force: true
plugin :json_serializer
plugin :validation_helpers
# external associations
include Language_to_LanguageFamily
# ↓ allows accessing foreign keys
attr_accessor :parent_family
end
and my code:
require 'csv'
require 'sequel'
MyDB = Sequel.connect adapter: :sqlite, database: 'MirDB.sqlite'
MyDB.drop_table(*db.tables)
# Create an empty hash to store the id of each language family code
code_id = {}
# insert data into Language_Families DB
language_families_csv.each do |family|
# Create a new LanguageFamily object
language_family = LanguageFamily.new
language_family.level = family["level"].to_i
language_family.family_code = family["family_code"]
language_family.meta_comment = family["meta_comment"]
if parent_code = family["parent_code"]
language_family.parent = LanguageFamily.find(parent_code: parent_code)
end
language_family.save
code_id[language_family.family_code] = language_family.id
end
# insert data into Language DB
languages_csv.each do |lang_row|
# Create a new Language object
language = Language.new
language.language_code = lang_row["language_code"]
language.meta_comment = lang_row["meta_comment"]
# set the association to the corresponding language family if present in languages
if lang_row["parent_family"] # ← if in languages
lang_parent_family = LanguageFamily.first(family_code: lang_row["parent_family"])
if lang_parent_family # ← if such family exists
language.parent_family = lang_parent_family
# puts "#{language.parent_family} ↔ #{lang_parent_family.family_code}"
else
puts 'Family '.red + lang_parent_family.family_code.cyan + ' doesn’t exist!'.red
end
end
language.save
end
Finally, when I run puts Language.all.to_s, all I get is:
[
#<Language @values={:language_id=>1, :language_code=>"ENG", :meta_comment=>"English", :parent_family=>nil}>,
#<Language @values={:language_id=>2, :language_code=>"FRA", :meta_comment=>"French", :parent_family=>nil}>,
#<Language @values={:language_id=>3, :language_code=>"GER", :meta_comment=>"German", :parent_family=>nil}>,
#<Language @values={:language_id=>4, :language_code=>"SPA", :meta_comment=>"Spanish", :parent_family=>nil}>,
#<Language @values={:language_id=>5, :language_code=>"POL", :meta_comment=>"Polish", :parent_family=>nil}>,
#<Language @values={:language_id=>6, :language_code=>"CES", :meta_comment=>"Czech", :parent_family=>nil}>,
#<Language @values={:language_id=>7, :language_code=>"RUS", :meta_comment=>"Russian", :parent_family=>nil}>,
#<Language @values={:language_id=>8, :language_code=>"UKR", :meta_comment=>"Ukrainian", :parent_family=>nil}>,
#<Language @values={:language_id=>9, :language_code=>"BEL", :meta_comment=>"Belarusian", :parent_family=>nil}>,
#<Language @values={:language_id=>10, :language_code=>"CSB", :meta_comment=>"Kashubian", :parent_family=>nil}>,
#<Language @values={:language_id=>11, :language_code=>"POX", :meta_comment=>"Polabian", :parent_family=>nil}>,
#<Language @values={:language_id=>12, :language_code=>"SLK", :meta_comment=>"Slovak", :parent_family=>nil}>,
(…)
]
I've uncomplicated your set up a bit. You have to make sure your code works, before you start extracting modules, it makes it that much harder to figure out what's going on. Also this is my first time using Sequel.
Test:
Import test: