I'm trying to do something similar (a directed graph). I have both a nodes table and a join table, nodenodes. Everything works fine but I would like to enforce a validates_unique on the join table that ensures that the ordered pair of (parent_id, child_id) doesn't get repeated:
DB.create_table :nodes do primary_key :id String :name end DB.create :node_nodes do primary_key :id foreign_key :parent_id, :nodes foreign_key :child_id, :nodes end class Node < Sequel::Model many_to_many :children, :join_table=>:node_nodes, :left_key=>:child_id, :right_key=>:parent_id, :class=>"Node" many_to_many :parents, :join_table=>:node_nodes, :left_key=>:parent_id, :right_key=>:child_id, :class=>"Node" end I could set the primary_key in node_nodes to be [:parent_id, :child_id] and that would certainly enforce uniqueness of the duplet. However, I'd prefer to have this done in a model. I tried adding: class NodeNode < Sequel::Model plugin :validation_helpers def validate validate_unique([:parent_id, :child_id]) end end but obviously no one is creating NodeNode instances so validate never gets called. Is there a proper way to accomplish this sort of thing? Ideally, what I'd really like is for add_parent and add_child to not do anything if the relationship already exists. I know there's a FindOrCreate module example that uses :extend but I'm not sure how it would work in this case. Thanks for the help. Charlton On Jun 4, 5:26 pm, Jeremy Evans <jeremyeva...@gmail.com> wrote: > On Jun 4, 12:09 pm, David Fisher <tib...@gmail.com> wrote: > > > I'm really lost for how to properly use models. I understand how to > > use Sequel for stuff like the short example on the front page (http:// > > sequel.rubyforge.org/), creating a table and dataset, inserting, > > finding and updating items on the dataset. Pretty much everything in > > the "Cheat Sheet" makes sense. Yet how to use the model, I'm lost and > > I can't seem to find documentation outside the RDoc explaining how it > > works. > > A model object is basically just the database row hash wrapped in an > object that has some behavior attached to it. A model class is > basically a dataset wrapped in an object that has some behavior > attached to it. > > > I'm actually trying to do something with Twitter, but I'll explain it > > in other terms in case people aren't hip to how Twitter works. I have > > a table called :people. Each person in the table is a row, and each > > person has a name, location and social security number (which is > > unique to them). Of course no man is an island, and each person can > > call any other number of people friends. However, just because someone > > calls you a friend, doesn't mean you automatically consider them a > > friend too (but they can keep calling you a friend all they want). > > > So each person has people that calls them a friend (one_to_many?), and > > people they call friends (many_to_one?). > > No, that's not how you should model it. > > > How in the world do you set this up? I know there's some stuff about > > join tables, but I don't know how that works. > > You should be using a join table, and with Sequel the association type > you are looking forward is calledmany_to_many. > > DB.create_table :friendships do > foreign_key :person_id, :people > foreign_key :called_friend_by, :people > end > > Person.many_to_many:called_friends, :left_key=>:called_friend_by, > :join_table=>:friendships > > Person.many_to_many:considered_by_friends, :right_key=>:called_friend_by, > :join_table=>:friendships > > This makes a person's called_friends association represent the other > people this person calls a friend. It makes a person's > considered_by_friends association represent the other people that > calls this person a friend. There are probably better names for the > associations, but we'll use these for the sake of this example. > > > How exactly do I create a new person then, and insert the data about > > them? How do tell the DB then that Joe is a friend of Bob? > > # Create New Person > jeremy = Person.create(:name=>'jeremy) > # Update data about them > jeremy.update(:age=>29) > > # Tell DB that Joe calls Bob a friend > joe = Person[:name=>'Joe'] > bob = Person[:name=>'Bob'] > joe.add_called_friend(bob) > > > I thought that it would be easy enough to have a table called > > "friendship" perhaps (with a model around it?), that uses a pair of > > user_id's (or SSN in my example above) as the primary_key? How do I do > > this syntaxtically? Is this the best way of doing it? How do I > > expresss that Joe calls Bob a friend, but Bob doesn't call Joe a > > friend? From Bob's perspective, how do I pull a list of everyone that > > calls Bob a friend, and a list of everyone that Bob calls a friend? If > > these are stored as user_ids, then how do I get a list of all the > > names that call each other friends? > > Well, I probably should have read your entire post first, but I was > responding to it as I was reading it. Anyway, the create_table > statement above shows how to do this. > > You express that one person calls another a friend without a > reciprocal agreement by having one of the columns in the table > represent the caller and one the callee. > > # Everyone who calls Bob a friend > bob.considered_by_friends > > # Everyone who Bob calls a friend > bob.called_friends > > Each of those will give you an Array of Person objects. To get the > names, you can do two things: > > # Get all information from the database, then strip out the name > bob.called_friends.map{|x| x.name} > > # Get just the names from the database, so it is faster. > bob.called_friends_dataset.select(:name).map(:name) > > > Sorry if this is excessively basic or that I'm asking for too verbose > > of an explaination. I'll probably need some code examples, because > > just telling me to set up amany_to_manyrelationship will still leave > > me confused. > > It is a bit basic, but we were all in your position at one time. > Hopefully the above code examples are enough. If not, please feel > free to ask more detailed questions. > > > Other things I don't understand: > > -I've seen that you're supposed to put your unique validation stuff in > > the model. I'm currently doing it in the database on creation of the > > table (ie. varchar :entry, unique => true). Do I still do it in the > > table on creation, or do I only do it in the model? Will I still need > > to rescue the errors raised in my code when I call an insert? > > You 100% want a database unique constraint/index. You may also want a > uniqueness validation, but the only purpose of that is to display a > nice error message to the user. > > Sequel's default is to raise exceptions if validations fail. You may > want to turn that off (Sequel::Model.raise_on_save_failure = false). > Sequel will always raise an exception if the database gives an error > when inserting a record due to a constraint violation. If you have a > validation, almost all the time the validation will catch the > uniqueness error, but you can't guarantee that (the validation may > pass and the database insert/update may fail due to race conditions). > > > Thank you SOOO MUCH in advance. I'm bashing my head and ripping my > > hair out here, and aside from looking at examples on Github, I can't > > find much for noobs like me :) > > Sequel is well documented, but the documentation is not exactly > beginner friendly. I realize this, but there's only so much I can do > about it. Since I'm not a beginner, I'm not particularly good at > guessing what documentation would be most helpful to beginners. There > are probably a lot of knowledge assumptions that I have internalized > that would be incorrect in respect to beginners, so I think that if I > just try to write beginner-friendly documentation, the result would > probably not be high quality. > > I'm very open to accepting documentation patches of all types, and I > do try to explain things here. > > Jeremy --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "sequel-talk" group. To post to this group, send email to sequel-talk@googlegroups.com To unsubscribe from this group, send email to sequel-talk+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sequel-talk?hl=en -~----------~----~----~----~------~----~------~--~---