Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Question about many to many association with additional columns
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  5 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Jesús Gabriel y Galán  
View profile  
 More options Nov 21 2011, 11:44 am
From: Jesús Gabriel y Galán <jgabrielyga...@gmail.com>
Date: Mon, 21 Nov 2011 17:44:27 +0100
Local: Mon, Nov 21 2011 11:44 am
Subject: Question about many to many association with additional columns
Hi,

I have a many to many association in which the join table has
additional columns. I'm modeling a deck management application for
Magic the Gathering, and I have the following tables:

DB.create_table(:mtg_database) do
        Integer :gatherer_id
        primary_key [:gatherer_id]
        String :name
        String :cost
        String :spell_type
        String :power_toughness
        String :rules
        String :sets
end

DB.create_table(:deck) do
        primary_key :id
        String :name
        String :format
        String :description
end

DB.create_table(:deck_card) do
        foreign_key :deck_id, :deck
        foreign_key :gatherer_id, :mtg_database
        primary_key [:deck_id, :gatherer_id]
        Integer :qty
end

The deck_card table contains the relation between a deck and the cards
it contains. For each deck and card, it has an additional column named
qty which contains the number of such card a deck has. What I would
like to do is have a method in a deck model that allows me to add a
card to a deck, which will handle the qty field correctly (set it to 1
the first time a card is added to a deck, increment it from that point
on). I have managed to make it work, checking an old message from this
list related to books and locations, as so:

class Card < Sequel::Model(:mtg_database)
    unrestrict_primary_key
    one_to_many :deck_card
end

class DeckCard < Sequel::Model(:deck_card)
    many_to_one :card, :key => :gatherer_id
    many_to_one :deck
end

class Deck < Sequel::Model(:deck)
    many_to_many :cards, :join_table=>:deck_card, :right_key =>
:gatherer_id, :order=> :name
    one_to_many :deck_card

    def deck_card(card)
      deck_card_dataset[:gatherer_id => card.gatherer_id]
    end

    def add_card_to_deck card
        deck_card = deck_card card
        if deck_card
            deck_card.update({:qty => deck_card.qty + 1})
        else
            DeckCard.create({:card => card, :deck => self, :qty => 1})
        end
    end
end

This works and I'm able to add cards to decks like this:

deck = Deck.first(:id => some_id)
deck.add_card_to_deck(Card.first(:gatherer_id => some_card_id))

But I have some questions:

1.- Is this the best way to handle this?
2.- I would now need a way to iterate through all the deck's cards
having access to both the card info (all columns in mtg_database
table) along with the qty column from the join table. What would be
the best way to do that?
3.- I had to add the unrestrict_primary_key in the Card model. Until I
did, I wasn't able to create cards setting the gatherer_id, which is
an external id that I want to have as pk of that table. Am I doing
something wrong? I'm creating Cards like this:

    def update_or_create_card params
        card = Card.first :gatherer_id => params[:gatherer_id]
        card = Card.new unless card
        card.gatherer_id = params[:gatherer_id]
        card.name = params[:name]
        card.cost = params[:cost]
        card.spell_type = params[:type]
        card.power_toughness = params[:power_toughness]
        card.rules = params[:rules]
        card.sets = params[:sets]
        card.save
        card
    end

Regards,

Jesus.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Jesús Gabriel y Galán  
View profile  
 More options Nov 22 2011, 4:16 am
From: Jesús Gabriel y Galán <jgabrielyga...@gmail.com>
Date: Tue, 22 Nov 2011 10:16:15 +0100
Local: Tues, Nov 22 2011 4:16 am
Subject: Re: Question about many to many association with additional columns
Sorry if this is a double post. I sent it right after subscribing to
the group yesterday, and I haven't received it or see it in
groups.google.com.

Hi,

I have a many to many association in which the join table has
additional columns. I'm modeling a deck management application for
Magic the Gathering, and I have the following tables:

DB.create_table(:mtg_database) do
       Integer :gatherer_id
       primary_key [:gatherer_id]
       String :name
       String :cost
       String :spell_type
       String :power_toughness
       String :rules
       String :sets
end

DB.create_table(:deck) do
       primary_key :id
       String :name
       String :format
       String :description
end

DB.create_table(:deck_card) do
       foreign_key :deck_id, :deck
       foreign_key :gatherer_id, :mtg_database
       primary_key [:deck_id, :gatherer_id]
       Integer :qty
end

The deck_card table contains the relation between a deck and the cards
it contains. For each deck and card, it has an additional column named
qty which contains the number of such card a deck has. What I would
like to do is have a method in a deck model that allows me to add a
card to a deck, which will handle the qty field correctly (set it to 1
the first time a card is added to a deck, increment it from that point
on). I have managed to make it work, checking an old message from this
list related to books and locations, as so:

class Card < Sequel::Model(:mtg_database)
   unrestrict_primary_key
   one_to_many :deck_card
end

class DeckCard < Sequel::Model(:deck_card)
   many_to_one :card, :key => :gatherer_id
   many_to_one :deck
