sequel 4.7.0 → 4.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +46 -0
- data/README.rdoc +25 -1
- data/doc/active_record.rdoc +1 -1
- data/doc/advanced_associations.rdoc +143 -17
- data/doc/association_basics.rdoc +80 -59
- data/doc/release_notes/4.8.0.txt +175 -0
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/odbc/mssql.rb +4 -2
- data/lib/sequel/adapters/shared/postgres.rb +19 -3
- data/lib/sequel/adapters/shared/sqlite.rb +3 -3
- data/lib/sequel/ast_transformer.rb +1 -1
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/graph.rb +23 -9
- data/lib/sequel/dataset/misc.rb +2 -2
- data/lib/sequel/dataset/sql.rb +3 -3
- data/lib/sequel/extensions/columns_introspection.rb +1 -1
- data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +1 -1
- data/lib/sequel/extensions/pg_array_ops.rb +6 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +7 -0
- data/lib/sequel/extensions/pg_json_ops.rb +5 -0
- data/lib/sequel/extensions/query.rb +8 -2
- data/lib/sequel/extensions/to_dot.rb +1 -1
- data/lib/sequel/model/associations.rb +476 -152
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -3
- data/lib/sequel/plugins/dataset_associations.rb +21 -18
- data/lib/sequel/plugins/many_through_many.rb +87 -20
- data/lib/sequel/plugins/nested_attributes.rb +12 -0
- data/lib/sequel/plugins/pg_array_associations.rb +31 -12
- data/lib/sequel/plugins/single_table_inheritance.rb +9 -1
- data/lib/sequel/sql.rb +1 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +2 -2
- data/spec/adapters/postgres_spec.rb +7 -0
- data/spec/core/object_graph_spec.rb +250 -196
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +100 -6
- data/spec/extensions/many_through_many_spec.rb +1002 -19
- data/spec/extensions/nested_attributes_spec.rb +24 -0
- data/spec/extensions/pg_array_associations_spec.rb +17 -12
- data/spec/extensions/pg_array_spec.rb +4 -2
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/integration/associations_test.rb +1003 -48
- data/spec/integration/dataset_test.rb +12 -5
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/type_test.rb +1 -1
- data/spec/model/associations_spec.rb +467 -130
- data/spec/model/eager_loading_spec.rb +332 -5
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c96550377e172f2fe1bbca889e66dbf6f4ba2ea
|
4
|
+
data.tar.gz: 09b5f64bdd1e48a538caf87c359f0196c5cecf87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d089308d0d6ae3643cd8d84d6a67ad2f130dbafc8bafc1d0432c6ce0e0f496f50c76fed0db3a503a82afc889be9ba7c5639f971b763d1a0b9def6867939ac743
|
7
|
+
data.tar.gz: 11bcfe87e96fbd8c61b336994cd142ceec0d0fe5964ab94ea4d2886e7304843180377c488515d2f87dcaf9839974d988c9805c3872baecc5e7f9c373ea2d70fc
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,49 @@
|
|
1
|
+
=== 4.8.0 (2014-03-01)
|
2
|
+
|
3
|
+
* Add SQL::AliasedExpression#alias alias for #aliaz (jeremyevans)
|
4
|
+
|
5
|
+
* Handle SQL::Identifier, SQL::QualifiedIdentifier, and SQL::AliasedExpression objects as first argument to Dataset#graph (jeremyevans)
|
6
|
+
|
7
|
+
* Respect qualification and aliases in symbols passed as first argument to Dataset#graph (dividedmind) (#769)
|
8
|
+
|
9
|
+
* Recognize new constraint violation error messages in SQLite 3.8.2+ (itswindtw) (#766)
|
10
|
+
|
11
|
+
* Use limit strategy to correctly handle limited associations in the dataset_associations plugin (jeremyevans)
|
12
|
+
|
13
|
+
* Handle issues in dataset_associations plugin when dataset uses unqualified identifiers for associations requiring joins (jeremyevans)
|
14
|
+
|
15
|
+
* Handle fractional seconds in input timestamps in the odbc/mssql adapter (Ross Attrill, jeremyevans)
|
16
|
+
|
17
|
+
* Return fractional seconds in timestamps in the odbc adapter (jeremyevans)
|
18
|
+
|
19
|
+
* Support :plain and :phrase options to Dataset#full_text_search on PostgreSQL (jeremyevans)
|
20
|
+
|
21
|
+
* Use limit strategy to correctly handle filtering by limited associations (jeremyevans)
|
22
|
+
|
23
|
+
* Simplify queries used for filtering by associations with conditions (jeremyevans)
|
24
|
+
|
25
|
+
* Use an eager limit strategy by default for *_one associations with orders (jeremyevans)
|
26
|
+
|
27
|
+
* Support :limit_strategy eager_graph option, for specifying strategy used for limited associations in that eager graph (jeremyevans)
|
28
|
+
|
29
|
+
* Add eager_graph_with_options to model datasets, for specifying options specific to the eager_graph call (jeremyevans)
|
30
|
+
|
31
|
+
* Handle offsets on *_many associations when eager graphing when there are no associated results (jeremyevans)
|
32
|
+
|
33
|
+
* Make Database#register_array_type work without existing scalar conversion proc in the pg_array extension (jeremyevans)
|
34
|
+
|
35
|
+
* Handle presence validations on foreign keys in associated objects when creating new associated objects in the nested_attributes plugin (jeremyevans)
|
36
|
+
|
37
|
+
* Respect offsets when eager graphing *_one associations (jeremyevans)
|
38
|
+
|
39
|
+
* Add association_join to model datasets, for setting up joins based on associations (jeremyevans)
|
40
|
+
|
41
|
+
* Add one_through_many association to many_through_many plugin, for only returning a single record (jeremyevans)
|
42
|
+
|
43
|
+
* Add :graph_order association option, useful when :order needs to contain qualified identifiers (jeremyevans)
|
44
|
+
|
45
|
+
* Add one_through_one association, similar to many_to_many but only returning a single record (jeremyevans)
|
46
|
+
|
1
47
|
=== 4.7.0 (2014-02-01)
|
2
48
|
|
3
49
|
* Don't swallow underlying exception if there is an exception closing the cursor on PostgreSQL (jeremyevans) (#761)
|
data/README.rdoc
CHANGED
@@ -639,12 +639,14 @@ SQL query.
|
|
639
639
|
|
640
640
|
=== Associations
|
641
641
|
|
642
|
-
Associations are used in order to specify relationships between model classes that reflect relationships between tables in the database, which are usually specified using foreign keys. You specify model associations via
|
642
|
+
Associations are used in order to specify relationships between model classes that reflect relationships between tables in the database, which are usually specified using foreign keys. You specify model associations via class methods:
|
643
643
|
|
644
644
|
class Post < Sequel::Model
|
645
645
|
many_to_one :author
|
646
646
|
one_to_many :comments
|
647
|
+
one_to_one :first_comment, :class=>:Comment, :order=>:id
|
647
648
|
many_to_many :tags
|
649
|
+
one_through_one :first_tag, :class=>:Tag, :order=>:name, :right_key=>:tag_id
|
648
650
|
end
|
649
651
|
|
650
652
|
+many_to_one+ and +one_to_one+ create a getter and setter for each model object:
|
@@ -745,6 +747,28 @@ You can dynamically customize eager loads for both +eager+ and +eager_graph+ whi
|
|
745
747
|
# Eagerly load only replies containing 'foo', and the person and tags for those replies
|
746
748
|
Post.eager(:replies=>{proc{|ds| ds.where(Sequel.like(text, '%foo%'))}=>[:person, :tags]}).all
|
747
749
|
|
750
|
+
=== Joining with Associations
|
751
|
+
|
752
|
+
You can use the association_join method to add a join to the model's dataset based on the assocation:
|
753
|
+
|
754
|
+
Post.association_join(:author)
|
755
|
+
# SELECT * FROM posts
|
756
|
+
# INNER JOIN authors AS author ON (author.id = posts.author_id)
|
757
|
+
|
758
|
+
This comes with variants for different join types:
|
759
|
+
|
760
|
+
Post.association_left_join(:replies)
|
761
|
+
# SELECT * FROM posts
|
762
|
+
# LEFT JOIN replies ON (replies.post_id = posts.id)
|
763
|
+
|
764
|
+
Similar to the eager loading methods, you can use multiple associations and nested associations:
|
765
|
+
|
766
|
+
Post.association_join(:author, :replies=>:person).all
|
767
|
+
# SELECT * FROM posts
|
768
|
+
# INNER JOIN authors AS author ON (author.id = posts.author_id)
|
769
|
+
# INNER JOIN replies ON (replies.post_id = posts.id)
|
770
|
+
# INNER JOIN people AS person ON (person.id = replies.person_id)
|
771
|
+
|
748
772
|
=== Extending the underlying dataset
|
749
773
|
|
750
774
|
The recommended way to implement table-wide logic by defining methods on the dataset using +dataset_module+:
|
data/doc/active_record.rdoc
CHANGED
@@ -396,7 +396,7 @@ ActiveRecord option :: Sequel option
|
|
396
396
|
<tt>:polymorphic</tt>, <tt>:as</tt>, <tt>:source_type</tt> :: The +sequel_polymorphic+ external plugin
|
397
397
|
<tt>:include</tt> :: <tt>:eager</tt>, <tt>:eager_graph</tt>
|
398
398
|
<tt>:readonly</tt> :: No equivalent, the Sequel <tt>:read_only</tt> option just means the modification methods are not created (it makes the association read only, not records retrieved through the association)
|
399
|
-
<tt>:through</tt>, <tt>:source</tt> :: Use a +many_to_many+ association, or the +many_through_many+ plugin
|
399
|
+
<tt>:through</tt>, <tt>:source</tt> :: Use a +many_to_many+ or +one_through_one+ association, or the +many_through_many+ plugin
|
400
400
|
<tt>:touch</tt> :: The +touch+ plugin
|
401
401
|
<tt>:autosave</tt> :: A +before_save+ or +after_save+ hook
|
402
402
|
<tt>:finder_sql</tt> :: <tt>:dataset</tt> to set a custom dataset
|
@@ -240,6 +240,145 @@ Using the :eager_loader proc, you should be able to eagerly load all association
|
|
240
240
|
that can be eagerly loaded, even if Sequel doesn't natively support such eager
|
241
241
|
loading.
|
242
242
|
|
243
|
+
== Limited Associations
|
244
|
+
|
245
|
+
Sequel supports specifying limits and/or offsets for associations:
|
246
|
+
|
247
|
+
Artist.one_to_many :first_10_albums, :class=>:Album, :order=>:release_date, :limit=>10
|
248
|
+
|
249
|
+
For retrieving the associated objects for a single object, this just uses
|
250
|
+
a LIMIT:
|
251
|
+
|
252
|
+
artist.first_10_albums
|
253
|
+
# SELECT * FROM albums WHERE (artist_id = 1) LIMIT 10
|
254
|
+
|
255
|
+
=== Eager Loading via eager
|
256
|
+
|
257
|
+
However, if you want to eagerly load an association, you must use a different
|
258
|
+
approach. Sequel has 3 separate strategies for dealing with such cases,
|
259
|
+
depending on database support.
|
260
|
+
|
261
|
+
On PostgreSQL, for *_one associations that don't use an offset, it will by
|
262
|
+
default use the distinct on strategy, which uses a DISTINCT ON clause:
|
263
|
+
|
264
|
+
Artist.one_to_one :first_album, :class=>:Album, :order=>:release_date
|
265
|
+
Artist.where(:id=>[1,2]).eager(:first_album).all
|
266
|
+
# SELECT DISTINCT ON (albums.artist_id) *
|
267
|
+
# FROM albums
|
268
|
+
# WHERE (albums.artist_id IN (1, 2))
|
269
|
+
# ORDER BY albums.artist_id, release_date
|
270
|
+
|
271
|
+
Otherwise, if the database supports window functions, it will use a subquery
|
272
|
+
with a window function:
|
273
|
+
|
274
|
+
Artist.where(:id=>[1,2]).eager(:first_10_albums).all
|
275
|
+
# SELECT * FROM (
|
276
|
+
# SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY release_date) AS x_sequel_row_number_x
|
277
|
+
# FROM tracks
|
278
|
+
# WHERE (tracks.album_id IN (1, 2))
|
279
|
+
# ) AS t1
|
280
|
+
# WHERE (x_sequel_row_number_x <= 10)
|
281
|
+
|
282
|
+
If the database doesn't support window functions, it will fall back to
|
283
|
+
retrieving all records, and then will slice the resulting array to get
|
284
|
+
the first 10 after retrieval.
|
285
|
+
|
286
|
+
=== Eager Loading via eager_graph_with_options
|
287
|
+
|
288
|
+
When eager loading an association via eager_graph (which uses JOINs), the
|
289
|
+
situation is similar. Sequel can use a variant of the same 3 strategies,
|
290
|
+
but by default it retrieves all records and then does the array slice
|
291
|
+
in ruby. As eager_graph does not support options, to use an eager_graph
|
292
|
+
limit strategy you have to use the eager_graph_with_options method with
|
293
|
+
the :limit_strategy option.
|
294
|
+
|
295
|
+
The distinct on strategy uses DISTINCT ON in a subquery and JOINs that
|
296
|
+
subquery:
|
297
|
+
|
298
|
+
Artist.eager_graph_with_options(:first_album, :limit_strategy=>true).all
|
299
|
+
# SELECT artists.id, artists.name, first_album.id AS first_album_id,
|
300
|
+
# first_album.name AS first_album_name, first_album.artist_id,
|
301
|
+
# first_album.release_date
|
302
|
+
# FROM artists
|
303
|
+
# LEFT OUTER JOIN (
|
304
|
+
# SELECT DISTINCT ON (albums.artist_id) *
|
305
|
+
# FROM albums
|
306
|
+
# ORDER BY albums.artist_id, release_date
|
307
|
+
# ) AS first_album ON (first_album.artist_id = artists.id)
|
308
|
+
|
309
|
+
The window function approach JOINs to a nested subquery using a window
|
310
|
+
function:
|
311
|
+
|
312
|
+
Artist.eager_graph_with_options(:first_10_albums, :limit_strategy=>true).all
|
313
|
+
# SELECT artists.id, artists.name, first_10_albums.id AS first_10_albums_id,
|
314
|
+
# first_10_albums.name AS first_10_albums_name, first_10_albums.artist_id,
|
315
|
+
# first_10_albums.release_date
|
316
|
+
# FROM artists
|
317
|
+
# LEFT OUTER JOIN (
|
318
|
+
# SELECT id, name, artist_id, release_date
|
319
|
+
# FROM (
|
320
|
+
# SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY release_date) AS x_sequel_row_number_x
|
321
|
+
# FROM albums
|
322
|
+
# ) AS t1 WHERE (x_sequel_row_number_x <= 10)
|
323
|
+
# ) AS first_10_albums ON (first_10_albums.artist_id = artists.id)
|
324
|
+
|
325
|
+
The reason that Sequel does not automatically use the distinct on or
|
326
|
+
window function strategy for eager_graph is that it can perform much worse than the
|
327
|
+
default of just doing the array slicing in ruby. If you are only using eager_graph to
|
328
|
+
return a few records, it may be cheaper to get all of their associated records and filter
|
329
|
+
them in ruby as opposed to computing the set of limited associated records for all rows.
|
330
|
+
|
331
|
+
It's recommended to only use an eager_graph limit strategy if you have benchmarked
|
332
|
+
it against the default behavior and found it is faster for your use case.
|
333
|
+
|
334
|
+
=== Filtering By Associations
|
335
|
+
|
336
|
+
In order to return correct results, Sequel automatically uses a limit strategy when
|
337
|
+
using filtering by associations with limited associations, using a similar approach as
|
338
|
+
when using eager to eagerly load.
|
339
|
+
|
340
|
+
The distinct on strategy:
|
341
|
+
|
342
|
+
Artist.where(:first_album=>Album[1]).all
|
343
|
+
# SELECT *
|
344
|
+
# FROM artists
|
345
|
+
# WHERE (artists.id IN (
|
346
|
+
# SELECT albums.artist_id
|
347
|
+
# FROM albums
|
348
|
+
# WHERE ((albums.artist_id IS NOT NULL) AND (albums.id IN (
|
349
|
+
# SELECT DISTINCT ON (albums.artist_id) albums.id
|
350
|
+
# FROM albums
|
351
|
+
# ORDER BY albums.artist_id, release_date
|
352
|
+
# )) AND (albums.id = 1))))
|
353
|
+
|
354
|
+
The window function strategy:
|
355
|
+
|
356
|
+
Artist.where(:first_10_albums=>Album[1]).all
|
357
|
+
# SELECT *
|
358
|
+
# FROM artists
|
359
|
+
# WHERE (artists.id IN (
|
360
|
+
# SELECT albums.artist_id
|
361
|
+
# FROM albums
|
362
|
+
# WHERE ((albums.artist_id IS NOT NULL) AND (albums.id IN (
|
363
|
+
# SELECT id FROM (
|
364
|
+
# SELECT albums.id, row_number() OVER (PARTITION BY albums.artist_id ORDER BY release_date) AS x_sequel_row_number_x
|
365
|
+
# FROM albums
|
366
|
+
# ) AS t1
|
367
|
+
# WHERE (x_sequel_row_number_x <= 10)
|
368
|
+
# )) AND (albums.id = 1))))
|
369
|
+
|
370
|
+
Note that if the database does not support window functions, filtering by associations
|
371
|
+
with limited associations will not work correctly, leading it to return results where
|
372
|
+
associated object used as an argument is not within the limit of the association.
|
373
|
+
|
374
|
+
== Additional Association Types
|
375
|
+
|
376
|
+
While the above examples for limited associations showed one_to_many and one_to_one associations,
|
377
|
+
it's just because those are the simplest examples. Sequel supports all of the same features for
|
378
|
+
many_to_many and one_through_one associations that are enabled by default, as well as the
|
379
|
+
many_through_many and one_through_many associations that are added by the many_through_many
|
380
|
+
plugin.
|
381
|
+
|
243
382
|
== ActiveRecord associations
|
244
383
|
|
245
384
|
Sequel supports all of associations that ActiveRecord supports, though some
|
@@ -361,27 +500,14 @@ Sequel::Model:
|
|
361
500
|
|
362
501
|
class Invoice < Sequel::Model
|
363
502
|
many_to_one :client
|
364
|
-
|
365
|
-
# has_one :through equivalent 1
|
366
|
-
# eager load with :eager=>:firm option on :client association, and eager loading :client
|
367
|
-
def firm
|
368
|
-
client.firm if client
|
369
|
-
end
|
370
|
-
|
371
|
-
# has_one :through equivalent 2
|
372
|
-
# eager load the usual way
|
373
|
-
many_to_many :firms, :join_table=>:clients, :left_key=>:id, :left_primary_key=>:client_id, :right_key=>:firm_id
|
374
|
-
def firm
|
375
|
-
firms.first
|
376
|
-
end
|
377
|
-
|
378
|
-
# has_one :through equivalent 3
|
379
|
-
# eager loading requires custom :eager_loader proc
|
380
|
-
many_to_one :firm, :dataset=>proc{Firm.join(:clients, :firm_id=>:id, :id=>client_id).select_all(:firms)}
|
503
|
+
one_through_one :firm, :join_table=>:clients, :left_key=>:id, :left_primary_key=>:client_id, :right_key=>:firm_id
|
381
504
|
end
|
382
505
|
|
383
506
|
Firm.first.invoices
|
384
507
|
|
508
|
+
To handle cases where there are multiple join tables, use the many_through_many
|
509
|
+
plugin that ships with Sequel.
|
510
|
+
|
385
511
|
=== Polymorphic Associations
|
386
512
|
|
387
513
|
Sequel discourages the use of polymorphic associations, which is the reason they
|
data/doc/association_basics.rdoc
CHANGED
@@ -41,14 +41,23 @@ As is the code to add a related album to an artist:
|
|
41
41
|
|
42
42
|
@artist.add_album(:name=>'RF')
|
43
43
|
|
44
|
+
It also makes it easier to creating queries that use joins based on the association:
|
45
|
+
|
46
|
+
Artist.association_join(:albums)
|
47
|
+
# SELECT * FROM artists
|
48
|
+
# INNER JOIN albums ON (albums.artist_id = artists.id)
|
49
|
+
|
44
50
|
== The Types of Associations
|
45
51
|
|
46
|
-
Sequel has
|
52
|
+
Sequel has five different association types built in:
|
47
53
|
|
48
54
|
* many_to_one
|
49
55
|
* one_to_many
|
50
56
|
* one_to_one
|
51
57
|
* many_to_many
|
58
|
+
* one_through_one
|
59
|
+
|
60
|
+
It ships with additional association types via plugins.
|
52
61
|
|
53
62
|
=== many_to_one
|
54
63
|
|
@@ -68,13 +77,19 @@ table for each row in the associated table.
|
|
68
77
|
many_to_one :artist
|
69
78
|
end
|
70
79
|
|
71
|
-
=== one_to_many
|
80
|
+
=== one_to_many and one_to_one
|
72
81
|
|
73
82
|
The one_to_many association is used when the table for the associated class
|
74
83
|
contains a foreign key that references the primary key in the table for the
|
75
84
|
current class. It is named because for each row in the current table there
|
76
85
|
can be many rows in the associated table:
|
77
86
|
|
87
|
+
The one_to_one association can be thought of as a subset of the one_to_many association,
|
88
|
+
but where there can only be either 0 or 1 records in the associated table. This is
|
89
|
+
useful if there is a unique constraint on the foreign key field in the associated table.
|
90
|
+
It's also useful if you want to impose an order on the association and just want the
|
91
|
+
first record returned.
|
92
|
+
|
78
93
|
# Database schema:
|
79
94
|
# artists albums
|
80
95
|
# :id <----\ :id
|
@@ -84,27 +99,12 @@ can be many rows in the associated table:
|
|
84
99
|
class Artist
|
85
100
|
# Uses plural form of associated model name
|
86
101
|
one_to_many :albums
|
87
|
-
end
|
88
|
-
|
89
|
-
=== one_to_one
|
90
|
-
|
91
|
-
The one_to_one association can be thought of as a subset of the one_to_many association,
|
92
|
-
but where there can only be either 0 or 1 records in the associated table.
|
93
|
-
It is the least frequently used of the four associations. If you assume
|
94
|
-
each artist cannot be associated with more than one album:
|
95
102
|
|
96
|
-
# Database schema:
|
97
|
-
# artists albums
|
98
|
-
# :id <----\ :id
|
99
|
-
# :name \----- :artist_id
|
100
|
-
# :name
|
101
|
-
|
102
|
-
class Artist
|
103
103
|
# Uses singular form of associated model name
|
104
104
|
one_to_one :album
|
105
105
|
end
|
106
|
-
|
107
|
-
=== many_to_many
|
106
|
+
|
107
|
+
=== many_to_many and one_through_one
|
108
108
|
|
109
109
|
The many_to_many association allows each row in the current table to be associated
|
110
110
|
to many rows in the associated table, and each row in the associated table to
|
@@ -112,6 +112,14 @@ many rows in the current table, by using a join table to associate the two table
|
|
112
112
|
If you assume each artist can have multiple albums and each album can have multiple
|
113
113
|
artists:
|
114
114
|
|
115
|
+
The one_through_one association can be thought of as a subset of the many_to_many
|
116
|
+
association, but where there can only be 0 or 1 records in the associated table.
|
117
|
+
This is useful if there is a unique constraint on the foreign key in the join table
|
118
|
+
that refrences the current table. It's also useful if you want to impose an order
|
119
|
+
on the association and just want the first record returned. The one_through_one
|
120
|
+
association is so named because it sets up a one-to-one association through a
|
121
|
+
single join table.
|
122
|
+
|
115
123
|
# Database schema:
|
116
124
|
# albums
|
117
125
|
# :id <----\
|
@@ -124,14 +132,15 @@ artists:
|
|
124
132
|
class Artist
|
125
133
|
# Uses plural form of associated model name
|
126
134
|
many_to_many :albums
|
127
|
-
|
128
|
-
|
129
|
-
|
135
|
+
|
136
|
+
# Uses plural form of associated model name
|
137
|
+
one_through_one :album
|
130
138
|
end
|
131
139
|
|
132
140
|
=== Differences Between many_to_one and one_to_one
|
133
141
|
|
134
|
-
If you want to setup a 1-1 relationship between two models,
|
142
|
+
If you want to setup a 1-1 relationship between two models, where the
|
143
|
+
foreign key in one table references the associated table directly, you have to use
|
135
144
|
many_to_one in one model, and one_to_one in the other model. How do you
|
136
145
|
know which to use in which model?
|
137
146
|
|
@@ -293,6 +302,8 @@ Examples:
|
|
293
302
|
@artist.remove_album(@album)
|
294
303
|
@artist.remove_all_albums
|
295
304
|
|
305
|
+
one_through_one associations do not have any modification methods added.
|
306
|
+
|
296
307
|
== Caching
|
297
308
|
|
298
309
|
Associations are cached after being retrieved:
|
@@ -334,7 +345,7 @@ ending in +_dataset+ that returns a dataset representing the objects in the asso
|
|
334
345
|
@album.artist_id
|
335
346
|
# 10
|
336
347
|
@album.artist_dataset
|
337
|
-
# SELECT * FROM artists WHERE (id = 10)
|
348
|
+
# SELECT * FROM artists WHERE (id = 10) LIMIT 1
|
338
349
|
|
339
350
|
@artist.id
|
340
351
|
# 20
|
@@ -400,7 +411,7 @@ can combine the approaches:
|
|
400
411
|
@artist.albums_dataset.where(:publisher=>@publisher)
|
401
412
|
|
402
413
|
This doesn't just work for +many_to_one+ associations, it also works for
|
403
|
-
|
414
|
+
the other associations:
|
404
415
|
|
405
416
|
Album.one_to_one :album_info
|
406
417
|
# The album related to that AlbumInfo instance
|
@@ -414,6 +425,10 @@ This doesn't just work for +many_to_one+ associations, it also works for
|
|
414
425
|
# All albums related to that Tag instance
|
415
426
|
Album.where(:tags=>Tag[4])
|
416
427
|
|
428
|
+
Album.one_through_one :tag
|
429
|
+
# All albums related to that Tag instance
|
430
|
+
Album.where(:tag=>Tag[4])
|
431
|
+
|
417
432
|
Note that for +one_to_many+ and +many_to_many+ associations, you still
|
418
433
|
use the plural form even though only a single model object is given.
|
419
434
|
|
@@ -474,10 +489,7 @@ This will return all albums that whose popular tags would include
|
|
474
489
|
at least one of those tags.
|
475
490
|
|
476
491
|
Note that filtering by associations does not work for associations
|
477
|
-
that use blocks with instance-specific code
|
478
|
-
have a limit or offset. This includes many_to_one/one_to_one
|
479
|
-
associations that would return multiple values if they were not
|
480
|
-
limited to a single value.
|
492
|
+
that use blocks with instance-specific code.
|
481
493
|
|
482
494
|
== Name Collisions
|
483
495
|
|
@@ -966,7 +978,7 @@ Use an array with two arguments for the value to specify a limit and an offset.
|
|
966
978
|
This probably doesn't make a lot of sense for *_to_one associations, though you
|
967
979
|
could use it to specify an offset.
|
968
980
|
|
969
|
-
==== :join_table [+many_to_many+]
|
981
|
+
==== :join_table [+many_to_many+, +one_through_one+]
|
970
982
|
|
971
983
|
Name of table that includes the foreign keys to both the current model and the
|
972
984
|
associated model, as a symbol. Defaults to the name of current model and name
|
@@ -977,7 +989,7 @@ Here's an example of the defaults:
|
|
977
989
|
Album.many_to_many :artists # :join_table=>:albums_artists
|
978
990
|
Person.many_to_many :colleges # :join_table=>:colleges_people
|
979
991
|
|
980
|
-
==== :left_key [+many_to_many+]
|
992
|
+
==== :left_key [+many_to_many+, +one_through_one+]
|
981
993
|
|
982
994
|
Foreign key in join table that points to current model's primary key, as a
|
983
995
|
symbol. Defaults to :"#{model_name.underscore}_id".
|
@@ -986,10 +998,11 @@ symbol. Defaults to :"#{model_name.underscore}_id".
|
|
986
998
|
|
987
999
|
Can use an array of symbols for a composite key association.
|
988
1000
|
|
989
|
-
==== :right_key [+many_to_many+]
|
1001
|
+
==== :right_key [+many_to_many+, +one_through_one+]
|
990
1002
|
|
991
1003
|
Foreign key in join table that points to associated model's primary key, as a
|
992
|
-
symbol. Defaults to :"#{association_name.singularize}_id"
|
1004
|
+
symbol. Defaults to :"#{association_name.singularize}_id" for +many_to_many+
|
1005
|
+
and :"#{association_name}_id" for +one_through_one+.
|
993
1006
|
|
994
1007
|
Album.many_to_many :tags # :right_key=>:tag_id
|
995
1008
|
|
@@ -1065,7 +1078,7 @@ A module or array of modules to extend the dataset with. These are used to
|
|
1065
1078
|
set up association extensions. For more information , please see the
|
1066
1079
|
{Advanced Associations page}[rdoc-ref:doc/advanced_associations.rdoc].
|
1067
1080
|
|
1068
|
-
==== :primary_key
|
1081
|
+
==== :primary_key [+many_to_one+, +one_to_one+, +one_to_many+]
|
1069
1082
|
|
1070
1083
|
The column that the :key option references, as a symbol. For +many_to_one+
|
1071
1084
|
associations, this column is in the associated table. For +one_to_one+ and
|
@@ -1077,7 +1090,7 @@ array of symbols for a composite key association.
|
|
1077
1090
|
Artist.one_to_many :albums # :primary_key=>:arid
|
1078
1091
|
Album.one_to_many :artist # :primary_key=>:arid
|
1079
1092
|
|
1080
|
-
==== :left_primary_key [+many_to_many+]
|
1093
|
+
==== :left_primary_key [+many_to_many+, +one_through_one+]
|
1081
1094
|
|
1082
1095
|
Column in current table that :left_key option points to, as a symbol.
|
1083
1096
|
Defaults to primary key of current table.
|
@@ -1087,7 +1100,7 @@ Defaults to primary key of current table.
|
|
1087
1100
|
|
1088
1101
|
Can use an array of symbols for a composite key association.
|
1089
1102
|
|
1090
|
-
==== :right_primary_key [+many_to_many+]
|
1103
|
+
==== :right_primary_key [+many_to_many+, +one_through_one+]
|
1091
1104
|
|
1092
1105
|
Column in associated table that :right_key points to, as a symbol.
|
1093
1106
|
Defaults to primary key of the associated table.
|
@@ -1097,7 +1110,7 @@ Defaults to primary key of the associated table.
|
|
1097
1110
|
|
1098
1111
|
Can use an array of symbols for a composite key association.
|
1099
1112
|
|
1100
|
-
==== :join_table_block [+many_to_many+]
|
1113
|
+
==== :join_table_block [+many_to_many+, +one_through_one+]
|
1101
1114
|
|
1102
1115
|
A proc that can be used to modify the dataset used in the add/remove/remove_all
|
1103
1116
|
methods. It's separate from the association block, as that is called on a
|
@@ -1389,12 +1402,14 @@ needed, as one of the other eager_graph related association options is usually s
|
|
1389
1402
|
If specified, should be a proc that accepts a single hash argument, which will contain
|
1390
1403
|
at least the following keys:
|
1391
1404
|
|
1392
|
-
:self :: The dataset that is doing the eager loading
|
1393
|
-
:table_alias :: An alias to use for the table to graph for this association.
|
1394
|
-
:implicit_qualifier :: The alias that was used for the current table (since you can cascade associations).
|
1395
1405
|
:callback :: A callback proc used to dynamically modify the dataset to graph into the
|
1396
1406
|
current dataset, before such graphing is done. This is nil if no callback
|
1397
1407
|
proc is used.
|
1408
|
+
:implicit_qualifier :: The alias that was used for the current table (since you can cascade associations).
|
1409
|
+
:join_type :: Override the join type to use when graphing.
|
1410
|
+
:limit_strategy :: The limit strategy symbol to use when graphing (for limited associations only)
|
1411
|
+
:self :: The dataset that is doing the eager loading
|
1412
|
+
:table_alias :: An alias to use for the table to graph for this association.
|
1398
1413
|
|
1399
1414
|
Example:
|
1400
1415
|
|
@@ -1413,7 +1428,15 @@ Sequel has to do some guess work when attempting to add the association's
|
|
1413
1428
|
order to an eager_graphed dataset. In most cases it does so correctly, but
|
1414
1429
|
if it has problems, you'll probably want to set this option to false.
|
1415
1430
|
|
1416
|
-
==== :
|
1431
|
+
==== :graph_order
|
1432
|
+
|
1433
|
+
Override the order added when using eager_graph, instead of using the one
|
1434
|
+
defined in :order. This is useful if :order contains qualified identifiers,
|
1435
|
+
as the qualifiers may not match the aliases automatically used by eager_graph.
|
1436
|
+
This should contain unqualified identifiers, and eager_graph will automatically
|
1437
|
+
qualify them with the appropriate alias.
|
1438
|
+
|
1439
|
+
==== :graph_join_table_conditions [+many_to_many+, +one_through_one+]
|
1417
1440
|
|
1418
1441
|
The additional conditions to use on the SQL join for the join table when
|
1419
1442
|
eagerly loading the association via eager_graph. Should be a hash or an array
|
@@ -1428,7 +1451,7 @@ has received a specific degree:
|
|
1428
1451
|
:join_table=>:degrees_received,
|
1429
1452
|
:graph_join_table_conditions=>{:degree=>'BS'}
|
1430
1453
|
|
1431
|
-
==== :graph_join_table_block [+many_to_many+]
|
1454
|
+
==== :graph_join_table_block [+many_to_many+, +one_through_one+]
|
1432
1455
|
|
1433
1456
|
The block to pass to join_table for the join table when eagerly loading the
|
1434
1457
|
association via eager_graph. This is used for similar reasons as :graph_block,
|
@@ -1450,7 +1473,7 @@ has received a bachelor's degree (degree starting with B):
|
|
1450
1473
|
This should be done when graphing the join table, instead of when graphing the
|
1451
1474
|
final table, as :degree is a column of the join table.
|
1452
1475
|
|
1453
|
-
==== :graph_join_table_join_type [+many_to_many+]
|
1476
|
+
==== :graph_join_table_join_type [+many_to_many+, +one_through_one+]
|
1454
1477
|
|
1455
1478
|
The type of SQL join to use for the join table when eagerly loading the
|
1456
1479
|
association via eager_graph. Defaults to the :graph_join_type option or
|
@@ -1458,7 +1481,7 @@ association via eager_graph. Defaults to the :graph_join_type option or
|
|
1458
1481
|
you want to use a different join type when JOINing to the join table then
|
1459
1482
|
you want to use for JOINing to the final table
|
1460
1483
|
|
1461
|
-
==== :graph_join_table_only_conditions [+many_to_many+]
|
1484
|
+
==== :graph_join_table_only_conditions [+many_to_many+, +one_through_one+]
|
1462
1485
|
|
1463
1486
|
The conditions to use on the SQL join for the join table when eagerly loading
|
1464
1487
|
the association via eager_graph, instead of the default conditions specified
|
@@ -1525,12 +1548,12 @@ Like the :primary_key option, but :primary_key references the method name, while
|
|
1525
1548
|
Like the :key option, but :key references the column
|
1526
1549
|
name, while :key_method references the method name.
|
1527
1550
|
|
1528
|
-
==== :left_primary_key_column [+many_to_many+]
|
1551
|
+
==== :left_primary_key_column [+many_to_many+, +one_through_one+]
|
1529
1552
|
|
1530
1553
|
Like the :left_primary_key option, but :left_primary_key references the method name, while
|
1531
1554
|
:left_primary_key_column references the underlying column.
|
1532
1555
|
|
1533
|
-
==== :right_primary_key_method [+many_to_many+]
|
1556
|
+
==== :right_primary_key_method [+many_to_many+, +one_through_one+]
|
1534
1557
|
|
1535
1558
|
Like the :right_primary_key option, but :right_primary_key references the column
|
1536
1559
|
name, while :right_primary_key_method references the method name.
|
@@ -1678,7 +1701,7 @@ instances.
|
|
1678
1701
|
==== :cartesian_product_number
|
1679
1702
|
|
1680
1703
|
The number of joins completed by this association that could cause more
|
1681
|
-
than one row for each row in the current table (default: 0 for *
|
1704
|
+
than one row for each row in the current table (default: 0 for *_one
|
1682
1705
|
associations, 1 for *_to_many associations).
|
1683
1706
|
|
1684
1707
|
This should only be modified in specific cases. For example, if you have
|
@@ -1703,40 +1726,38 @@ plugin.
|
|
1703
1726
|
|
1704
1727
|
==== :eager_limit_strategy
|
1705
1728
|
|
1706
|
-
This setting determines what strategy to use for loading the associations
|
1729
|
+
This setting determines what strategy to use for eager loading the associations
|
1707
1730
|
that use the :limit setting to limit the number of returned records. You
|
1708
1731
|
can't use LIMIT directly, since you want a limit for each group of
|
1709
1732
|
associated records, not a LIMIT on the total number of records returned
|
1710
1733
|
by the dataset.
|
1711
1734
|
|
1712
1735
|
By default, if a *_to_many association uses a limit or offset, or a
|
1713
|
-
|
1736
|
+
one_*_one association uses an offset or an order, Sequel will choose to use an
|
1714
1737
|
eager limit strategy. The default strategy depends on the database
|
1715
1738
|
being used. For databases which support window functions, a window
|
1716
1739
|
function will be used. Other databases will just have an ruby array
|
1717
1740
|
slice done on the entire record set.
|
1718
1741
|
|
1719
|
-
For
|
1720
|
-
because none is needed for a true
|
1742
|
+
For one_*_one associations without an offset or order, no strategy is used by default
|
1743
|
+
because none is needed for a true one-to-one association (since there
|
1721
1744
|
is only one associated record per current record). However, if you are
|
1722
1745
|
using a one_to_one association where the relationship is really one_to_many,
|
1723
1746
|
and using an order to pick the first matching row, then if you don't
|
1724
|
-
|
1747
|
+
use an limit strategy, you'll be loading all related
|
1725
1748
|
rows just to have Sequel ignore all rows after the first. By using a
|
1726
1749
|
strategy to change the query to only return one associated record per
|
1727
1750
|
current record, you can get much better database performance.
|
1728
1751
|
|
1729
1752
|
In general, Sequel picks an appropriate strategy, so it is not usually
|
1730
|
-
necessary to specify a specific strategy.
|
1731
|
-
associations where there is more than one associated record per current
|
1732
|
-
record. For those, you should probably specify true to this option to have
|
1733
|
-
Sequel pick an appropriate strategy.
|
1753
|
+
necessary to specify a specific strategy.
|
1734
1754
|
|
1735
|
-
You can
|
1736
|
-
|
1755
|
+
You can specify true for this option to have Sequel choose which strategy
|
1756
|
+
to use (this is the default). You can specify a symbol to manually choose
|
1757
|
+
a strategy. The available strategies are:
|
1737
1758
|
|
1738
1759
|
:distinct_on :: Uses DISTINCT ON to ensure only the first matching record
|
1739
|
-
is loaded (only used for
|
1760
|
+
is loaded (only used for one_*_one associations without
|
1740
1761
|
offsets on PostgreSQL).
|
1741
1762
|
:window_function :: Uses a ROW_NUMBER window functions to ensure the
|
1742
1763
|
correctly limited/offset records are returned.
|