简体   繁体   English

使用ruby和datamapper检索具有多对多关联的记录

[英]Retrieve records which have many-to-many association, using ruby and datamapper

I'm learning Sinatra, and I have read datamapper documentation and found this n to n relationship example: 我正在学习Sinatra,并且已经阅读了datamapper文档并发现了此n对n关系示例:

class Photo
    include DataMapper::Resource
    property :id, Serial
    has n, :taggings
    has n, :tags, :through => :taggings
end
class Tag
    include DataMapper::Resource
    property :id, Serial
    has n, :taggings
    has n, :photos, :through => :taggings
end
class Tagging
    include DataMapper::Resource
    belongs_to :tag,   :key => true
    belongs_to :photo, :key => true
end

What I understood from the code above is that one photo may have many or zero tags, and a tag may have many or zero photos. 从上面的代码中我了解到,一张照片可能有很多或零个标签,而一个标签可能有很多或零个照片。 How do I retrieve a list of photos with the tags associated to it already loaded. 如何检索带有已加载标签的照片列表。 I know datamapper uses the lazy approach, so it does not automatically loads the associated classes (in this case photo.tag). 我知道datamapper使用惰性方法,因此它不会自动加载关联的类(在本例中为photo.tag)。 So this: 所以这:

photos = Photo.all

would result in an array with Photo objects without the tags. 会导致包含带标签的Photo对象的数组。 Is there a way to automatically retrieve it or do I have to iterate over the array and set that manually? 有没有一种方法可以自动检索它,或者我必须遍历数组并手动进行设置?

Thanks in advance! 提前致谢!

I also have a database which has similar relations. 我也有一个具有相似关系的数据库。 Author , Post , Tag are main models, and Subscribedtag and Tagging are built through has n, :through . AuthorPostTag是主要模型, SubscribedtagTagging通过has n, :through构建。

class Author
    include DataMapper::Resource

    property :id,                     Serial
    property :email,          String, :unique => true
    property :password,       String
    property :first_name,     String
    property :last_name,          String
    property :bio,                    Text
    property :phone,          String, :unique => true
    property :twitter,        String, :unique => true
    property :facebook,       String, :unique => true
    property :show_phone,     Boolean, :default => false
    property :show_facebook,  Boolean, :default => false
    property :show_twitter,   Boolean, :default => false  
    property :is_admin,       Boolean, :default => false
    property :this_login,      DateTime
    property :last_login,      DateTime
    property :session_lasting, Integer, :default => 0

    has n, :posts
    has n, :subscribedtags
    has n, :tags, :through => :subscribedtags 
end

class Post
      include DataMapper::Resource

      property :id,           Serial
      property :title,        String, :required => true
      property :body,           Text,   :required => true
      property :is_blog_post, Boolean, :default => true
      property :viewed,             Integer, :default => 0
      property :featured,     Boolean, :default => false
      property :created_at,     DateTime
      property :updated_at,     DateTime

      belongs_to :author
      belongs_to :category
      has n, :comments
      has n, :taggings
      has n, :tags, :through => :taggings

      validates_length_of :title, :min => 3
      validates_length_of :body, :min => 20
      validates_format_of :title, :with => /\w/

      #some other methods 

end


class Tag
    include DataMapper::Resource

    property :id,               Serial
    property :name,             String, :unique => true

    has n, :taggings
    has n, :posts, :through => :taggings
    has n, :subscribedtags
    has n, :authors, :through => :subscribedtags

    validates_length_of :name, :min => 1
    validates_format_of :name, :with => /\w/

 # some other methods

end

class Tagging
    include DataMapper::Resource

    belongs_to :tag, :key => true
    belongs_to :post, :key => true
end

class Subscribedtag
  include DataMapper::Resource

  belongs_to :tag, :key => true
  belongs_to :author, :key => true
end

The way you've defined models, allows you to write queries, like that. 定义模型的方式允许您编写类似的查询。

2.2.0 :016 > kant = Tag.get(25) # getting tag instance with id 25 and assign it to variable named kant
 => #<Tag @id=25 @name="İmmanuil Kant"> 
