Rails: вложенное пространство имен создает странный путь к частичному элементу, что приводит к ошибке MissingTemplate

У меня есть модель 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, чтобы он не включал «книги» дважды в путь, когда он ищет частичный фрагмент?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
104
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваш контроллер имеет пространство имен 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

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

mate 22.04.2024 12:04

просто чтобы добавить в качестве еще одного варианта, вы также можете переопределить to_partial_path на своих моделях, что, я думаю, будет лучшим решением: def to_partial_path "authors/author" end

Alex 22.04.2024 16:20

спасибо, это работает, но метод обезьяньего исправления to_partial_path (а также lookup_context) кажется мне слишком «хакерским». В конце концов я решил принять принцип KISS, т.е. вообще не использовать вложенные пространства имен.

mate 22.04.2024 19:27

На пути стоят две «книги»… но почему?

Пространства имен ваших моделей влияют на частичные пути по умолчанию.

Вы можете изучить, как это определяется здесь:

https://github.com/rails/rails/blob/main/actionview/lib/action_view/renderer/abstract_renderer.rb#L76

Я ищу способ добиться желаемой функциональности без использования явного пути.

Чтобы не использовать явный путь, вам нужно избавиться от пространств имен ваших моделей.

Пусть это будут просто Book и Author. Проверьте пример здесь:

https://guides.rubyonrails.org/association_basics.html#why-associations-questionmark

И я думаю, вам не нужна отдельная таблица для хранения one-to-many ассоциации (в вашем случае books.author_id должна выполнить эту работу).

Спасибо за ваш вклад. «Книги» и «авторы» — это всего лишь вымышленные понятия, иллюстрирующие эту мысль. В реальном проекте я в основном буду использовать связь «многие ко многим». Поэтому в примере я использую третью модель Authorship.

mate 22.04.2024 12:06

Другие вопросы по теме