Wiele odwołań do CACHE w logu i prośba o wyjaśnienie

Cześć

Zaczynam przygodę z railsami (v3) chyba standardowo od przykładów blogowych.
Kwestia następująca: Jest artykuł który ma wiele kategorii.

[code]class Article < ActiveRecord::Base
has_and_belongs_to_many :categories

end

class Category < ActiveRecord::Base
has_and_belongs_to_many :articles

end[/code]
Dla akcji show zdefiniowanej jako

def edit @article = Article.find(params[:id]) end
w widoku mam

<% for category in Category.all %> <%= check_box_tag 'article[category_ids][]', category.id, @article.category_ids.include?(category.id), :id => dom_id(category) %> <%= label_tag dom_id(category), category.name, :class => "check_box_label" %> <% end %>
To powoduje wpisy w logu

Started GET "/articles/2/edit" for 127.0.0.1 at 2011-02-04 21:10:21 +0100 Processing by ArticlesController#edit as HTML Parameters: {"id"=>"2"} Article Load (0.0ms) SELECT "articles".* FROM "articles" WHERE ("articles"."id" = 2) LIMIT 1 Category Load (0.0ms) SELECT "categories".* FROM "categories" ORDER BY categories.name Category Load (0.0ms) SELECT "categories".id FROM "categories" INNER JOIN "articles_categories" ON "categories".id = "articles_categories".category_id WHERE ("articles_categories".article_id = 2 ) ORDER BY categories.name CACHE (0.0ms) SELECT "categories".id FROM "categories" INNER JOIN "articles_categories" ON "categories".id = "articles_categories".category_id WHERE ("articles_categories".article_id = 2 ) ORDER BY categories.name CACHE (0.0ms) SELECT "categories".id FROM "categories" INNER JOIN "articles_categories" ON "categories".id = "articles_categories".category_id WHERE ("articles_categories".article_id = 2 ) ORDER BY categories.name CACHE (0.0ms) SELECT "categories".id FROM "categories" INNER JOIN "articles_categories" ON "categories".id = "articles_categories".category_id WHERE ("articles_categories".article_id = 2 ) ORDER BY categories.name CACHE (0.0ms) SELECT "categories".id FROM "categories" INNER JOIN "articles_categories" ON "categories".id = "articles_categories".category_id WHERE ("articles_categories".article_id = 2 ) ORDER BY categories.name
Jeśli natomiast zrobię tak:

def edit @article = Article.find(params[:id]) @categories = Category.all @article_categories = @article.category_ids end
To log wygląda sensowniej

Started GET "/articles/2/edit" for 127.0.0.1 at 2011-02-04 21:12:54 +0100 Processing by ArticlesController#edit as HTML Parameters: {"id"=>"2"} Article Load (0.0ms) SELECT "articles".* FROM "articles" WHERE ("articles"."id" = 2) LIMIT 1 Category Load (0.0ms) SELECT "categories".* FROM "categories" ORDER BY categories.name Category Load (0.0ms) SELECT "categories".id FROM "categories" INNER JOIN "articles_categories" ON "categories".id = "articles_categories".category_id WHERE ("articles_categories".article_id = 2 ) ORDER BY categories.name
Pytanie od zielonego, dlaczego tak? Jak rozwiązać to inaczej niż poprzez ustawianie zmiennych w kontrolerze (dla bardziej rozbudowanych obiektów będize to mało fajne). Dlaczego w wersji pierwszej wygląda jakby ten JOIN był ciągnięty kilka razy. Co ciekawe wpisów CACHE w logu jest dokładnie o 1 mniej niż wszystkich kategorii w bazie.

Dobra, trochę ogarnąłem.
Zapytanie jest dlatego, że przy każdej iteracji po kategoriach pobieram kategorie artykulu i sprawdzam czy dana jest wsrod nich. Za 1 razem bierze z bazy a później cacheuje, dlatego jest n-1 logów CACHE. Pytanie, czy da się to inaczej zrobić, żeby minąć to cacheowanie? Bo przy dużej liczbie elementów w relacji to będize rzygało do loga jak szalone.

I jaki patent jest lepszy

@article.categories.all.include?(category)

czy

@article.category_ids.all.include?(category.id)

