Duży hobbystyczny projekt

walidacje mam w modelu comment.rb

class Comment < ActiveRecord::Base
attr_accessible :content, :nick

validates :nick, :content, :presence => true
validates :nick, :content, :length => {:minimum => 2}
validates :content, :uniqueness => true
end

a w comments/index.html.erb mam

<% if @comment.errors.any? %>

Popraw



    <% @comment.errors.full_messages.each do |comment| %>
  • <%= comment %>

  • <% end %>

<% else %>

nie ma bledu


<% end %>

I jak zamiast redirect_to użyję render “show”, i tam wstawię kod z comments/index.html.erb, to działa i przekazuje wyswietla mi co jest nie tak, natomiast przy powrocie redirect_to “/” nie wyświetla mi błędu, lecz wyświetla

nie ma bledu

Walidację tak, ale wywołałeś metodę ‘save’ i nie sprawdziłeś, czy się powiodła. To o taką kontrolę mi chodziło.

W dawnych czasach ludzie w C pisali jakoś tak:

if( rc = funkcja_1() ) { if( rc = funkcja_2()) { ... } else { handle_error(rc); } } else { handle_error(rc); }
Bolesne, ale od razu widać było paranoika :slight_smile:

A właśnie - @comment.errors.any masz w widoku akcji index. Ale ten widok jest renderowany na obiektach dopiero co wyciągniętych z bazy. Zauważ, że redirect każe przeglądarce poprosić Twój serwer o nową stronę. Serwer nie pamięta, że przed chwilą w jakiejś innej akcji jakiś obiekt się nie zapisał. Po skończeniu wykonywania akcji w kontrolerze, wszystkie zmienne (w uproszczeniu) idą w niebyt. Jeśli chcesz coś zachować do następnej akcji użyj sesji, ciasteczka, albo parametrów przekazanych przeglądarce.

Walidacja Ci nie powie. Ona sprawia właśnie to, że obiekt się nie zapisze. Jeżeli reguły walidacji wykryły błąd, wtedy metoda save zwraca false. Jeżeli błędu nie było, metoda save zwraca true.

Metoda save automatycznie wywołuje metodę valid?. Wywołanie tej metody valid? odpala reguły walidacji i tworzy wpisy w tablicy @model.errors. Dlatego dopóki nie wywołasz na modelu save albo valid? błędów nie będzie.

Teraz o przekierowaniu: jeśli wracasz do poprzedniej strony, to obiekt, króry miał te błędy już nie istnieje. Pamiętaj, że to jest dialog przeglądardki z serwerem, a serwer nigdy nie pamięta co było przed chwilą. On zawsze musi mieć wszystko podane od nowa - przez ciasteczko albo przez parametry. W zasadzie, ponieważ proces programu w Railsach działa bez przerwy, mógłbyś użyć zmiennych globalnych (albo zmiennych instancji w klasach, czy coś innego), ale takie rozwiązania są złe.

Jeżeli zdecydowanie nie chcesz renderować strony z błędem w akcji create (choć kompletnie nie rozumiem dlaczego ludzie mają przed tym opory) to musiałbyś robić przekierowanie przekazując jakoś albo błędny obiekt, albo komunikaty błędów.

Na przykład tak:

redirect_to "/comments/#{@comment.id}?errors=Cośtam jest puste\nCoś innego jest błędne"

Generalnie - dużo zachodu, a działanie gorsze.


Ooo! Usunąłeś swojego posta? Nie był zły, a ja teraz wyglądam jakbym gadał sam ze sobą :slight_smile:

Zawsze do usług :smiley:

w uproszczeniu:

render :new nie renderuje od nowa akcji (nie ustawia zmiennych instancji itd) tylko renderuje widok, jeżeli w #create ustawisz @comment i w widoku new.html.erb też używasz @comment to będzie to ten sam komentarz - dlatego będzie miał informacje o błędach.

kiedy robisz redirect od nowa ustawiasz zmienne i renderujesz widok a tam @comment już oznacza co innego :slight_smile:

ja bym zdecydowanie do tego co próbujesz zrobić użył apotomo, mniej się napiszesz a wszystko będzie działać jak należy :slight_smile:

szybko usunąłem swój wcześniejszy post bo wystraszyłem się, że był zbyt … głupi/banalny. Generalnie pisałem w nim, o wątpliwościach o walidacji, na co odpowiedziałeś i o tym, że mam swój system komentarzy i chcę wyświetlać komentarze o błędach dokładnie w miejscu gdzie jest formularz komentarzy na mojej stronie, a ten formularz może być nie tylko na głównej stronie, ale na wielu podstronach, więc redirect_to wydawało się rozsądne, żeby powrócić z błędem dokładnie tam, gdzie napisałem komentarz.

Napisałeś “Generalnie - dużo zachodu, a działanie gorsze.”

Niekoniecznie, jak się to troszkę lepiej zrobi :slight_smile: to ustawiasz tylko “locales”: pl, en i jaki kto chce :slight_smile:

def create
@comment=Comment.new(params[:comment])
if @comment.save
@error = “post zapisany poprawnie”
else
@error = “Błąd zapisu”
if @comment.errors.any?
@error = @errors + " / " + “Komentarz zawiera błędy:”
@comment.errors.full_messages.each do |comment|
@error = @error + " / " + comment
end
end
end
redirect_to “/”, :notice => @error
end

P.S. jak usunę swój post i wstawię jeszcze raz, to ktoś w między czasie pisze odpowiedź i wychodzi zamieszanie :/, więc nie będę ich usuwał tylko edytował.

Ale wielkie dzięki za odpowiedzi! Wiele z nich się dowiedziałem.

A apotomo użyję na pewno, tylko chcę się nauczyć pisać z apotomo i bez niego

Mówią, że nie ma głupich pytań, są tylko głupie odpowiedzi. Mówią też, że tylko krowa nie ma wątpliwości. :slight_smile:

Ja bym jednak zachęcał do rezygnacji z przekierowań na rzecz renderowania odpowiedzi od razu. Chyba juz to komuś pisałem, ale zobacz jak wygląda dialog przeglądarki z serwerem, jeżeli robisz przekierowanie w razie błędu:

  • przeglądarka: POST: zapisz mi ten obiekt. Udało się?
  • serwer: 302 Redirect: zobacz na /comments (a ja sobie ustawie notice do wyświetlenia następnym razem)
  • przeglądarka: GET /comments
  • serwer: 200 OK (a w treści sobie znajdziesz notice. Język człowieków rozumiesz, prawda?)

Porównaj to z dialogiem w przypadku udanego zapisu:

  • przeglądarka: POST: zapisz mi ten obiekt. Udało się?
  • serwer: 302 Redirect: zobacz na /comments (a ja sobie ustawie notice do wyświetlenia następnym razem)
  • przeglądarka: GET /comments
  • serwer: 200 OK (a w treści sobie znajdziesz notice. Język człowieków rozumiesz, prawda?)

A według mnie to powinno wyglądać tak:

  • przeglądarka: POST: zapisz mi ten obiekt. Udało się?
  • serwer: 422 Nie. Masz tu formatkę.

I Ty i serwer macie o wiele mniej roboty. Przycisk F5 też działa prawidłowo w drugim przypadku. Mam też w formatce pola wypełnione tym, co wpisałem, a nie muszę od nowa wszystkiego wpisywać - mogę np. tylko poprawić literówkę w emailu, zamiast wpisywać go od nowa - jeśli nie widzę, co wpisałem mogę się nie domyślić, że to była literówka.

Chcąc to wszystko zaimplementować po przekierowaniu dodajesz sobie całą masę roboty - musisz jakoś zserializować i przekazać następnemu żądaniu GET obiekt, który przecież do bazy się nie zapisze - bo właśnie go walidacje odrzuciły.

Aha, i jeszcze jedno: nic Cię nie zmusza do renderowania akurat widoku :new - akcja :create w przypadku błędu spokojnie może wyrenderować dokładnie to samo co akcja :index, czy gdziekolwiek jest ta formatka tworzenia komentarza. Zachęcam tylko do tego, żeby nie robić redirecta tylko zwrócić odpowiedni kod błędu HTTP.

Dziś rozpoczynam prace nad projektem i cokolwiek robię z formularzem widzę tylko duży czerwony napis: “We’re sorry, but something went wrong.”. EDIT: Doszedłem, że problem pojawia się w przypadku wstawienia polskich znaków do zmiennej @error w comments_controller.

Na początku plików kontrolerów (i modeli) dodaj: “# encoding: UTF-8”

Projekt powoli rośnie i po pewnym czasie pojawił się problem ze strukturą aplikacji. Problem dotyczy tego, czy ta struktura, którą wymyśliłem jest dobra, akceptowalna, kiepska?

Zrobiłe tak, że kontrolery z dopiskiem subpage w nazwie odpowiadają tylko za wygenerowanie widoku nowej podstrony. Pozostałe kontrolery (users, comments, programs) odpowiedzialne są za zbiór funkcjonalności związanych z danym modelem. Czyli za funkcjonalności typu profile, login, regiser odpowiada jedne kontroler “users”, który w swoim widoku zawiera panele (np. profile_panel.html.erb), a które za pomoca helerów ładowane są do widoków obsługiwanych przez kontrolery takie jak subpage_profile.

1.a Czy dla różnych funkcjonalności związanych z obsługą użytkowników takich jak np. profile, register, login i tp. zrobić osobne kontrolery?

1.b Czy też wrzucić to razem do jednego kontrolera “users” i w widoku tegoż kontrolera generować podstrony odpowiedzialne za rożne funkcjonalności?

1.c Czy pozostawić to tak jak jest obecnie, czyli kontrolery takie jak users są odpowiedzialne za panele logowania, rejestracji, profilu i te ładowane są za pomocą helperów do widoków podstron, za które z kolei odpowiedzialne są kontrolery do generowania samych widoków?

  1. Co z kontrolerami do ładowania widoków podstron takimi jak np. subpage_register_controller (nieodpowiedzialnymi za nic innego), czy są potrzebne, czy lepiej bez nich?

STRUKTURA APLIKACJI:

Dla strony głównej (w uproszczeniu):

home_controller => action index => views/comments(index.html.erb) => {
render partial_1 => shared_1.html
helper_login_panel => views/users(login_panel.html.erb) => [wysyłka formularza edycji] => users_controller => action login_validate => redirect_to “/”
render partial_2 => shared_2.html
helper_comments_panel => views/comments(commenst_panel.html.erb) => [wysyłka formularza edycji] => comments_controller => action (destroy, create, update i tp.)
render partial_3 => shared_3.html
render partial_4 =>shared_4.html {
link_to “Zarejestruj się”, :action => “subpage_register”, :controller => ‘subpage_register’
link_to “Twój profil”, :action => “subpage_profile”, :controller => ‘subpage_profile’
}
}

Dla podstrony register (w uproszczeniu):

subpage_register_controller => action subpage_register=> views/subpage_register(subpage_register.html.erb) => {
render partial_1 => shared_1.html
helper_login_panel => views/users(login_panel.html.erb) => [wysyłka formularza edycji] => users_controller => action login_validate => redirect_to “/”
render partial_2 => shared_2.html
helper_users_register_panel => views/users(register_panel.html.erb) => [wysyłka formularza edycji] => users_controller => action create => views/subpage_register(subpage_summary.html.erb)
render partial_3 => shared_3.html
render partial_4 =>shared_4.html
}

Dla strony podstrony profile (w uproszczeniu):

subpage_profile_controller => action subpage_profile=> views/subpage_profile(subpage_profile.html.erb) => {
render partial_1 => shared_1.html
helper_login_panel => views/users(login_panel.html.erb) => [wysyłka formularza edycji] => users_controller => action login_validate => redirect_to “/”
render partial_2 => shared_2.html
helper_users_profile_panel => views/users(profile_panel.html.erb) => [wysyłka formularza edycji] => users_controller => action update => redirect_to “/”
render partial_3 => shared_3.html
render partial_4 =>shared_4.html
}