has_many i jeden lub więcej powiązanych rekordów

Mam dwa modele Link i Feed z relacją wiele do wielu połączone przez model LinkFeed.

Chciałbym aby każdy Link należał do jednego lub więcej Feedów.

W modelu link dałem walidację: validates :feeds, length: {minimum: 1}

Dwa testy do tego:

[code=ruby] it “raises error when trying to create link with no associated Feed record” do
expect {Link.create!(url: “http://google.com”)}.to raise_error
end

it “doesn’t raise error when trying to create link with associated Feed record” do
@feed = Feed.create(name: “Test Feed”)
expect{@feed.links.create!(url: “http://google.com”)}.to_not raise_error
end[/code]
Pierwszy przechodzi, drugi nie:

Failure/Error: expect{@feed.links.create!(url: "http://google.com")}.to_not raise_error expected no Exception, got #<ActiveRecord::RecordInvalid: Validation failed: Feeds is too short (minimum is 1 characters)> with backtrace: # ./spec/models/link_spec.rb:19:in `block (3 levels) in <top (required)>' # ./spec/models/link_spec.rb:19:in `block (2 levels) in <top (required)>' # ./spec/models/link_spec.rb:19:in `block (2 levels) in <top (required)>'
Będę wdzięczny za podpowiedź jak takie coś się realizuje.

Feeds is too short (minimum is 1 characters)

To podpowiada, że length waliduje długość stringów, nie tablic.

spróbuj czy zadziała

validate do errors.add(:base, "Must have at least one feed") unless feeds.size > 0 end
albo

validates_presence_of :feeds

Niestety oba nie działają :frowning:
W pierwszym dostaję taki błąd:

Failure/Error: expect{@feed.links.create!(url: "http://google.com")}.to_not raise_error expected no Exception, got #<ActiveRecord::RecordInvalid: Validation failed: Must have at least one feed> with backtrace: # ./spec/models/link_spec.rb:19:in `block (3 levels) in <top (required)>' # ./spec/models/link_spec.rb:19:in `block (2 levels) in <top (required)>'
W drugim:

Failure/Error: expect{@feed.links.create!(url: "http://google.com")}.to_not raise_error expected no Exception, got #<ActiveRecord::RecordInvalid: Validation failed: Feeds can't be blank> with backtrace: # ./spec/models/link_spec.rb:19:in `block (3 levels) in <top (required)>' # ./spec/models/link_spec.rb:19:in `block (2 levels) in <top (required)>'
Ciekawi mnie w którym momencie ten kod: @feed.links.create!(url: “http://google.com”) odpala walidacje (bo z tego wygląda, że najpierw odpala walidację, a potem dopiero tworzy join rekord).

A pokaż modele. Może nie sejwuje ci się LinkFeed.

Modele:

[code=ruby]class Link < ActiveRecord::Base
attr_accessible :url

validates :url, presence: true
validates_presence_of :feeds

has_many :link_feeds
has_many :feeds, through: :link_feeds
end[/code]

[code=ruby]class Feed < ActiveRecord::Base
attr_accessible :name

validates :name, presence: true

has_many :link_feeds
has_many :links, through: :link_feeds
end[/code]

[code=ruby]class LinkFeed < ActiveRecord::Base
attr_accessible :feed_id, :link_id

belongs_to :link
belongs_to :feed
end[/code]

Siedze nad tym cały dzień, na SO też nie dostałem niestety odpowiedzi :confused: Może jednak ktoś się zlituje i pomoże, bo to pewnie coś trywialnego.

W skrócie:
Link.create!(url:“google.com”) powinno rzucać błędem bo nie ma powiązanego jednego lub więcej Feed’ów.

@feed = Feed.create(name: ‘Test Feed’)
@feed.links.create!(url: “google.com”) powinno zakończyć się sukcesem bo link jest podpięty pod @feed.

Będę wdzięczny za pomoc.

pierwsza odpowiedź z tego tematu nie rozwiąże Twojego problemu?

Nie, tego już próbowałem w pierwszym poście :frowning:

ojej, jakie lewą ręką przez prawe ucho wymyśliłem :wink:

Zdaje się, że nie możesz sprawdzać obecności relacji przez presence gdy jest ona przez through. Jeżeli wywalisz validate_presence_of, to zacznie ci się wszystko sejwować, ale także sam link bez feeda.

A teraz lewa ręka: trzeba stworzyć link, potem feed, na nim stworzyć link_feed, a potem ten link_feed przypisać do linka, czyli zamiast

 @feed.links.create!(url: "google.com")

robić:

 link = Link.new(url: 'google.pl'); link.link_feeds << @feed.link_feeds.create; link.save

Czyli ogólnie jak robić taki link to, albo :

@feed.links.create!(url: "google.com")

albo :

link = Link.new(url: 'google.pl') @feed.links << link
I w tym przypadku walidacja jest troche bez sensu, bo i tak muszę mieć @feed zeby dodac do niego link.

Nie, musisz stworzyć feed_link najpierw, i przez niego łączyć link i feed

link.link_feeds << @feed.[u]link_feeds.create[/u]

A w kontrolerze cos a’la

[code]feed = Feed.create(params[:feed])
link = Link.new(params[:link]
link.feed_links << feed.feed_links.create

if link.save
redirect_to …itd[/code]

@feed.links.create!(url: "google.com")

bierze feeda i chce stworzyć i sejwnąć link przypisany do niego, ale nie tworzy po drodze link_feed. Ponieważ nie ma link_feed, link nie widzi feeda i nie przechodzi walidacji. Trzeba więc stworzyć z palca linkfeed i go przypisać do nowego linka.

Na pewno? Bo ładnie mi to działa.

1.9.3-p392 :001 > @feed = Feed.create(name: "New Feed") (0.1ms) begin transaction SQL (4.0ms) INSERT INTO "feeds" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Tue, 23 Apr 2013 12:05:58 UTC +00:00], ["name", "New Feed"], ["updated_at", Tue, 23 Apr 2013 12:05:58 UTC +00:00]] (150.8ms) commit transaction => #<Feed id: 10, name: "New Feed", created_at: "2013-04-23 12:05:58", updated_at: "2013-04-23 12:05:58"> 1.9.3-p392 :002 > link = Link.new(url: "http://onet.pl") => #<Link id: nil, url: "http://onet.pl", created_at: nil, updated_at: nil> 1.9.3-p392 :003 > @feed.links << link (0.1ms) begin transaction SQL (0.5ms) INSERT INTO "links" ("created_at", "updated_at", "url") VALUES (?, ?, ?) [["created_at", Tue, 23 Apr 2013 12:06:25 UTC +00:00], ["updated_at", Tue, 23 Apr 2013 12:06:25 UTC +00:00], ["url", "http://onet.pl"]] (142.7ms) commit transaction (0.1ms) begin transaction SQL (0.5ms) INSERT INTO "link_feeds" ("created_at", "feed_id", "link_id", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Tue, 23 Apr 2013 12:06:25 UTC +00:00], ["feed_id", 10], ["link_id", 10], ["updated_at", Tue, 23 Apr 2013 12:06:25 UTC +00:00]] (119.7ms) commit transaction Link Load (0.3ms) SELECT "links".* FROM "links" INNER JOIN "link_feeds" ON "links"."id" = "link_feeds"."link_id" WHERE "link_feeds"."feed_id" = 10 => [#<Link id: 10, url: "http://onet.pl", created_at: "2013-04-23 12:06:25", updated_at: "2013-04-23 12:06:25">]
W kontrolerze dałem:

[code] def create
@link = Link.new(params[:link])
@feed = Feed.find(params[:feed_id])

begin 
  @feed.links << @link
  redirect_to @feed, notice: 'Link was successfully created.'
rescue
  render action: "new"
end

end[/code]

No to ciekawostka, bo mi wywala Validation failed: Feeds can’t be blank