@article.categories.include?(category)
Powinien tylko 1 query wywołać, bo @article.categories będzie zcachowane w tablicy.

@article.categories.all.include?(category) za każdym razem wywoła query.

Gdybyś chciał sobie oszczędzić czasu mógłbyś użyć formtastic albo simple_form

f.input :categories, :as => :check_boxes
na widoku by załatwiło sprawę

aczkolwiek dobrze jako początkujący nauczyć się najpierw bez formtastic’a żyć.

Dzięki za zainteresowanie tematem.
Jest tak jak mówisz, dane są brane z cache. Z tym że chciałem uniknąć tego, żeby tak brzydko nie rzygał do loga. Udało mi się to tylko przez ustawienie categories i article_categories w kontrolerze.

Teraz mam kolejny problem. Tym razem przy którejś z kolei modyfikacji Article (dokładnie przy różnych zmianach kategorii).

ActiveRecord::AssociationTypeMismatch in ArticlesController#update Category(#13434360) expected, got Category(#12977376)
Model ten sam co wyżej. Działa za pierwszym razem, czasami też za drugim a później rzyga takim czymś.
Gdyby ktoś chciał spróbować to http://github.com/downloads/ccjr/blog/chapter08.zip są to źródła z “Beginning Rails 3” i dzieje się dokładnie to samo.
Googlałem ale nic nie wymyśliłem. Nested_attributes nie działają dla habtm.

Musisz zrozumieć kiedy danę z brane z cache Railsów bez wywoływania zapytania a kiedy są brane z cache bazy danych bo niedawno było takie samo query.
To pierwsze nie generuje wpisu do loga i jest szybkie a to drugie generuje.

wersja @article.categories.include?(category) nie powinna “rzygać do loga”. (1 query powinno iść dla @article.categories i jedno dla Category.all).

Natomiast:
Category(#13434360) expected, got Category(#12977376)
Ten problem występuje np gdy w konsoli zrobisz reload! i będziesz próbował użyć kategori po reload! na obiekcie który był utworzony przed reload!.

Kiedy jest brane z cache to wiem, zresztą widać w logach CACHE, chodzi bardziej o to, żeby wyelimnować próby sięgania do cache tak jak to robię przy ustawianiu zmiennych w kontrolerze. Pewnie to krzywe rozwiązanie ale jak widzę w logach powtarzający n razy po sobie się wiersz CACHE to jakoś tak się dziwnie czuję :slight_smile:

@article.categories.include?(category)

właśnie wyrzuca do loga pierwszy load prawdziwy i później cache.

Co do błędu

Category(#13434360) expected, got Category(#12977376)

to domyślam się czym on jest spowodowany i przy używaniu konsoli nie zdizwiłoby mnie to pewnie, ale to się dzieje normalnie przy requestach z przeglądarki. Póki co nie wiem jak to rozwiązać. Będę wdzięczny za wszelką pomoc.

Pobrałem te źródła i tam w Gemfile jest beta Railsów 3. Może spróbuj użyć 3.0.3 wersji to będzie ok i tak jak mówię ?

Ja tylko chciałem wspomnieć, że has_and_belongs_to_many jest deprecated i wreszcie pewnie kiedyś wyleci z railsów.

Drogus: a znasz może powód takiej decyzji?

Bo to jeden wielki hax ? Pozatym nieekonomiczna nazwa :smiley:

czyli has_many :trough zamiast tego zalecane ?

Nie, to co innego. Zamiast has_and_belongs_to_many tworzysz dodatkowy model łączący z jakąś sensowną nazwą (np. dla związku User <-> User może to być Friendship, dla User <-> Role np. Relationship itp). Ten dodatkowy model ma tę zaletę, że pozwala na przechowywanie dodatkowych informacji, np. kiedy dana relacja (przyjaźni, uprawnienia, członkostwa) została utworzona, przez kogo, jaki ma status itp. Przy has_and_belongs_to_many nie masz takiej możliwości.

Nie, to co innego. (…)[/quote]
Artur79 chyba miał to samo na myśli, zgodnie z tym co napisałeś może być np. User has_many Roles through Relationship

Radarkowi chyba chodziło o to że to co innego pod względem zastosowań (jest ich więcej). Cykl co autor miał na myśli ? :wink: