Zapamiętanie czy model jest valid

Witam, mam powiazane modele A has many B has many C has many D. Mam widok z listą modeli A gdzie wywoływane jest a.valid? czyli odpalane sa wszystkie validacje powiazanych modeli, a jest tego trochę. Jakie jest najlepsze rozwiązanie żeby tego nie robić, ponieważ na liście tylko czytam dane, nic nie modyfikuje. Pierwsze co przychodzi mi do głowy to dołożyć do tabeli A kolumne valid i tam zapisac ten stan, ale moze ktoś ma jakieś inne pomysły ? Jeśli normalnie w bazie, to gdzie wrzucic sprawdzanie tego stanu, do after_validation_on_update/create ?

Może to pomoże?

validates_associated :relacja, :on => :create

niestety w edycji moga zmieniać się parametry przypisanych już wcześniej modeli, tak że musze mieć bez : on

Chyba, źle Cię zrozumiałem… Z tego co wiem walidacja domyślnie odpalana jest na :save więc przy “czytaniu danych” jest pomijana. Skąd bierze Ci się wywołanie a.valid?.

Sam je wywołuje w szablonie, bo chce wyswietlic uzytkownikowi czy model i powiazane z nim modele sa poprawnie wypełnione.

<% unless foo.valid? %> costam jest nie tak, musisz poprawić <% end
ale jak wspomniałem to sprawdzenie wywołuje sporo zapytań, dlatego chciałbym to jakoś zapamiętać i tylko odczytywać flagę.

[quote=Artur79]Sam je wywołuje w szablonie, bo chce wyswietlic uzytkownikowi czy model i powiazane z nim modele sa poprawnie wypełnione.

<% unless foo.valid? %> costam jest nie tak, musisz poprawić <% end
ale jak wspomniałem to sprawdzenie wywołuje sporo zapytań, dlatego chciałbym to jakoś zapamiętać i tylko odczytywać flagę.[/quote]
Proszę popraw mnie jeśli nie mam racji… Czy istniejący obiekt kiedykolwiek może być wadliwy (zakładając, że użytkownik posługuje się wyłącznie Twoimi formularzami edycji/tworzenia obiektu)? Przecież walidacja przeprowadzana jest też podczas edycji czy tworzenia – błędny obiekt nie zostanie zachowany. Więc na liście prezentowane są wyłącznie spójne, przechodzące walidację dane.

[quote=kbl][quote=Artur79]Sam je wywołuje w szablonie, bo chce wyswietlic uzytkownikowi czy model i powiazane z nim modele sa poprawnie wypełnione.

<% unless foo.valid? %> costam jest nie tak, musisz poprawić <% end
ale jak wspomniałem to sprawdzenie wywołuje sporo zapytań, dlatego chciałbym to jakoś zapamiętać i tylko odczytywać flagę.[/quote]
Proszę popraw mnie jeśli nie mam racji… Czy istniejący obiekt kiedykolwiek może być wadliwy (zakładając, że użytkownik posługuje się wyłącznie Twoimi formularzami edycji/tworzenia obiektu)? Przecież walidacja przeprowadzana jest też podczas edycji czy tworzenia – błędny obiekt nie zostanie zachowany. Więc na liście prezentowane są wyłącznie spójne, przechodzące walidację dane.[/quote]
niekoniecznie:
możesz użyć czegoś takiego

  model.save(false)

i zapisujesz obiekt bez walidacji :wink:

Jeśli walidacja jest, tak jak w przykładzie klb validates_associated :relacja, :on => :create i dodatkowo mam validates_presence_of :relacja, :on => :update, to wtedy obiekt się zapisze ale nie będzie @object.valid? A mam potrzebę stosowania takich konstrukcji bo przykładowo, jest pytanie które ma obowiązkowo posiadać opcje a chciałbym najpierw dodać pytanie a w drugim etapie dodać do niego opcje. Dlatego szukam rozwiązania żeby nie sprawdzać tych rzeczy za każdym razem jak wchodzę na liste pytań, bo to obciąża mocno baze, tylko jakoś sobie zapamiętać ten stan, może jest jakis lepszy pomysł niż to co opisałem w pierwszym poście.

Jak widać to chyba jedyny pomysł :wink:

Zrób sobie tylko defaultowego scopa gdzie z selecta wypieprzysz tą kolumnę, co by nie ładować zbędnych danych za każdym razem.
Choć i tak wpływu na wydajność za dużego nie będzie.

możesz też zrobić relacje i tam trzymać status

[code]class Foo
has_one :status
end

class Status
belongs_to :foo
end[/code]
i wtedy nie masz żadnych niepotrzebnych danych, dopóki o to nie zapytasz, ale to chyba przerost formy nad treścią.

obawiam sie ze dodatkowa tabela to kolejny join, wiec chyba lepsza juz kolumna

