Relacja wiele do wielu + dodatkowe pole w bazie

Witam.

W mojej aplikacji sa 2 modele polaczone relacja typu wiele-do-wielu: User oraz Tournament. Tabela w bazie ma 3 pola:
user_id, tournament_id, points
czyli przechowuje informacje o tym ze dany user bierze udzial w danym turnieju i ma w nim iles tam punktow.
Moj problem polega na tym: nie wiem w jaki sposob, przy dodawaniu do turnieju nowego uzytkownika ustawic jego punkty na pewnym bazowym poziomie powiedzmy 50pkt model tournament wyglada nastepujaco:

[code]class Tournament < ActiveRecord::Base
has_and_belongs_to_many :users

def addUser( userID )
self.users << User.find( userID )
end
end[/code]
no i nie wiem w jaki sposob dobrac sie do pola points w bazie.

W migracji możesz napisać:

add_column :tournaments_users, :points, :integer, :default => 50

W Twoim przypadku (kiedy oprócz relacji wiele-do-wielu chcesz przechowywać dodatkowe informacje - tutaj punkty) najlepiej jest zrobić z tej łączącej tabeli dodatkowy model, nazwijmy go Subscription, który będzie łączył User i Tournament (tzw. “rich model”). Efekt relacji wiele-do-wielu możesz uzyskać za pomocą has_many :through, np.

[code=“ruby”]class User
has_many :subscriptions
has_many :tournaments, :through => :subscriptions
end

class Tournament
has_many :subscriptions
has_many :users, :through => :subscriptions
end

class Subscription
belongs_to :user
belongs_to :tournament
end[/code]
Tabela subscriptions będzie miała 3 kolumny: user_id, tournament_id i points, możesz ją utworzyć za pomocą migracji tak jak napisał Drogus.

Z racji tego, że model Subscriptions ma dodatkową kolumnę :point, nie możesz używać wygodnego << przy tworzeniu asocjacji (czyli user << tournament, bo niby skąd Rails ma “wiedzieć” jaką wartość chcesz mieć w subscription.points). Tzn. możesz ale przeczytaj dokładne wyjaśnienie Susser’a (link poniżej)

Najlepiej faktyczne połączenie tworzyć tak:

[code=“ruby”]@user = User.find(1)
@tournament = Tournament.find(1)

@subscription = Subscription.new(:user => @user, :tournament => @tournament, :points => 10)[/code]
opcja :through jest dostępna od Rails 1.1

Więcej o has_many :through tutaj i tutaj i tutaj

Dzięki za linki, o to właśnie mi chodziło.

Witam po długiej przewie :slight_smile:

Ostanio, czyli po sesji, powróciłem do dręczenia railsów i własnie utknąłem na takim samym problenie co kolega powyżej.

Otóż, dając te same dane co wyżej, to chcę osiągnąć coś takiego: podczas edycji usera pojawia się lista wszytkich tournamentów do których jest zapisany + te, do których można się zapisać, a przy każdym liczba punktów.

Problem mam dokłądnie w tym miejscu, iż nie wiem, jak połączyć subskrybcje i tounamenty, tak by się nie dublowały - aktualnie robię tak, że mam @user.subscriptions i Tournament.find(:all, :select => “*, 0 as points”) i wypisuję jedno i drugie.

Drugi problem, to taki, że nie wiem, jak to ułozyć w formularzu, by się to ładnie chciało zapisywać… Kombinowałem, coś w rodzaju user[subscriptions][id] = points…

Witam Wszystkich

Mam problem z zapisaniem ‘w jedym strzale’ obiektu do bazy.


MODEL

Mam:
model Entry
model Category
model Categorization - łączący Entry i Category

class Entry < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations
end

class Category < ActiveRecord::Base
has_many :categorizations
has_many :entries, :through => :categorizations
end

class Categorization < ActiveRecord::Base
belongs_to :category
belongs_to :entry
end

W tabeli entries:
name :string
email :string
description :text

W tabeli categories:
name :string

(w tabeli kategoria sa wpisane na stale nazwy kategorii np:

category_id = 1, name=“grafika wektorowa”
category_id = 2, name=“grafika rastrowa”
category_id = 3, name=“zdjecia”
itp.

W tabeli categorizations mam:
category_id :integer
entry_id :integer


VIEW

W formularzu rhtml podajemy:
nazwę, email, opis i jest mozliwosc wyboru od 1 do kilku kategorii

hosiawak podaje tak:

  @user = User.find(1)
  @tournament = Tournament.find(1)
  @subscription = Subscription.new(:user => @user, :tournament => @tournament, :points => 10) 

ale to jest w momencie jak juz jest ‘znany’ User i Tournament

a ja chce przypisac kategorię (category) juz przy tworzeniu nowego wpisu (entry)

[b]czy jest jakis inny sposob niz ten:

  • tworze nowy wpis
  • zapisuje do bazy
  • pobieram z bazy ten wpis :expressionless:
  • dodaje kategorie
  • zapisuje do bazy
    ?
    [/b]
    oczywiscie nie musze korzystac z has_many :through, poniewaz nie przewiduje dodatkowych pol w tabeli categorizations - moge zastosowac has_and_belongs_to_many

Nie wiem czy dobrze zrozumiałem o co Ci chodzi (w tym momencie ciężką mam głowę ;-)). Ale chyba miałem podobny problem niedawno. Pomogło rozwiązanie z railscastów habtm checkboxes.

[code]W widoku:
<%= check_box_tag “entry[category_ids][]” jakas_wartosc %>

W kontolerze:
params[:entry][:category_ids] ||= []
Entry.create #czy co tam chcesz[/code]
Mam nadzieję, że zrozumiałem o co chodzi i pomogłem :wink:

Dzięki seban - działa