end

class Deck < Sequel::Model(:deck)
   many_to_many :cards, :join_table=>:deck_card, :right_key =>
:gatherer_id, :order=> :name
   one_to_many :deck_card
   def deck_card(card)
     deck_card_dataset[:gatherer_id => card.gatherer_id]
   end
   def add_card_to_deck card
       deck_card = deck_card card
       if deck_card
           deck_card.update({:qty => deck_card.qty + 1})
       else
           DeckCard.create({:card => card, :deck => self, :qty => 1})
       end
   end
end

This works and I'm able to add cards to decks like this:

deck = Deck.first(:id => some_id)
deck.add_card_to_deck(Card.first(:gatherer_id => some_card_id))

But I have some questions:
1.- Is this the best way to handle this?
2.- I would now need a way to iterate through all the deck's cards
having access to both the card info (all columns in mtg_database
table) along with the qty column from the join table. What would be
the best way to do that?
3.- I had to add the unrestrict_primary_key in the Card model. Until I
did, I wasn't able to create cards setting the gatherer_id, which is
an external id that I want to have as pk of that table. Am I doing
something wrong? I'm creating Cards like this:

   def update_or_create_card params
       card = Card.first :gatherer_id => params[:gatherer_id]
       card = Card.new unless card
       card.gatherer_id = params[:gatherer_id]
       card.name = params[:name]
       card.cost = params[:cost]
       card.spell_type = params[:type]
       card.power_toughness = params[:power_toughness]
       card.rules = params[:rules]
       card.sets = params[:sets]
       card.save
       card
   end

Regards,

Jesus.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Jeremy Evans  
View profile  
 More options Nov 23 2011, 9:08 pm
From: Jeremy Evans <jeremyeva...@gmail.com>
Date: Wed, 23 Nov 2011 18:08:16 -0800 (PST)
Local: Wed, Nov 23 2011 9:08 pm
Subject: Re: Question about many to many association with additional columns
On Nov 21, 8:44 am, Jesús Gabriel y Galán <jgabrielyga...@gmail.com>
wrote:

> Hi,

> I have a many to many association in which the join table has
> additional columns. I'm modeling a deck management application for
> Magic the Gathering, and I have the following tables:

> DB.create_table(:mtg_database) do
>         Integer :gatherer_id
>         primary_key [:gatherer_id]

You can probably just use "primary key :gatherer_id" here

>         String :name
>         String :cost
>         String :spell_type
>         String :power_toughness
>         String :rules
>         String :sets

If any of these shouldn't be NULL, you might want to set :null=>false

> DB.create_table(:deck_card) do
>         foreign_key :deck_id, :deck
>         foreign_key :gatherer_id, :mtg_database
>         primary_key [:deck_id, :gatherer_id]
>         Integer :qty

I think :default=>1 would make sense for qty.

It looks fine if you aren't worried about concurrent access.  For
concurrent access, there's a race condition when you are updating.
The correct way to handle concurrent access would be to do:

  deck_card_dataset.filter(:gatherer_id =>
card.gatherer_id).update(:qty=>:qty+1)

> 2.- I would now need a way to iterate through all the deck's cards
> having access to both the card info (all columns in mtg_database
> table) along with the qty column from the join table. What would be
> the best way to do that?

In your many_to_many :cards association, add
a :select=>[:mtg_database.*, :deck_card__qty].  Then you can just do
deck.cards, and each entry in the array would have a :qty entry in the
values hash for the number of that card in the deck.

> 3.- I had to add the unrestrict_primary_key in the Card model. Until I
> did, I wasn't able to create cards setting the gatherer_id, which is
> an external id that I want to have as pk of that table. Am I doing
> something wrong? I'm creating Cards like this:

It should only affect mass assignment (which create uses).  There's
nothing wrong with it, though you have to be more careful with user
input if you unrestrict access to the primary key.

Jeremy


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Jesús Gabriel y Galán  
View profile  
 More options Nov 24 2011, 4:50 am
From: Jesús Gabriel y Galán <jgabrielyga...@gmail.com>
Date: Thu, 24 Nov 2011 10:50:57 +0100
Local: Thurs, Nov 24 2011 4:50 am
Subject: Re: Question about many to many association with additional columns

On Thu, Nov 24, 2011 at 3:08 AM, Jeremy Evans <jeremyeva...@gmail.com> wrote:
> On Nov 21, 8:44 am, Jesús Gabriel y Galán <jgabrielyga...@gmail.com>
> wrote:
>> Hi,

>> I have a many to many association in which the join table has
>> additional columns. I'm modeling a deck management application for
>> Magic the Gathering, and I have the following tables:

>> DB.create_table(:mtg_database) do
>>         Integer :gatherer_id
>>         primary_key [:gatherer_id]

> You can probably just use "primary key :gatherer_id" here

I understood this would create an autoincrementing column, whereas I
want to set the id myself, because it comes from an external system.