@artur79 dobrze myślisz w kierunku zapisywania stanu/ów modelu.

W przypadku jednego modelu można pomyśleć o zapisaniu w sesji, a później dokładaniu to co dalej zwalidowane. Jednakże musiałbyś to powiązać to z np aasm, czyli też podzielić na walidowane grupy. Pomyśl tez o gemie validatable i zastosowanie grup.

Idąc dalej z tematem to wybrałbym zastosowanie Presenter.a http://jamesgolick.com/2008/7/27/introducing-activepresenter-the-presenter-library-you-already-know.html , gem na http://github.com/jamesgolick/active_presenter
Trochę inne podejście ale też dobre czyli Conductor.y http://blog.new-bamboo.co.uk/2007/8/31/presenters-conductors-on-rails

Dzięki tczubinski. Czytałem już kiedyś o tych wzorcach projektowych, ale nie mam za bardzo pomysłu jakie one mają odniesienie do opisanego przypadku. Mi nie chodzi o ładne metody w tym momencie, ale o ograniczenie ilości zapytań.

Raczej nie chciałem wskazać na wzorce projektowe. Osobiście w kilku projektach w ten sposób rozwiązałem problem przechowywania zwalidowanych obiektów. Dzięki zastosowaniu presenter.ów możesz stworzyć obiekty które możesz zwalidować, przeglądać, dodawać następne np. przy wielostronicowych formularzach i uzupełniać o następne dane i dopiero na końcu zapisać wszystko do bazy. Dzięki temu ograniczysz zapytania do bazy praktycznie do minimum. Po prostu trochę inne podejście.
Mam nadzieję, że dobrze zrozumiałem Twoją problematykę. Jeśli nie, to może bardziej opisz dlaczego musisz operować na “żywych” obiektach, które ciągle walidujesz pewnie przy przeładowaniach lub może przy przejściach między stronami lub formularzami.

Obiekty sa, tak jak opisane w pierwszym poście, w relacjach has_many. Poprostu ze specyfiki aplikacji wynika że użytkownik może dodać niezwalidowane cześciowo rekordy, a następnie może uzupełniać te dane, niekoniecznie odrazu. Nie ma tutaj żadnego wielostronicowego formularza itp. rzeczy. Są jak wspomniałem np pytania, pytanie powinno mieć opcje, ale użytkownik może dodać sobie pytanie, zapisać je, dodać kolejne pytanie i później wrócić do tego pierwszego i dodać do niego opcję, żeby było zwalidowane.

Przychodzą mi dwie rzeczy do głowy, które być może Ci pomogą:

a) dodaj kolumnę draft, która będzie oznaczać czy dany rekord jest w tej chwili w stanie draft, czyli może być nieprawidłowy. Wtedy nie odpalaj walidacji, innymi słowy validates .... :unless => Proc.new {|v| v.draft?}
b) Dodaj do obiektu kolumnę is_valid i przedefiniuj metodę valid tak by działała podobnie do tego poniżej. Myślę, że cel jest jasny.

def valid? returning(super()) do |valid| self.is_valid = valid end end

mysle że zdecyduje się na rozwiązanie b), jest tylko jeszcze jedna sprawa, czy da się jakoś zautomatyzować walidowanie w góre, tzn jesli zmieniam jakis obiekt to zeby walidowało jego rodzica i przez niego dziadka, ten dziadek ma kolumne is_valid i metode jak powyżej w punkcie b) i u niego musi zapisac ze cały obiekt nie jest walid. Czy poprostu musze tutaj recznie odpalic dziadek.valid? po kazdym zapisie rodzica albo dziecka

Daj to w after_save. Po każdym zapisaniu znajdź wszystkich przodków (miejmy nadzieje, że używasz czegoś w stylu awesome_nested_set albo innego sposobu bardziej optymalnego do odczytów niż samo parent_id) i dla każdego z nich wywołaj save, które odpali valid i na bazie zapisze zmienioną wartość.

Uzywam acts_as_list. Tak mniej więcej miałem zrobić, chciałem sie upewnić czy nie ma lepszych sposobów. Dzięki za porady.

Zastanawiałem sie tez nad odpaleniem validacji w osobnym procesie za pomoca np. spawn http://github.com/tra/spawn, bo Delayed Job to chyba zbyt duża kobyła na coś takiego. Walidacja nie zajmuje paru minut czy sekund ale czasem moze dojsc do sekundy przy rozbudowanym drzewie modeli.

I co w związku z tym, że może dojść do sekundy, że tak przewrotnie spytam ?
Czy to, że to tak długo trwa czasami nie jest aby oznaką złego projektu?
Czy to, że może to trwać sekundę ma jakieś znaczenie dla tego ile średnio to trwa (np 0.1 s) ?