Wpis z mikrobloga

Mam taki problem mirasy:
1. Mam tabelę Articles i tabelę Votes. Artykuły są voteable.
2. Jeden użytkownik może zagłosować na jeden artykuł raz.
3. Przy wejściu na stronę główną serwisu użytkownik niezalogowany i zalogowany widzą to samo - 10 ostatnich artykułów.
4. Jeśli użytkownik jest zalogowany -> nie może głosować na artykuły, na które już zagłosował (jest to przedstawione wizualnie na elemencie listy - z frontem nie mam problemu).

Problem: w przypadku gdy użytkownik jest zalogowany chcę aby API zwracało razem z artykułami dodatkowy atrybut dla każdego z nich, informujący czy current user już zagłosował czy jeszcze nie.

Próba rozwiązania: Początkowo zrobiłem eacha, który sprawdzał dla każdego z artykułów czy istnieje głos current user'a na dany artykuł i na tej podstawie dodawałem wirtualny atrybut do każdego artykułu ze statusem głosowania - raz, że jest to skrajnie niewydajne bo mnożę niepotrzebne zapytania, a dwa, że i tak render json: nie zwracał wirtualnego atrybutu w danych.
Próbowałem też powalczyć SQLem żeby zwracać razem z artykułami liczbę głosów danego użytkownika, ale wtedy dostawałem tylko te artykuły, na które użytkownik zagłosował.

Mimo wertowania w poszukiwaniu podobnego problemu nie znalazłem "jedynej słusznej railsowej metody". A może źle do tego podchodzę? Ktoś ma pomysł?

#rails #rubyonrails #webdev
  • 9
  • Odpowiedz
  • Otrzymuj powiadomienia
    o nowych komentarzach

ale wtedy dostawałem tylko te artykuły, na które użytkownik zagłosował.

@WaveCreator: Pewnie przez INNER JOIN łączyłeś, a trzeba było LEFT, żeby dołączyć NULL gdy nie głosował.
  • Odpowiedz
@updater: Hmm, zastanawiam się jak to ubrać nieco czytelniej w słowa, ale spróbuję.
Uproszczona schema:

Article -> id, title, content, user
Vote -> voteable_type, voteable_id, user
User ->
  • Odpowiedz
@MQs: To nie takie proste.

Funkcje **eager_load i includes domyślnie korzystają z LEFT OUTER JOIN, a mimo tego jeśli na łączonej za ich pomocą tablicy zastosujesz funkcję where** to wywali ci wszystkie rekordy z lewej strony, które nie mają powiązania (co jak widać dziwi nie tylko nas).

Article.all.eager_load(:votes).where(votes: { user_id: current_user })
Zwróci wyłącznie te artykuły, na które zagłosował obecnie zalogowany użytkownik. Brakuje mi możliwości filtrowania zwracanych przez eagerload/includes powiązań -
  • Odpowiedz
wyglada na to ze chcesz bardzo dobrze konfigurowac jsona. Zainteresuj sie Serializerami dla jsona gdzie bedziesz mogl dodawac wlasnie takie atrybuty o jakie ci chodzi: https://github.com/Netflix/fast_jsonapi (tam mozesz dodac dla kazdego rekordu jakis custom data, np czy dany user zaglosowal na ten artykul). But anyway nadal nie widze problemu zeby po prostu pobierac z jakiejs tabeli ze dany user dodal vote na dany art. I potem pobrac tylko ta liste z tej
  • Odpowiedz
jezeli chcemy zaoszcedzic czas na kodzie to po prostu piszmy w czystym sql zapytanie i po klopocie jak nie do konca rozumie sie zachowania railsow ;)

@updater: Ostatecznie tak zrobiłem minutę temu ogarniając też dodatkowy atrybut z poziomu SQL.

scope :with_votes, -> (current_user) {
select("articles.*, CASE WHEN votes.user_id IS NOT NULL THEN true ELSE false END AS voted")
.joins(sanitize_sql_array(["LEFT JOIN votes ON votes.voteable_id = articles.id AND votes.voteable_type = 'Article' AND votes.user_id =
  • Odpowiedz
byl najbardizej popularny ale odkad Netflix zrobil jego niemal identyczna alternatywe ale szybsza to teraz ta od netflixa jest polecana ;)
  • Odpowiedz