Using the ruby sequel gem, I have a small (sqlite3) database with the following schema:
db.create_table :item_types do
primary_key :id
String :name, unique: true, null: false
end
db.create_table :item_values do
primary_key :id
String :name, unique: true, null: false
end
db.create_table :items do
primary_key :id
foreign_key :type, :item_types, key: :id
foreign_key :value, :item_values, key: :id
String :name, unique: true, null: false
String :desc, null: false
end
Ideally, I'd like to be able to be able to use similar to
item = Item.first(...)
puts item.type.name, item.value.name
Reading through the association guide, it looks like I need a one_to_one association in the Item model for item_types and item_values. However, I'm not sure of the proper way to link them. Should the foreign key be passed via the key option, or is there a more appropriate method?
The guide illustrates setting up a one to many association with this example:
# Database schema:
# artists albums
# :id <----\ :id
# :name \----- :artist_id
# :name
class Artist < Sequel::Model
one_to_many :albums
end
class Album < Sequel::Model
many_to_one :artist
end
To be honest I have never used Sequel but from guide it seems like you should be doing something like:
class ItemType < Sequel::Model
one_to_many :items
end
class ItemValue < Sequel::Model
one_to_many :items
end
class Item < Sequel::Model
many_to_one :type, class: 'ItemType', key: 'item_type_id'
many_to_one :value, class: 'ItemValue', key: 'item_value_id'
end
Congratulations. You just ported the EAV anti-pattern to Sequel.
Related
how to say? I do not understand what the sequel documentation tries to tell me about associations in case of a two models linked over a foreign key in one model A being a primary key in the other in a may_to_one case.
I always thought: If it is many_to _one in one direction it has to be one_to_many in the other... but the sequel provides a confusing chapter meant to clarify the topic with in addition an example I cannot follow.
It says in
"Differences Between many_to_one and one_to_one"
If you want to setup a 1-1 relationship between two models, where the foreign > key in one table references the associated table directly, you have to use
many_to_one in one model, and one_to_one in the other model. How do
you know which to use in which model? The simplest way to remember is
that the model whose table has the foreign key uses many_to_one, and
the other model uses one_to_one"
And continues to provide this strange example:
# Database schema:
# artists albums
# :id <----\ :id
# :name \----- :artist_id
# :name
class Artist
one_to_one :album
end
class Album
many_to_one :artist
end
In albums I may find several rows pointing to same artist... why shouldn't the artist point back to all his/her albums?
The sequel docu is crazy hard to read in many cases but this chapter reads easy but makes no sense for me:(
Same issue for me.
require "logger"
require "sequel"
db = Sequel.connect "postgres://localhost/postgres", :logger => Logger.new(STDOUT)
db.drop_table :artists, :cascade => true if db.table_exists?(:artists)
db.create_table :artists do
primary_key :id
foreign_key :album_id, :albums
end
db.drop_table :albums, :cascade => true if db.table_exists?(:albums)
db.create_table :albums do
primary_key :id
foreign_key :artist_id, :artists
end
class Artist < Sequel::Model(db[:artists])
one_to_one :album
end
class Album < Sequel::Model(db[:albums])
one_to_one :artist
end
artist_1 = Artist.create
album_1 = Album.create
artist_1.update :album => album_1
album_1.reload
puts album_1.artist.nil?
artist_2 = Artist.create
album_2 = Album.create
album_2.update :artist => artist_2
artist_2.reload
puts artist_2.album.nil?
We can fix this example by replacing any of one_to_one into many_to_one.
class Album
many_to_one :artist
end
In this case artist.album_id won't be used.
class Artist
many_to_one :albums
end
In this case album.artist_id won't be used.
The problem is that method names one_to_one and many_to_one were selected by underlying sequel logic and they are not user friendly.
You can create user friendly aliases for these methods. I prefer just to use it with comments. For example:
db.create_table :artists do
primary_key :id
foreign_key :album_id, :albums
end
db.create_table :albums do
primary_key :id
end
class Artist < Sequel::Model(db[:artists])
many_to_one :album # I have album_id foreign key
end
class Album < Sequel::Model(db[:albums])
one_to_one :artist # I don't have artist_id foreign key
end
I have my resources:
resources :flows do
resources :fmodules
end
the new method in fmodules controller:
# /flows/1/fmodules/new
def new
#flow = Flow.find(params[:flow_id])
#fmodule = #flow.fmodules.build
end
the models:
class Flow < ApplicationRecord
has_many :fmodules, dependent: :destroy
validates :code, presence: true, length: { maximum: 5 }
validates :name, presence: true
end
class Fmodule < ApplicationRecord
belongs_to :flow
end
When i try to go at /flows/1/fmodules/new ruby says unknown attribute 'flow_id' for Fmodule.
I dont know what is wrong
Here is the migration of Fmodel in addition
class CreateFmodules < ActiveRecord::Migration[5.1]
def change
create_table :fmodules do |t|
t.string :code
t.string :name
t.string :f_code
t.timestamps
end
add_foreign_key :fmodules, :flows, column: :f_code
end
end
So, the problem is that you don't have a flow_id in your fmodules table. In rails, by convention the foreign key is build automatically inferring column name from an argument you pass to belongs_to. That's why rails think that foreign key for flows table is flow_id and it raises exception not finding it. You can overwrite the default with foreign_key option like the following
class Fmodule < ApplicationRecord
belongs_to :flow, foreign_key: : f_code
end
I've the following two models:
class Dispute < ApplicationRecord
belongs_to :accuser, class_name: 'User', optional: true
belongs_to :defendant, class_name: 'User', optional: true
end
class User < ApplicationRecord
end
Here's the migration for Dispute:
class CreateDisputes < ActiveRecord::Migration[5.0]
def change
create_table :disputes do |t|
t.references :accuser
t.references :defendant
end
end
end
This is how they behave in Rails:
Dispute.first.accuser
# => <# User>
Dispute.first.defendant
# => <# User>
In Sequel, I'm supposed to use many_to_one, but does that mean that Sequel User model should have a corresponding one_to_many? Can't seem to get it to work.
This should work:
Sequel.migration do
change do
create_table(:disputes) do
primary_key :id
foreign_key :accuser_id, :users
foreign_key :defendant_id, :users
end
end
end
class Dispute < Sequel::Model
many_to_one :accuser, :class=>:User
many_to_one :defendant, :class=>:User
end
Referencing Sequel's docs I've set up a one_to_one association between a Position and a Company.
class Position < Sequel::Model
one_to_one :company
end
class Company < Sequel::Model
many_to_one :position
end
When I try to get a company through a position I get nil, although I can find the company with a direct Sequel query.
p = Position.first #=> #<Position #values={:id=>1}>
p.company #=> nil
Company.where(position_id: p.id).first #=> #<Company #values={:id=>1, position_id: 1}>
You look confused about the relations or the schema. Unless you have a very specific business case, a company has many positions, which makes one-to-many relation and many positions can belong to one company, which makes many-to-one.
Here's how I see it:
require 'sqlite3'
require 'sequel'
DB = Sequel.connect('sqlite://companies')
DB.create_table :companies do
primary_key :id
String :name
end
DB[:companies].insert(name: 'Acme')
DB.create_table :positions do
primary_key :id
String :name
foreign_key :company_id, :companies
end
DB[:positions].insert(name: 'CEO', company_id: DB[:companies].first[:id])
DB[:positions].insert(name: 'CTO', company_id: DB[:companies].first[:id])
class Company < Sequel::Model
one_to_many :positions
end
class Position < Sequel::Model
many_to_one :company
end
p Company.first.positions
# [#<Position #values={:id=>1, :name=>"CEO", :company_id=>1}>, #<Position #values={:id=>2, :name=>"CTO", :company_id=>1}>]
p Position.first.company
# #<Company #values={:id=>1, :name=>"Acme"}>
I can get your associations to work:
require 'sequel'
DB = Sequel.connect('sqlite://test.db')
unless DB.table_exists? (:positions)
DB.create_table :positions do
primary_key :id
string :name
foreign_key :company_id
end
end
unless DB.table_exists?(:companies)
DB.create_table :companies do
primary_key :id
string :name
foreign_key :position_id
end
end
class Position < Sequel::Model
one_to_one :company
end
class Company < Sequel::Model
many_to_one :position
end
ford = Company.create(name: "Ford")
vw = Company.create(name: "VW")
accountant.company = ford
p accountant.company #=> #<Company #values={:id=>53, :name=>"Ford", :position_id=>35}>
puts accountant.id #=> 35
accountant.add_company(vw) #=> undefined method `add_company' for #<Position...
I'll add that the Sequel docs for associations are terrible. They need to include a complete example--including how to create the objects so that they are associated with each other.
I am having issues constructing the proper models, associations, and query for the following scenario and then returning results as JSON using Sequel with Ruby.
The database structure___
You can create a list of books. Each library contains books. Defined by the following:
db.create_table(:books) do
primary_key :id
String :name
String :author
DateTime :created
end
db.create_table(:libraries) do
primary_key :id
String :name
String :city
String :state
DateTime :created
end
db.create_table(:libraries_books) do
Integer :library_id
Integer :book_id
primary_key [:library_id, :book_id]
end
class Library < Sequel::Model(:libraries)
many_to_many :libraries_books, :left_key=>:library_id, :right_key=>:book_id, :join_table=>:libraries_books
one_to_many :libraries_books, :key=>:library_id
end
class LibraryBook < Sequel::Model(:libraries_books)
many_to_one :libraries
many_to_one :books
end
I am trying to determine the correct way to access all the book names for a given library. I initially tried to follow the Sequel Associations guide but was not able to figure out how I could use LibraryBook with associations to get all the books for a library and join on the Book model to get the proper columns.
After getting stuck with some of the methods described, I attempted to create my own query as such:
LibraryBook.select(:books.*)
.join_table(:inner, :libraries, :id => :library_id)
.join_table(:inner, :books, :id => :book_id)
.where(:library_id => 1)
Which seems to get me partially there. However, when I use the serialization extension, I get an error when the results are being converted:
undefined method `book_id' for #<LibraryGame:0x007fa9e904b470>
Any insight into that can be provided would be very helpful!
Try the following:
db.create_table(:books) do
primary_key :id
String :name
String :author
DateTime :created
end
db.create_table(:libraries) do
primary_key :id
String :name
String :city
String :state
DateTime :created
end
db.create_table(:books_libraries) do
foreign_key :library_id, :libraries, key: :id
foreign_key :book_id, :books, key: :id, index: true
primary_key [:library_id, :book_id]
end
class Library < Sequel::Model
many_to_many :books
end
class Book < Sequel::Model
many_to_many :libraries
end
Note renaming the libraries_books table to books_libraries and the use of the foreign_key directive for referential integrity. Conventions should allow things to just work.
Library[7].books # returns all books for library '7'
Or alternatively:
Book.where(libraries: Library[7])
Or multiple libraries:
Book.where(libraries: Library.where(id: [3,7,9]))
If sequel is not able to do the inflection for Library/Libraries then you may need to add your own inflection rule, eg:
Sequel.inflections do |inflect|
inflect.irregular 'Library', 'Libraries'
end