Нужна помощь в построении правильных ассоциаций между моделями - PullRequest
0 голосов
/ 06 марта 2020

У меня есть две модели: User и Channel. Существует 4 типа каналов с разными ценами: канал животных на 1000, канал новостей на 800 и др. c. Каждый пользователь может подписаться на разные каналы, поэтому в итоге я создал три модели со следующими ассоциациями:

Channel
has_many :user_channels
has_many :users, through: :user_channels

User 
has_many :user_channels
has_many :channels, through: :user_channels

UserChannel
belongs_to :user
belongs_to :channel

Но я не уверен, что это правильный способ обработки.

Также сбивает с толку то, что когда я пытаюсь получить цену канала для конкретного пользователя, как user.user_channels.price, где price - это поле Channel, результат - это коллекция, которая на самом деле мне кажется неправильной, но опять же, я только уч.

схема:

create_table "channels", force: :cascade do |t|
    t.integer "category"
    t.decimal "price"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

create_table "user_channels", force: :cascade do |t|
    t.integer "user_id"
    t.integer "channel_id"
    t.decimal "spent_amount"
    t.date "start_date"
    t.date "due_date"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

create_table "users", force: :cascade do |t|
    t.string "first_name"
    t.string "last_name"
    t.string "email", default: "", null: false
    t.boolean "admin", default: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

Ответы [ 2 ]

1 голос
/ 07 марта 2020

Также сбивает с толку то, что когда я пытаюсь получить цену канала для конкретного пользователя как user.user_channels.price, где цена - это поле результата канала, это сбор, который на самом деле мне кажется неправильным, но опять я только учусь. Если вы хотите узнать цену канала для конкретного пользователя, вам нужно сделать это

# Assume you know ids of user and channel both. user = User.find(1).includes(:channels) #Preload channeles to look through them finding seeking channel and its price channel_id = 1 price = user.channels.find_by(id: channel_id)&.price # User might not be subscribed to the channel. In that scenario the price will be nil.

1 голос
/ 06 марта 2020

Вы не задаете БД правильный вопрос. Если вы хотите узнать цену канала, как ПРОСТО знать, что пользователь говорит вам об этом? Поскольку UserChannel является таблицей отношений, вы просите ее "дать мне все каналы этого пользователя" с помощью user.user_channels. Который является коллекцией.

@my_channels = user.user_channels
#=> returns an array of UserChannels objects NOT an array of channels 

Вы должны указать, по какому каналу вы хотите цену. Есть много способов сделать это:

@my_var = user.channels.where(category: 'animals')

Это даст вам множество всего, что соответствует этим критериям. Если столбец категории имеет уникальное ограничение, он просто даст вам один результат, но все равно будет в массиве.

@my_var = user.channels.find_by(category: 'animals') 

вызовет этот запрос и выдаст первый элемент, который возвращается как объект #<channel>. Вы можете запросить цену:

@my_var.price #or all in one:
@my_var = user.channels.find_by(category: 'animals').price

Вам не нужно напрямую обращаться к user_channels. И я бы обычно назвал его users_channels, чтобы обозначить двустороннюю природу has_many_through, поскольку вы можете делать такие вещи, как:

@channel = Channel.first
@channel.users 
#=> returns an array of all users that have that channel.
...