>>         String :name
>>         String :cost
>>         String :spell_type
>>         String :power_toughness
>>         String :rules
>>         String :sets

> If any of these shouldn't be NULL, you might want to set :null=>false

Thanks for the tip. I guess name and spell type shouldn't be null.

>> DB.create_table(:deck_card) do
>>         foreign_key :deck_id, :deck
>>         foreign_key :gatherer_id, :mtg_database
>>         primary_key [:deck_id, :gatherer_id]
>>         Integer :qty

> I think :default=>1 would make sense for qty.

True.

OK, you mean to do this in the else part of the check right?
I mean I need to do different things for the first insertion and the
subsequent ones,
and I understand this statement needs that the row in the deck_card
table already exists, correct?
If so, wouldn't there still be a race condition?

def add_card_to_deck card
  deck_card = deck_card card
  if deck_card
    deck_card_dataset.filter(:gatherer_id =>
card.gatherer_id).update(:qty=>:qty+1)
  else
    DeckCard.create({:card => card, :deck => self, :qty => 1})
  end
end

Or am I misunderstanding and you meant a different thing?

>> 2.- I would now need a way to iterate through all the deck's cards
>> having access to both the card info (all columns in mtg_database
>> table) along with the qty column from the join table. What would be
>> the best way to do that?

> In your many_to_many :cards association, add
> a :select=>[:mtg_database.*, :deck_card__qty].  Then you can just do
> deck.cards, and each entry in the array would have a :qty entry in the
> values hash for the number of that card in the deck.

Great, thanks, I'll try this.

>> 3.- I had to add the unrestrict_primary_key in the Card model. Until I
>> did, I wasn't able to create cards setting the gatherer_id, which is
>> an external id that I want to have as pk of that table. Am I doing
>> something wrong? I'm creating Cards like this:

> It should only affect mass assignment (which create uses).  There's
> nothing wrong with it, though you have to be more careful with user
> input if you unrestrict access to the primary key.

OK, but my question is why is that needed when the PK is not an
autoincrementing id?
If I need to build a model in which I need to setup the id myself when
creating, as is my case, what should I do?
Is this the recommended way? Until I add that line I was getting
errors when I tried to set the gatherer_id in a Card object.

Thanks a lot of for the help,

Jesus.


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Jeremy Evans  
View profile  
 More options Nov 24 2011, 3:20 pm
From: Jeremy Evans <jeremyeva...@gmail.com>
Date: Thu, 24 Nov 2011 12:20:12 -0800 (PST)
Local: Thurs, Nov 24 2011 3:20 pm
Subject: Re: Question about many to many association with additional columns
On Nov 24, 1:50 am, Jesús Gabriel y Galán <jgabrielyga...@gmail.com>
wrote:

Ah, I didn't appreciate that distinction.  You could do:

  Integer :gatherer_id, :primary_key=>true

> >  deck_card_dataset.filter(:gatherer_id =>
> > card.gatherer_id).update(:qty=>:qty+1)

> OK, you mean to do this in the else part of the check right?

No, in the if part of the check.  The else part (which does the
create), is fine, though you could remove the :qty=>1 if you make that
the default.

> I mean I need to do different things for the first insertion and the
> subsequent ones,
> and I understand this statement needs that the row in the deck_card
> table already exists, correct?

Yes, but you are still checking for existance in the if statement.

> If so, wouldn't there still be a race condition?

Yes, there is in the sense that if two processes/threads attempt to
create at the same time.  However, the primary key constraint in the
deck_card table will prevent that, so an error would be raised by one
of the processes/threads in that case.

> def add_card_to_deck card
>   deck_card = deck_card card
>   if deck_card
>     deck_card_dataset.filter(:gatherer_id =>
> card.gatherer_id).update(:qty=>:qty+1)
>   else
>     DeckCard.create({:card => card, :deck => self, :qty => 1})
>   end
> end

> Or am I misunderstanding and you meant a different thing?

That's basically right.

> >> 3.- I had to add the unrestrict_primary_key in the Card model. Until I
> >> did, I wasn't able to create cards setting the gatherer_id, which is
> >> an external id that I want to have as pk of that table. Am I doing
> >> something wrong? I'm creating Cards like this:

> > It should only affect mass assignment (which create uses).  There's
> > nothing wrong with it, though you have to be more careful with user
> > input if you unrestrict access to the primary key.

> OK, but my question is why is that needed when the PK is not an
> autoincrementing id?

If the primary key is not autoincrementing, then you are more likely
to want unrestricted access to it.  However, Sequel defaults to
restricted access for all primary keys, not just those that are
autoincrementing.

> If I need to build a model in which I need to setup the id myself when
> creating, as is my case, what should I do?

Using unrestrict_primary_key is fine.  Just be careful with user
input.

> Is this the recommended way? Until I add that line I was getting
> errors when I tried to set the gatherer_id in a Card object.

There's no problem with enabling unrestricted access.  The alternative
in your case would be to call the setter method manually:

  Model.create(hash){|m| m.primary_key = ...}

Jeremy


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »