Проблема с отображением данных из нескольких вложенных таблиц в rails 4

Я новичок в рельсах. Я искал и застрял в этой проблеме уже пару дней.

Я создаю сайт, на котором есть пользователи, группы (к которым пользователи могут присоединиться), туры (которые принадлежат группам) и остановки (остановки в турах). У меня есть таблицы для каждого, а также дополнительные таблицы, связывающие их между собой по id (bandmembership, bandtourmembership, tourstopmembership).

Я следовал нескольким руководствам и использовал own_to, has_many => through, чтобы связать все это вместе, и я использовал вложенные атрибуты для успешного отображения данных с одного уровня.

Окончательный формат, который я пытаюсь отобразить,

User Name
=> Band Name #1
====> Tour Name #1
========> Tour Stop #1
========> Tour Stop #2
====> Tour Name #2
========> Tour Stop #1
========> Tour Stop #2
=> Band Name #2
====> Tour Name #3
========> Tour Stop #1
========> Tour Stop #2

и Т. Д.

В настоящее время я могу только отображать название группы без ошибок, но оно отображает одно и то же название группы 3 раза (в базе данных их 3). Когда я пытаюсь добавить туры, выдает ошибку. Я также хотел бы попытаться использовать частичное и коллекцию, чтобы разбить рендеринг каждого элемента.

Мои вопросы

  1. Почему частичное отображение одного и того же имени 3 раза и как мне заставить его отображать правильное имя?

  2. Почему я не могу получить доступ к турам групп и как заставить его сотрудничать?


представления/пользователи/show.html.erb

<h1>Dashboard</h1>
<%= render partial: 'shared/user_item' %>
<% if @user.bands.any? %>
   <h2>You are in <%= @user.bands.count %> bands:</h2>
   <%= render partial: 'shared/band_item', collection: @band_items %>
<% else %>

общий/_band_item.html.erb

<%= @band.name %>

общий/_tour_item.html.erb

<%= @tour.name %>

общий/_stop_item.html.erb

<%= @stop.name %>

controllers/users_controller.rb

class UsersController < ApplicationController

  def create
    @user = User.new(user_params)
    if @user.save
      sign_in @user
      flash[:success] = "You are now signed in"
      redirect_to @user
    else
      render 'new'
    end
  end

  def show
    @user = User.find(params[:id])
    @band_items = Bandmembership.where(user_id: @user.id)
    @band = Band.find(params[:id])
  end

  def new
    @user = User.new
  end

  private

    def user_params
      params.require(:user).permit(:firstname, :lastname, :email, :password, :password_confirmation)
    end


end

модели/user.rb

class User < ActiveRecord::Base

  has_many :bandmemberships
  has_many :bands, :through => :bandmemberships
  has_many :tours, :through => :bands

  accepts_nested_attributes_for :bands, :tours

end

models/bandmembership.rb

class Bandmembership < ActiveRecord::Base

  belongs_to :user
  belongs_to :band

end

models/tour.rb

class Tour < ActiveRecord::Base

  has_many :tourstopmemberships
  has_many :stops, :through => :tourstopmemberships

  has_many :bandtourmemberships
  has_many :bands, :through => :bandtourmemberships

  accepts_nested_attributes_for :stops

end

person user3155441    schedule 03.01.2014    source источник


Ответы (1)


Это пахнет чем-то, что можно решить с помощью метода делегата на уровне класса (Rails Antipatterns, стр. 6-7).

У вас есть метод show, извлекающий параметры как для пользователя, так и для группы. Это что-то вроде tld.com/user/1/band/3?

Если у вас нет параметров для обоих в идентификаторе, то он вытягивает идентификатор пользователя для группы или наоборот.

С точки зрения кода вы должны иметь возможность провести рефакторинг примерно так:

<h1>Dashboard</h1>

<%# this should render app/views/users/_user.html.erb %>
<%= render @user %>

<% if @user.bands %>
  <h2>
    You are in <%= @user.bands.count %> bands:
  </h2>
  <ul>
    <%= @user.bands.each do band %>
      <%# this should render app/views/bands/_band.html.erb %>
      <%= render @band,
                 locals: (band: band) %>
    <% end %>
  </ul>
<%- end -%>

Ваш _band.html.erb будет

<li class="band-name">
    <%= band.name %>
</li>

Код может быть не на 100% правильным, поскольку я продолжаю прыгать между приложением 2.3 и приложением 4.x. Но принцип таков:

1.) Используйте возможности ActiveRecord в своих интересах. У вас есть ассоциация модели, поэтому вам не нужно выполнять поиск самостоятельно. @user.bands должен возвращать массив групп, к которым принадлежит пользователь, поскольку он принадлежит к этим группам ЧЕРЕЗ членство в группах.

2.) Если вам нужно добраться до чего-то, не ходите по дереву через 2 или 3 предмета. например @user.band.first.tour.first — это плохо, плохо. Создайте метод, который находит это в модели пользователя, а затем переходите оттуда, например.

def next_tour
    User.tour.first etc etc
end

а затем вызовите его в представлении как @user.next_tour или что-то еще.

3.) Используйте силу рендеринга @collection_name и используйте значения по умолчанию, чтобы очистить свой код. Его легче читать и лучше, чем множество фрагментов, плавающих в файле shared/.

Это то, о чем я часто говорю, когда выступаю с докладами о Rails View: партиалы должны находиться в папке контроллера, под которым они находятся. /app/views/tours/_tour.html.erb и т. д. было бы лучше, чем tour_item под общим доступом. Это рендеринг одной записи тура для любого места в приложении.

Я также не уверен насчет tourstopmemberships как модели присоединения. К чему ты присоединяешься? Почему бы просто не сделать так, чтобы в туре было много остановок, а остановки принадлежали бы туру? Если вы также рассматриваете модель места проведения, то, возможно, остановки — это модель соединения туров и мест проведения. Затем это позволяет вам добавлять дополнительные метаданные на остановку.

stop.tour
stop.venue
stop.start_time
stop.support_act (which could be a different relationship)

и Т. Д.

person John Athayde    schedule 03.01.2014
comment
Я пытаюсь обернуть мою голову вокруг этого. Я переместил все свои части обратно в соответствующие виды. Я никогда не предполагал, что URL-адрес будет tld.com/user/1/band/3, поэтому использование @band = Band.find(params[:id]) может быть просто неправильным. Мое намерение состояло в том, что по мере того, как представление выполняло итерации, выполняя операторы each, оно вытягивало каждую полосу. Что касается № 3, часть модели, которую я не совсем понимаю... То есть вы говорите, что я должен изменить каждую модель, чтобы перейти на один уровень ниже? Таким образом, User получит группу в модели, Band получит Tour, а Tour получит Stop? Затем к ним можно получить доступ через Do, каждый метод? - person user3155441; 03.01.2014