У меня есть модель ActiveRecord с именем Book
и модель с именем Book::Author
. У автора есть много книг по модели Book::Authorship
(ассоциация один-ко-многим).
Не по теме:
Причина помещения модели Author в пространство имен Book заключается в том, что мне может понадобиться другая модель автора (например, Post::Author), которая совершенно не связана с Book::Author. С помощью пространства имен я могу четко показать взаимосвязь, например:
example.com/books/authors
явно указывает, что это авторы, написавшие книги (а не посты или что-то еще).
В моем приложении только администраторы могут создавать/обновлять/удалять книги и авторов. Поэтому я создал отдельное пространство имен Admin
для контроллеров и представлений только для администраторов.
В файле routes.rb
у меня есть:
Rails.application.routes.draw do
# These are for the regular users:
# only #index and #show actions are defined in the respective controllers:
namespace :books do
resources :authors, only: %i[index show]
end
resources :books, only: %i[index show]
# These are for the admins only:
# all CRUD methods are defined in the respective controllers:
namespace :admin do
namespace :books do
resources :authors
end
resources :books
end
resources :admin
root "books#index"
end
Затем я создал контроллер Admin::Books::AuthorsController
:
/app/controllers/admin/books/authors_controller.rb
Я ожидаю, что путь просмотра будет следовать тому же шаблону пути:
/app/views/admin/books/authors/_index.html.erb
для индекса;/app/views/admin/books/authors/_author.html.erb
для частичного.К сожалению, это не так: индексная страница работает, но не может найти частичный _author.html.erb
.
Вот как я пытаюсь отобразить список авторов в /app/views/admin/books/authors/_index.html.erb
:
<% @books_authors.each do |book_author| %>
<%= render book_author %>
<% end %>
Что дает следующую ошибку:
ActionView::MissingTemplate в Admin::Books::Authors#index
Частично отсутствует admin/books/books/author/_author с {...}.
На пути стоят две «книги»… но почему?
Приведенный выше код работает только с явным путем к шаблону:
<% @books_authors.each do |book_author| %>
<%= render "admin/books/authors/author", book_author: book_author %>
<% end %>
Что мне не нравится, поскольку это нарушает соглашение о парадигме конфигурации. Я не хочу вручную вводить путь в каждом представлении в пространстве имен Admin::Books::
.
Я ищу способ добиться желаемой функциональности без использования явного пути. Как я могу сказать Rails, чтобы он не включал «книги» дважды в путь, когда он ищет частичный фрагмент?
Ваш контроллер имеет пространство имен Admin::Books
, а ваша модель — Books::Author
, вместе вы получаете двойной путь «книги». Логика примерно выглядит так:
[
File.dirname(Admin::Books::AuthorsController.new.lookup_context.prefixes.first),
Books::Author.new.to_partial_path
].join("/")
#=> "admin/books/books/authors/author"
lookup_context — это то, что вы можете изменить (вероятно, это не очень хорошая идея):
# app/controllers/admin/books/authors_controller.rb
def index
lookup_context.prefixes = ["admin/books", "application"]
@books_authors = Books::Author.all
end
Другой способ — удалить одно из пространств имен Books
:
# either change your controller
class Admin::AuthorsController < ApplicationController
def index
@books_authors = Books::Author.all
end
end
# or model, what if someone writes a book and a post?
class Admin::Books::AuthorsController < ApplicationController
def index
@books_authors = Author.joins(:books)
end
end
Сохранение плоских моделей — популярный способ придерживаться соглашений: нет пространств имен — нет проблем:
BookAuthor
PostAuthor
просто чтобы добавить в качестве еще одного варианта, вы также можете переопределить to_partial_path
на своих моделях, что, я думаю, будет лучшим решением: def to_partial_path "authors/author" end
спасибо, это работает, но метод обезьяньего исправления to_partial_path
(а также lookup_context
) кажется мне слишком «хакерским». В конце концов я решил принять принцип KISS, т.е. вообще не использовать вложенные пространства имен.
На пути стоят две «книги»… но почему?
Пространства имен ваших моделей влияют на частичные пути по умолчанию.
Вы можете изучить, как это определяется здесь:
Я ищу способ добиться желаемой функциональности без использования явного пути.
Чтобы не использовать явный путь, вам нужно избавиться от пространств имен ваших моделей.
Пусть это будут просто Book
и Author
. Проверьте пример здесь:
https://guides.rubyonrails.org/association_basics.html#why-associations-questionmark
И я думаю, вам не нужна отдельная таблица для хранения one-to-many
ассоциации (в вашем случае books.author_id
должна выполнить эту работу).
Спасибо за ваш вклад. «Книги» и «авторы» — это всего лишь вымышленные понятия, иллюстрирующие эту мысль. В реальном проекте я в основном буду использовать связь «многие ко многим». Поэтому в примере я использую третью модель Authorship
.
Спасибо. Я (почти) сделал такой же вывод, но мне пришлось его подтвердить, просто чтобы убедиться, что я ничего не упускаю. С самого начала у меня были трудности с моделями с пространством имен, но я нашел решение здесь, которое довольно неудобное, поскольку вам приходилось (снова!) вручную вводить имена классов и таблиц, чтобы оно заработало. Похоже, что Rails действительно не очень хорошо работает с моделями в пространстве имен, поэтому я, вероятно, оставлю их.