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
-~----------~----~----~----~------~----~------~--~---

Reply via email to