2017-01-24 50 views
7

Jestem początkujący w szynach i mam problem ze znalezieniem właściwego wyjścia z moim problemem.Double ma wiele atrybutów

mam trzy modele: rozmowa, uczestnika wiadomości, które mają następujące atrybuty:

Rozmowa:

module Messenger 
    class Conversation <ActiveRecord::Base 

    has_many :participants, :class_name => 'Messenger::Participant' 

    def messages 
     self.participants.messages.order(:created_at) 
    end 
    end 
end 

Uczestnik:

module Messenger 

    class Participant <ActiveRecord::Base 

    has_many :messages, :class_name => 'Messenger::Message' 

    belongs_to :conversation, :class_name => 'Messenger::Conversation' 

    end 
end 

Wiadomość:

module Messenger 

    class Message <ActiveRecord::Base 

    default_scope {order(:created_at)} 
    default_scope {where(deleted: false)} 

    belongs_to :participant, :class_name => 'Messenger::Participant' 

    end 
end 

Mój problem polega na tym, że próbuję utworzyć jedną formę, aby utworzyć rozmowę z pierwszą wiadomością. Formularz wygląda następująco:

= form_for @conversation, url: messenger.conversations_create_path do |f| 
    .row 
    .col-md-12.no-padding 
     .whitebg.padding15 
     .form-group.user-info-block.required 
      = f.label :title, t('trad'), class: 'control-label' 
      = f.text_field :title, class: 'form-control' 

     .form-group.user-info-block.required 
      = f.label :model, t('trad'), class: 'control-label' 
      = f.text_field :model, class: 'form-control' 

     .form-group.user-info-block.required 
      = f.label :model_id, t('trad'), class: 'control-label' 
      = f.text_field :model_id, class: 'form-control' 

     = fields_for @message, @conversation.participants.message do |m| 
      = m.label :content, t('trad'), class: 'control-label' 
      = m.text_area :content, class:'form-control' 

    .user-info-block.action-buttons 
    = f.submit t('trad'), :class => 'btn btn-primary pull-right' 

Próbowałem wielu sposobów, aby ta forma prosta, ale ja napotkał pewne problemy, które nie wiem jak to naprawić za pomocą szyny poprawnie.

Próbowałem użyć Field_for, aby dołączyć wiadomość do mojego formularza rozmowy, ale ponieważ nie mam nic zapisanego w mojej bazie danych, wydaje mi się, że nie mogę połączyć wiadomości z nieistniejącym uczestnikiem.

W zasadzie chcę, aby moja pierwsza forma, po sprawdzeniu poprawności, utworzyła rozmowę, powiązała bieżącego użytkownika z tą konwersacją i powiązała wiadomość z tym pierwszym użytkownikiem, ale zakładam, że istnieją sposoby na zrobienie tego za pomocą frameworka i nie chciałbym zrobić tego ręcznie.

Jaki jest właściwy sposób na osiągnięcie tego celu? Czy jestem na dobrej drodze, czy mogę coś zmienić lub coś dodać?

Edytuj: aby uczynić go bardziej zrozumiałym, uczestnik otrzymał identyfikator użytkownika i identyfikator_wiadomości, co oznacza, że ​​jest to tabela relacji. Nie mogę dostosować atrybutów moich modeli, aby było to łatwiejsze, ponieważ muszę je zachować w ten sposób ze względów bezpieczeństwa.

+0

można zajrzeć do has_many za pośrednictwem stowarzyszenia wiadomości w rozmowie zamiast Twoja ręcznie zwijana metoda. – engineerDave

Odpowiedz

1

Pierwszy, w celu formularzu przyjęcia zagnieżdżonych atrybutów przy użyciu pomocnika fields_for formularz, trzeba określić accepts_nested_attributes_for modelu Conversation:

module Messenger 
    class Conversation <ActiveRecord::Base 

    has_many :participants, :class_name => 'Messenger::Participant' 

    # Required for form helper 
    accepts_nested_attributes_for :participants 

    [...] 

Ponieważ chcesz zapisać zarówno Participant także jako Message z tej samej formie, trzeba dodać drugi accepts_nested_attributes_for modelu Participant:

module Messenger 

    class Participant <ActiveRecord::Base 

    has_many :messages, :class_name => 'Messenger::Message' 

    # Required for form helper 
    accepts_nested_attributes_for :messages 

    belongs_to :conversation, :class_name => 'Messenger::Conversation' 

    end 
end 

Następny, w kontrolerze, ponieważ jest to nowy Conversation, że nie ma żadnych Participant w pierwszym, trzeba build skojarzony Participant (przypuszczalnie oparta na current_user), a także związane Message aby ta nowa Participant:

def new 
    @conversation.participants.build(user: current_user).messages.build 
end 

Wreszcie w widoku określić pola atrybutów w trzech zagnieżdżonych bloków, , f.fields_for :participants do |p| i p.fields_for :messages do |m|:

= form_for @conversation, url: messenger.conversations_create_path do |f| 
    [...] 
    = f.fields_for :participants do |p| 
    = p.fields_for :messages do |m| 
     = m.label :content, t('trad'), class: 'control-label' 
     = m.text_area :content, class:'form-control' 

    .user-info-block.action-buttons 
    = f.submit t('trad'), :class => 'btn btn-primary pull-right' 

uwaga Side: The (nieprawidłowo wdrożone) messages metoda Conversation należy zastąpić zwykłą has_many :through zależnością:

has_many :messages, through: :participants 
+0

Cóż, dziękuję milion razy, oto rezultat, którego szukałem , a słowo kluczowe "through" stanowi znaczną poprawę dla mojego kodu. Działa doskonale, akceptowane! – RiddlerNewComer

2

Wiadomość musi być belong_to Bezpośrednia rozmowa, ponieważ musisz ujednoznacznić się, gdy uczestnicy mają więcej niż jedną rozmowę.

Więc zrobiwszy, że można zbudować domyślną wiadomość rozmowa w sterowniku za pomocą

@conversation.messages.build(participant: @conversation.participants.first) 

to dość rozwlekły, więc można dodać kilka metod modelowych zmniejszyć wezwanie kontrolera

@conversation.build_default_message 

W tym przypadku chcesz utworzyć konwersację, ale musi również utworzyć wiadomość z danymi wprowadzonymi przez użytkownika. Tak więc konwersacja musi akceptować atrybuty w imieniu wiadomości.Można to zrobić za pomocą accepts_nested_attributes_for

class Conversation 
    accepts_nested_attributes_for :messages 
end 

Pozwoliłoby to na stworzenie rozmowa z 1 lub więcej powiązanych komunikatów za pomocą

Conversation.create(
    ..., 
    messages_attributes: [ 
    { participant_id: 1, content: 'question' } 
    ] 
) 
+0

Zdefiniowaliśmy nasz model w ten sposób i nie możemy powiązać wiadomości bezpośrednio w przekonwertowaniu ze względów bezpieczeństwa, dlatego muszę przechowywać takie modele. – RiddlerNewComer

0

Przede wszystkim, myślę, że masz błędy w tej metodzie:

def messages 
    self.participants.messages.order(:created_at) 
end 

od Conversationhas_many :participants:

self.participants zwróci tablicę, a nie pojedynczy aktywny obiekt rekordu Participant. Więc nie możesz bezpośrednio wywołać messages w tablicy. Musisz powtórzyć tę tablicę i wywołać messages na każdym obiekcie.

Użyj formularza zagnieżdżonego i metody fields_for i accepts_nested_attributes_for (informacje o tym można znaleźć w dokumencie SO lub dokumentacji), a następnie przesłać kod i przyczynę błędu. Wtedy ktoś może ci pomóc.

I używać fields_for:

Ponieważ nie można powiązać wiadomość bezpośrednio do Conversation i trzeba pole wiadomość, trzeba połączyć @message do każdego uczestnika. możesz zbudować Participant z current_user lub pierwszego użytkownika, tj. User.first dla , a następnie zbudować @message dla tego @message dla tego Participant.