Rails应用中基于多模型数据生成PDF的实现方案咨询
Great question! Let’s dive in—first off, Prawn absolutely supports generating single PDFs from multiple models in your Rails app. It’s actually one of the most flexible options for this use case, since you have full control over how you pull and format data from different sources. Let’s walk through how to use it, plus cover other popular gems that might fit your workflow better if you prefer working with HTML/CSS instead of direct Ruby code.
Step 1: Set Up the Gem
Add Prawn to your Gemfile:
gem 'prawn'
Run bundle install to install it.
Step 2: Create a PDF Generator Class
Create a dedicated class (e.g., app/pdfs/combined_report_pdf.rb) to handle the PDF generation—this keeps your controllers clean and makes the code reusable:
class CombinedReportPdf def initialize(user) @user = user @orders = user.orders.includes(:products) # Pull related orders and products in one query @pdf = Prawn::Document.new end def generate add_user_info add_orders_list add_product_summary @pdf.render end private def add_user_info @pdf.text "User Report: #{@user.name}", size: 20, style: :bold @pdf.text "Email: #{@user.email}" @pdf.move_down 20 end def add_orders_list @pdf.text "Recent Orders", size: 16, style: :bold @orders.each_with_index do |order, index| @pdf.text "#{index + 1}. Order ##{order.id} (Placed: #{order.created_at.strftime('%Y-%m-%d')})" @pdf.text "Total: $#{order.total_amount}" @pdf.move_down 10 end @pdf.move_down 20 end def add_product_summary @pdf.text "Purchased Products", size: 16, style: :bold all_products = @orders.flat_map(&:products).uniq all_products.each do |product| @pdf.text "- #{product.name}: $#{product.price} (Ordered #{product.orders.count} times)" end end end
Step 3: Use the Class in Your Controller
In your controller (e.g., users_controller.rb), add an action to generate and send the PDF:
def generate_report @user = User.find(params[:id]) pdf = CombinedReportPdf.new(@user).generate send_data pdf, filename: "user_report_#{@user.id}.pdf", type: "application/pdf", disposition: "inline" end
That’s it! This pulls data from User, Order, and Product models and combines them into a single, structured PDF.
If you’d rather build your PDF layout using HTML/CSS (which is great for complex designs or if you already have ERB templates), these gems are perfect:
Wicked PDF
Wicked PDF uses wkhtmltopdf under the hood to convert HTML/CSS views into PDFs. It’s super intuitive if you’re comfortable with Rails views.
Setup & Usage
- Add the gem to your
Gemfile:
gem 'wicked_pdf' gem 'wkhtmltopdf-binary' # Includes the binary so you don't need to install it separately
Run bundle install.
- Create an ERB template (e.g.,
app/views/users/generate_report.pdf.erb) that pulls data from multiple models:
<h1>User Report: <%= @user.name %></h1> <p>Email: <%= @user.email %></p> <h2>Recent Orders</h2> <ul> <% @user.orders.each do |order| %> <li>Order #<%= order.id %> - Placed on <%= order.created_at.strftime('%Y-%m-%d') %> - Total: $<%= order.total_amount %></li> <% end %> </ul> <h2>Purchased Products</h2> <ul> <% @user.orders.flat_map(&:products).uniq.each do |product| %> <li><%= product.name %> - $<%= product.price %> (Ordered <%= product.orders.count %> times)</li> <% end %> </ul>
- Update your controller action:
def generate_report @user = User.find(params[:id]) respond_to do |format| format.pdf do render pdf: "user_report_#{@user.id}", template: "users/generate_report.pdf.erb", disposition: "inline" end end end
Grover
Grover uses Puppeteer (a headless Chrome instance) to render HTML/CSS into PDFs, which means it supports modern CSS features (flexbox, grid, etc.) that older tools might struggle with.
Setup & Usage
- Add the gem:
gem 'grover'
Run bundle install, then run rails generate grover:install to set up the initializer.
Create the same ERB template as above (or any HTML/CSS view tailored to your needs).
Controller action:
def generate_report @user = User.find(params[:id]) html = render_to_string(template: "users/generate_report.pdf.erb", layout: "pdf") # Use a dedicated PDF layout if needed pdf = Grover.new(html).to_pdf send_data pdf, filename: "user_report_#{@user.id}.pdf", type: "application/pdf", disposition: "inline" end
PDFKit
Similar to Wicked PDF, PDFKit uses wkhtmltopdf but has a simpler API for basic use cases. It’s lightweight and easy to set up.
Setup & Usage
- Add gems:
gem 'pdfkit' gem 'wkhtmltopdf-binary'
Run bundle install.
- Create an initializer (
config/initializers/pdfkit.rb):
PDFKit.configure do |config| config.wkhtmltopdf = Rails.root.join('bin', 'wkhtmltopdf').to_s if Rails.env.development? end
- Controller action:
def generate_report @user = User.find(params[:id]) html = render_to_string(template: "users/generate_report.pdf.erb") kit = PDFKit.new(html) pdf = kit.to_pdf send_data pdf, filename: "user_report_#{@user.id}.pdf", type: "application/pdf", disposition: "inline" end
内容的提问来源于stack exchange,提问作者Jul

