I have a associative model, when I try to delete a record it returns true but the record still exists, The Model has 2 columns which are keys, while the third column is an enum, also a key.Any tips relating to updating/deleting a associative model in DataMapper
class A
#some property definition
end
class B
#some property definition
end
class C
include DataMapper::Resource
include DataMapper::Grape::Resource
expose :type
property :type, Enum[:a, :b], :key => true, :unique => false
belongs_to :a, :key => true
belongs_to :b, :key => true
end
Inside racksh
element = C.get(:a,1,1)
# => returns Hash Object
# Fine till now
element.destroyed?
# => true
# I dont know why did it return true, I am 100% sure I havent performed destroy or destroy! on element
element.type
# => :a
element.type = :b
# => DataMapper::ImmutableError: Immutable resource cannot be modified
element.destroy!
# => true
C.get(:a,1,1)
# => the same Hash Object rather than returning nil
Related
I'm learning Sinatra, and I have read datamapper documentation and found this n to n relationship example:
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). So this:
photos = Photo.all
would result in an array with Photo objects without the tags. 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.
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.
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.
2.2.0 :055 > z = Author.get(1)
=> # returns details of author instance
2.2.0 :056 > z.tags
=> [#, #, #, #]
or querying via 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.
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",
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. And this instances individually has tag attributes, not the array consists of Photo instances.
if you call 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.
I have the following mongoid model that inherits from the Entry model:
class Entry::Twitter < Entry
field :retweet_count, :type => Integer, :default => 0
field :retweeted, :type => Boolean, :default => false
field :favorited, :type => Boolean, :default => false
# in_reply_to_screen_name, in_reply_to_status_id_str, in_reply_to_user_id_str
field :reply, :type => Hash
field :from, :type => Hash # user: id_str, name, screen_name
field :time, :type => Time # created_at
field :data, :type => Hash # entities (hashtags and user_mentions)
field :assets, :type => Hash # urls from original entities
field :service, :type => String, :default => "twitter"
attr_accessible :assets
# validations
validates_presence_of :retweet_count, :from, :time, :data
# override set_service cause of https://github.com/sferik/twitter/issues/303
def set_service
self.service = "twitter"
end
end
When i try to reference it i get the following warning:
ruby-1.9.3-p125 :001 > Entry::Twitter
(irb):1: warning: toplevel constant Twitter referenced by Entry::Twitter
=> Twitter
Instead of referencing to my model it references to the Top Level Constant Twitter that is defined by a gem.
What can i do to fix this? I don't want to use another name for my class.
here is the solution:
https://github.com/rails/rails/issues/6931
I just added require_dependency 'entry/twitter' to every files that references Entry::Twitter to avoid this problem, and it works fine now.
I use mongo mapper (0.8.6) in my sinatra service. I have one problem with stack level too deep. The problem is that there is conflict of the key "changes" in my model. Here is my model:
class ChangeLog
include MongoMapper::Document
belongs_to :resource
key :changes, Hash, :required => true
key :message, String, :required => true
key :note, String
key :user_uuid, String, :required => true
key :user_name, String, :required => true
timestamps!
end
However, I don't want to rename my key as in this case, it's the right name for my web service. Any suggestions?
changes is an instance method that will tell you what fields have changed since you last saved the document. Here's an example from MongoMapper's documentation
user = User.create(:name => 'John', :age => 29)
puts user.changed? # false
puts user.changes.inspect # {}
user.name = 'Steve'
puts user.changed? # true
puts user.changes.inspect # {"name"=>["John", "Steve"]}
Unfortunately, you're probably going to need to choose a different name for that field. Maybe "adjustments" or "variations" or "differences" or "modifications"?
I have a DataMapper many to many relationship, friends, that needs to be kept in the
order. Whats the best way to maintain the order? I placed an order property
in the join model, but cannot seem to figure out a good way to update it.
My code:
class Person
include DataMapper::Resource
property :id, Serial
property :name , String, :required => true
has n, :friendships, :child_key => [ :source_id ]
has n, :friends, self, :through => :friendships, :via => :target
end
class Friendship
include DataMapper::Resource
property :source_id, Integer, :key => true, :min => 1
property :target_id, Integer, :key => true, :min => 1
property :order, Integer
belongs_to :source, 'Person', :key => true
belongs_to :target, 'Person', :key => true
end
a = Person.new
b = Person.new
c = Person.new
a.friends = [b, c] # Keep in this order
a.friends == [b, c] # This should be true
a.friends != [c, b]
a.save
Saving person a should create a friendship between a and b with order value = 1
as well as a second friendship between a and c with order value = 2.
I've been having trouble setting the order values. From what I can tell, the friendships don't actually get created until a is saved, so I can't update them before saving. And I can't updated them after saving because the order is lost.
Any ideas?
You could do this:
Friendship.create(:source => a, :target=> b)
Friendship.create(:source => a, :target=> c)
a.reload
a.friends == [b,c]
This should give you the desired effect, assuming that the order column in your database backend auto increments. If it doesn't then you need to add something like this to your Friendship model:
before :create do
self.order = Friendship.first(:order=>[:order.desc]).order + 1 rescue 0
end
If you care about the order of these friendships then you also need to do this in the Person model:
has n, :friendships, :child_key => [ :source_id ], :order => [:order.asc]
When I define the User has_many meetings, it automatically creates a "user_id"
key/value pair to relate to the User collections. Except I can't run any
mongo_mapper finds using this value, without it returning nil or [].
Meeting.first(:user_id => "1234")
Meeting.all(:user_id => "1234")
Meeting.find(:user_id => "1234")
All return nil. Is there another syntax? Basically I can't run a query on the automatically generated associative ObjectId.
# Methods
class User
include MongoMapper::Document
key :user_name, String, :required => true
key :password, String
many :meetings
end
class Meeting
include MongoMapper::Document
key :name, String, :required => true
key :count, Integer, :default => 1
end
# Sinatra
get '/add' do
user = User.new
user.meetings "foobar") #should read: Meeting.new(:name => "foobar")
user.save
end
get '/find' do
test = Meeting.first(:user_id => "4b4f9d6d348f82370b000001") #this is the _id of the newly create user
p test # WTF! returns []
end
As Jimmy mentioned about checking Meeting.all, I don't think you would have anything.
Based on your example above I see a couple potential issues.
- Your User requires a :user_name so it's not getting saved
- would never get saved, because you didn't set the name which is required
- Your Meeting isn't getting saved either
- One more thing, you need to concat your meeting to user.meetings
This works with mongo_mapper 0.6.10
require 'rubygems'
require 'mongo_mapper'
MongoMapper.database = "meetings"
class User
include MongoMapper::Document
key :user_name, String, :required => true
key :password, String
many :meetings
end
class Meeting
include MongoMapper::Document
key :name, String, :required => true
key :count, Integer, :default => 1
end
user = User.create(:user_name => "Rubyist")
user.meetings << Meeting.create(:name => "foobar")
user.save
Meeting.first(:user_id => user.id)
User.find(user.id).meetings
You may have figured this out already, but I hope this is helpful anyway.
You can try using
Meeting.find_by_user_id "1234"
Also, if you run script/console then does Meeting.all show each record as having a user_id assigned to it?
What about just User.find("1234").meetings ?