Rails优化:如何精简ActiveRecord生成自定义CSV的重复代码
Great question! The redundancy you're seeing comes from defining your column keys twice—once in questions and again in transformed. Let's fix this by centralizing your column mappings into a single source of truth that handles both custom headers and column order. Here's how to refactor your code cleanly:
Step 1: Define a Single Ordered Mapping
Instead of separate hashes, use an array of pairs where each pair contains the database column name and its corresponding custom header. This array will directly control your CSV's column order, so you can arrange it exactly how you want:
# Centralized mapping: [database_column_name, custom_csv_header] column_mappings = [ [:created_at, 'DATE SUBMITTED'], [:flight_date, 'FLIGHT DATE'], [:flight_num, 'FLIGHT NUMBER'], [:orn, 'ORIGIN'] ]
Step 2: Refactor the CSV Generation
Now we can reuse this mapping for both headers and data extraction, eliminating all duplicate key definitions:
CSV.open('/user/pepe/myfile.csv', 'wb') do |csv| # Write custom headers (extract the second value from each mapping pair) csv << column_mappings.map { |_, header| header } # Fetch only the columns we need (extract first value from each mapping pair) selected_columns = column_mappings.map { |col, _| col } MyModel.all.select(selected_columns).each do |record| # Build row data in the order of our mapping, compact to remove nil values row = column_mappings.map { |col, _| record.send(col) }.compact csv << row end end
Why This Works Better
- No redundancy: All column configuration lives in one place. Want to add/remove a column or change a header? Just update
column_mappings—no need to edit multiple parts of your code. - Controlled column order: The CSV columns will match the order of pairs in
column_mappings, so you don't have to rely on the database's column order. - Cleaner code: We eliminate the unnecessary
transformedhash and simplify data extraction usingrecord.send(col)(alternatively, you could userecord[col]if you prefer).
Bonus: Handle Custom Value Formatting
If you need to format values (like converting created_at to a human-readable string), you can extend the mapping logic without losing clarity:
row = column_mappings.map do |col, _| value = record.send(col) # Format datetime columns to a friendly string value = value.strftime('%Y-%m-%d %H:%M:%S') if col == :created_at value end.compact
内容的提问来源于stack exchange,提问作者myhouse