2.2.0 :017 > kant.posts
 => #returns post instances which has this tag.
2.2.0 :018 > kant.posts.count # count of posts with this tag.
 => 2 
2.2.0 :021 > kant.posts.first.author.first_name
 => "Ziya" # check my author class and first_name attribute.

Let's say I want to retrieve the tag instances which has no posts. 假设我要检索没有帖子的标签实例。 a simple ruby command. 一个简单的ruby命令。

2.2.0 :024 > Tag.each {|tnp| puts tnp.name if tnp.posts.count == 0}
Latın
Python
Ruby
Sosializm
Hegel

Or retrieving tags based on posts. 或根据帖子检索标签。

2.2.0 :034 > p = Post.get(9)
 => #<Post @id=9 @title="Why we use Lorem Ipsum" @body=<not loaded> @is_blog_post=false @viewed=0 @featured=false @created_at=#<DateTime: 2015-08-02T23:14:04+05:00 ((2457237j,65644s,0n),+18000s,2299161j)> @updated_at=#<DateTime: 2015-08-02T23:14:04+05:00 ((2457237j,65644s,0n),+18000s,2299161j)> @author_id=1 @category_id=1> 
2.2.0 :035 > p.tags
 => [#<Tag @id=19 @name="Bundesliqa">] 

retrieve posts which has no tag. 检索没有标签的帖子。

2.2.0 :043 > Post.each {|pnt| puts pnt.id if pnt.tags.count.zero?}
8 #post with id has no tags
2.2.0 :044 > Post.get(8).tags.count
 => 0 

you can also query via other attributes. 您还可以通过其他属性进行查询。

2.2.0 :046 > Tag.first(:name => "Lorem").id
 => 30 

iterate over results 遍历结果

2.2.0 :050 > Tag.first(:name => "Lorem").posts.each {|lorempost| puts lorempost.title} # printing post titles which are tagged with lorem.
Get'em all
qwerty

I also associated authors with tags through Subscribedtags model, which I can easily check which author is subscribed to which tag, and vice versa. 我还通过Subscribedtags模型将作者与标签相关联,可以轻松地检查哪个作者订阅了哪个标签,反之亦然。

2.2.0 :055 > z = Author.get(1)
 => # returns details of author instance
2.2.0 :056 > z.tags

=> [#, #, #, #] => [#,#,#,#]

or querying via Subscribedtag 或通过Subscribedtag查询

2.2.0 :057 > z.subscribedtags
 => [#<Subscribedtag @tag_id=2 @author_id=1>, #<Subscribedtag @tag_id=4 @author_id=1>, #<Subscribedtag @tag_id=25 @author_id=1>, #<Subscribedtag @tag_id=30 @author_id=1>] 

you can also define your own functions to utilize querying. 您还可以定义自己的函数以利用查询。 I've defined a subscribed_tags method which returns an array of subscribed tags' names. 我定义了一个subscribed_tags方法,该方法返回一个已订阅标签名称的数组。

2.2.0 :058 > z.subscribed_tags
 => ["Həyat", "Məstan", "İmmanuil Kant", "Lorem"] 

If I want to retrieve the first_name attribute of a random author, who is subscribed to tag named "Lorem", 如果我要检索随机作者的first_name属性,该作者订阅了名为“ Lorem”的标签,

2.2.0 :062 > Tag.first(:name => "Lorem").authors.sample.first_name
 => "Ziya" 

As an answer to your 2nd question, yes, most times you have to iterate. 作为对第二个问题的回答,是的,大多数时候您必须进行迭代。

Because Photos.all return a collection of Photo object instances. 因为Photos.all返回Photo对象实例的集合。 And this instances individually has tag attributes, not the array consists of Photo instances. 而且此实例分别具有标签属性,而不是由Photo实例组成的数组。

if you call p = Photo.all; print p.tags; 如果您调用p = Photo.all; print p.tags; p = Photo.all; print p.tags; it will return all tags associated with all photos, which may or may not be the thing you want. 它将返回与所有照片关联的所有标签,这可能是您想要的东西,也可能不是。

Feel free to ask more questions, if these are not enough. 如果这些还不够,请随时提出更多问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM