Моделирование сложных отношений в Rails - PullRequest
1 голос
/ 18 сентября 2009

UPDATE

Хорошо, я понял это. Мне пришлось назвать следующие для сортов:
<%=h @user.varieties.find_by_product_id(product.id).name %>

Вот мои два следующих вопроса:

(1) Это вызовет проблемы при редактировании / удалении записей, потому что я не вызываю модель соединения? Я видел несколько видео Райана Бейтса, где он подчеркивает этот момент, но у меня возникают проблемы при попытке сослаться на модель соединения здесь. Другими словами, должен ли вышеуказанный код вызываться через user_products?

Если я использую следующий код, который ссылается на таблицу соединений, я смогу получить его только для отображения variety_id из таблицы соединений (поскольку в таблице соединений нет столбца имени для разновидностей). Я не уверен, как заставить этот код ссылаться на variety_id в таблице соединений, но затем перейти к таблице сортов, чтобы получить фактическое название сорта из столбца «имя».

<% @user.products.each do |product| %>
   <% @user.user_products.find_by_product_id(product.id).variety_id %>
<% end %> 

(2) Этот сложный материал правильно размещен в слое вида или есть лучший способ перенести его на модель или контроллер?

Спасибо.

Оригинальный вопрос ниже теперь решён ...

У меня есть следующие модели:
- пользователи
- продукты
- сорта - user_products

Вот реальная версия того, что я пытаюсь сделать. Допустим, пользователь - это продуктовый магазин. Продукты фрукты, как яблоки. А сорта - это такие виды яблок, как фуджи и макинтош.

Мне нужно создать приложение, где:

  • Пользователь может добавить много типов товаров на свою страницу. Продукты могут включать несколько разновидностей, но пользователь НЕ обязан включать какие-либо разновидности. Например, продуктовый магазин Topps может добавить яблоки на свою страницу. Если это все, что они хотят показать, это должно быть хорошо. Тем не менее, они также могут добавить больше деталей, включая типы яблок, которые они несут, такие как fuji, mcintosh и т. Д. Сорт не может быть просто детальным продуктом. Другими словами, я не могу сделать каждый продукт чем-то вроде apple-fuji, apple-mcintosh. Они должны быть двумя отдельными моделями.

  • На странице пользователя (т. Е. В представлении «показать») мне нужно иметь возможность отображать как продукт, так и сорт (если есть). Система должна понимать, что сорта связаны с конкретным продуктом для данного конкретного пользователя.

После получения первого ответа я пересмотрел свои модели, как описано в ответе ниже. Каждый сорт принадлежит одному продукту, то есть fuji принадлежит только яблочному продукту, который является отдельным идентификатором в таблице продуктов. И продукт имеет много разновидностей, то есть яблочный продукт может иметь 5 или 10 разных разновидностей.

Однако все становится сложнее, потому что у каждого пользователя может быть свой набор комбинаций продуктов / сортов. Например, продуктовый магазин Topps (пользователь) может иметь яблоки (продукт), которые являются fuji и mcintosh (варианты). Но в продуктовом магазине Publix (пользователь) могут быть яблоки (продукт) красного вкуса и праздничные (сорта).

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

Когда я пробую код, указанный ниже в представлении show, я получаю следующую ошибку: неопределенный метод `user_product 'для #:

<% @user.products.each do |product| %>
   <% @user.user_products.find_by_product_id(product.id).varieties %>
<% end %>

С другой стороны, когда я пробую другой вариант, который вы мне дали (перечислены ниже), страница загружается, и запрос sql кажется правильным в журнале, но на странице не отображаются варианты, что странно, потому что я трижды проверил и есть записи в базе данных, которые должны соответствовать запросу. Подробности ниже ...

<% @user.products.each do |product| %>
   <% @user.varieties.find_by_product_id(product.id) %>
<% end %>    

Этот код запускает следующий SQL-запрос:

User Load (0.7ms) SELECT * FROM "users" WHERE ("users"."id" = 2)

Variety Load (0.5ms) SELECT "varieties".* FROM "varieties" INNER JOIN "user_product" ON "varieties".id = "user_products".variety_id WHERE (("seasons".user_id = 2))

Визуализация шаблона в макетах / приложении
Рендеринг пользователей / шоу

Product Load (0.7ms) SELECT "products".* FROM "products" INNER JOIN "user_product" ON "products".id = "user_products".product_id WHERE (("user_products".user_id = 2))

Variety Load (0.4ms) SELECT "varieties".* FROM "varieties" INNER JOIN "user_product" ON "varieties".id = "user_products".variety_id WHERE ("varieties"."product_id" = 1) AND (("user_products".user_id = 2)) LIMIT 1

Variety Load (0.2ms) SELECT "varieties".* FROM "varieties" INNER JOIN "user_products" ON "varieties".id = "user_products".variety_id WHERE ("varieties"."product_id" = 2) AND (("user_products".user_id = 2)) LIMIT 1

В данном случае выше, пользователь, на которого я смотрю, - user_id=2, he does have product_id=1 and product_id=2 in the database. And, in the user_products table, I do have a few records that list this user_id connected to each of these product_ids and associated with some variety_ids. Так что мне кажется, что я должен отображать некоторые результаты в моем представлении шоу, но я ничего не получаю.

Наконец, когда я пытаюсь сделать следующее:

<% @user.products.each do |product| %>
   <%=h @user.user_products.find_by_product_id(product.id) %>
<% end %>

Он отображает следующее в моих представлениях для каждой записи: #<User_product:0x4211bb0>

Ответы [ 2 ]

0 голосов
/ 19 сентября 2009

MikeH

Возможно, вы просто опечатка, @user.user_product должно быть @user.user_products

Когда я пытаюсь использовать код, указанный ниже в представление шоу, я получаю следующее ошибка: неопределенный метод `user_product ' для #:

<% @user.products.each do |product| %>  
  <% @user.user_product.find_by_product_id(product.id).varieties %>  
<% end %>

Также вы можете рассмотреть иерархическую систему типов, в которой у вас есть такие продукты, как «Fuji», «Macintosh» и «Apple».

В таком случае для «Fuji» и «Macintosh» столбцу «parent_id» будет присвоено значение «Apple».

Просто мысль.

0 голосов
/ 18 сентября 2009

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

Модели:

class User < ActiveRecord::Base
  has_many :user_products, :dependent => :destroy
  has_many :products, :through => :user_products
  has_many :varieties, :through => :user_products
end

class UserProduct < ActiveRecord::Base
  belongs_to :user
  belongs_to :product
  belongs_to :variety
end

class Product < ActiveRecord::Base
  has_many :varieties
end

class Variety < ActiveRecord::Base
  belongs_to :product
end

Контроллер:

class UserProductsController < ApplicationController
    before_filter do 
        @user = User.find(params['user_id'])
    end


    def create
      product = Product.find(params['product_id'])
      variety = Variety.find(params['variety_id']) if params['variety_id']

      @user_product = UserProduct.new
      @user_product.user = user
      @user_product.product = product
      @user_product.variety = variety
      @user_product.save
    end

    def destroy
        # Either do something like this:
        conditions = {:user_id => @user.id}
        conditions[:product_id] = params[:product_id] if params[:product_id]
        conditions[:variety_id] = params[:variety_id] if params[:variety_id]

        UserProduct.destroy_all conditions
    end
end

Просмотров: Если вы не заинтересованы в группировании сортов по различным продуктам и просто включите их в список, этого достаточно:

# users/show.html.erb
<%= render @user.user_products %>

# user_products/_user_product.html.erb
<%= h user_product.product.name %> 
<%= h user_product.variety.name if user_product.variety %>

В противном случае нужно добавить что-то более сложное. Некоторые из них можно было бы вставить в модель и контроллер с помощью прокси-серверов ассоциаций, но я не могу вам там помочь

# users/show
<% for product in @user.products do %>
    <%= product.name %>
    <%= render :partial => 'variety', 
        :collection => @user.varieties.find_by_product_id(product.id) %>
<% end %>

# users/_variety
<%= variety.name %> 

Разбивать его по частям, конечно, не нужно (и в этом примере, возможно, немного нелепо), но это помогает разделить разные части, особенно если вы хотите добавить больше элементов для отображения.

И чтобы ответить на ваши вопросы:

  1. Поскольку UserProduct является моделью соединения, вам не нужно отслеживать его индивидуальный идентификатор. У вас есть Пользователь, Продукт и Разнообразие (в этих случаях оно существует). Это все, что вам нужно, эта комбинация уникальна и, таким образом, вы можете найти запись для удаления. Кроме того, модель соединения никогда не должна редактироваться (при условии, что в ней нет больше атрибутов, чем в этих полях). Она либо создает, либо удаляет, поскольку все, что она делает, это сохраняет ассоциации.
  2. Так как это в основном вещь для просмотра, лучше всего поместить ее в виды. Конечно, вы можете использовать контроллер или модель, чтобы собрать все продукты и сорта заранее, выполнив что-то вроде этого:

Перемещение всей логики на контроллер и модель, вероятно, не очень хорошая идея, поскольку они не должны знать (или заботиться) о том, как вы хотите отобразить данные. Так что достаточно подготовить его.

@products_for_user = []
@user.products.each do |product|
 collected_product = {:product => product, 
                      :varieties => @user.varieties.find_by_product_id(product.id)}
  @products_for_user << collected_product
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...