Я работаю над проектом Ruby on Rails, сделанным кем-то другим. Мне поручено разрешить сортировку таблиц по столбцам.
В этой конкретной таблице есть столбцы из связанной таблицы. Таблица перечисляет историю транзакций и показывает адрес (и другие столбцы) свойства.
Для доступа к значению отношения тело таблицы использует transaction.property.street_address
Когда столбец таблицыссылка нажата, она отправляет параметры запроса на URL. Поскольку не существует транзакции address.street_address, я получаю ошибку 500.
Столбец street_address
находится в таблице связанных свойств, поэтому я не уверен, как сортировать по этому полю
<div class="table-container">
<table class="table text text--body-3 text--medium">
<tr class="table__heading text--body-4">
<% if !current_user.user? %>
<th>Date</th>
<% end %>
<th><%= link_to 'Address', { order: 'street_address', desc: @desc == :asc} %></th>
<th>Property type</th>
<th><%= link_to 'Price', { order: 'sales_price', desc: @desc == :asc} %></th>
<% if !current_user.agent? %>
<th><%= link_to 'Agent', { order: '', desc: @desc == :asc} %></th>
<% end %>
<th>Type</th>
<th></th>
</tr>
<% @transactions.each do |transaction| %>
<tr class="table__row" data-link-to="<%= property_path(transaction.property) %>">
<td><%= transaction.sales_date.try(:strftime, '%B %d, %Y') %></td>
<td class="table__address"><%= transaction.property.street_address %></td>
<td><%= transaction.property.types.map(&:humanize).join(', ') %></td>
<td><%= number_to_currency(transaction.sales_price) %></td>
<% if !current_user.agent? %>
<td><span><%= transaction.user.full_name %></span></td>
<% end %>
<% if transaction.property.for_sale? %>
<td>Sale</td>
<% else %>
<td>Lease</td>
<% end %>
<td class="table__more">
<%= link_to "See the property", property_path(transaction.property), class: 'table__row__details button button--tertiary button--tertiary-small' %>
</td>
<td>
</tr>
<% end %>
</table>
</div>
Вот модель транзакции:
class Transaction < ApplicationRecord
belongs_to :user
belongs_to :property
validates :cap_rate, presence: true
validates :sales_date, presence: true
validates :sales_price, presence: true
validates :noi, presence: true
scope :sold_property, -> do
joins(:property).where(properties: { for_sale: true })
end
scope :sold_before_by_broker_agents, -> (month, broker) do
joins(:user).where('sales_date < ?', month.end_of_month)
.merge(User.all_by_parent_id(broker.id))
end
end
Вот модель свойств:
class Property < ApplicationRecord
extend FriendlyId
friendly_id :slug_text, use: :slugged
PHOTOS_LIMIT = 15
STATES_MAP = {
'Alabama' => 'AL', 'Alaska' => 'AK', 'Arizona' => 'AZ', 'Arkansas' => 'AR',
'California' => 'CA', 'Colorado' => 'CO', 'Connecticut' => 'CT',
'Delaware' => 'DE', 'District of Columbia' => 'DC', 'Florida' => 'FL',
'Georgia' => 'GA', 'Hawaii' => 'HI', 'Idaho' => 'ID', 'Illinois' => 'IL',
'Indiana' => 'IN', 'Iowa' => 'IA', 'Kansas' => 'KS', 'Kentucky' => 'KY',
'Louisiana' => 'LA','Maine' => 'ME', 'Maryland' => 'MD', 'Massachusetts' => 'MA',
'Michigan' => 'MI', 'Minnesota' => 'MN', 'Mississippi' => 'MS', 'Missouri' => 'MO',
'Montana' => 'MT', 'Nebraska' => 'NE', 'Nevada' => 'NV', 'New Hampshire' => 'NH',
'New Jersey' => 'NJ', 'New Mexico' => 'NM', 'New York' => 'NY', 'North Carolina' => 'NC',
'North Dakota' => 'ND', 'Ohio' => 'OH', 'Oklahoma' => 'OK', 'Oregon' => 'OR',
'Pennsylvania' => 'PA', 'Rhode Island' => 'RI', 'South Carolina' => 'SC',
'South Dakota' => 'SD', 'Tennessee' => 'TN', 'Texas' => 'TX', 'Utah' => 'UT',
'Vermont' => 'VT', 'Virginia' => 'VA', 'Washington' => 'WA', 'West Virginia' => 'WV',
'Wisconsin' => 'WI', 'Wyoming' => 'WY'
}.freeze
PROPERTY_TYPES = [:industrial, :retail, :shopping_center, :multifamily,
:specialty, :office, :health_care, :hostpitality_or_hotel,
:sports_and_entertainment, :land, :general_business, :condominium,
:residential_income].freeze
belongs_to :user
has_many :viewed_properties
has_many :last_viewed_by, through: :viewed_properties, source: :user
has_many :favorite_users_properties
has_many :favorited_by, through: :favorite_users_properties, source: :user
has_one :property_transaction, class_name: 'Transaction', dependent: :destroy
has_many :property_amenities, dependent: :destroy
has_many :amenities, through: :property_amenities
has_many :property_highlights, inverse_of: :property, dependent: :destroy
has_many :unit_prices, inverse_of: :property, dependent: :destroy
has_many :conversations, dependent: :destroy
has_many :property_photos, inverse_of: :property, dependent: :destroy
has_many :brochures, inverse_of: :property, dependent: :destroy
enum land_location: [:industrial_park, :urban]
enum rail: [:yes, :no]
accepts_nested_attributes_for :property_highlights, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :unit_prices, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :property_photos, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :brochures, reject_if: :all_blank, allow_destroy: true
validate :listing_limit_exceeded, on: :create
validates :property_type,
:state,
:county,
:municipality,
:city,
:zip_code,
:street_address,
:listed_at,
:expires_at,
:property_status,
:description,
:status_date_change,
:parking,
:lot_size_units,
presence: true
validates :price,
:building_square_feet,
:lot_size,
:units,
:total_taxes,
numericality: { greater_than_or_equal_to: 0 }
validates :year_built, inclusion: { in: 1900..(Date.today.year + 5) }
validates :for_sale, presence: true, unless: :for_lease?
validates :for_lease, presence: true, unless: :for_sale?
validates :ceiling_height, numericality: { greater_than_or_equal_to: 0 }, if: -> { industrial? || office? }
validates :available_square_feet, numericality: { greater_than_or_equal_to: 0 }, unless: -> { multifamily? || specialty? || residential_income? || land?}
validates :max_contiguous_square_feet, numericality: { greater_than_or_equal_to: 0 }, if: -> { industrial? || retail? || office? }
validates :amps_volts_phase,
:number_of_dock_doors,
:number_of_drive_in_doors,
:available_office_square_feet,
numericality: { greater_than_or_equal_to: 0 }, if: :industrial?
validates :rail, presence: true, if: :industrial?
validates :income, :expense, numericality: { greater_than_or_equal_to: 0 }, if: -> { multifamily? || residential_income? }
validates :office_class, presence: true, if: :office?
validates :land_location, presence: true, if: :land?
validates :condominium_units,
numericality: { greater_than_or_equal_to: 0 }, if: :condominium?
validates :suite_number, presence: true, if: :condominium?
validates :flat_fee,
:percentage_of_sale,
#:referral_fee,
allow_blank: true, numericality: { greater_than_or_equal_to: 0 }
validates :apn, :zoning, presence: true, if: :for_sale?
validates :terms, presence: true, if: :for_lease?
geocoded_by :address
after_validation :geocode, if: :address_changed?
mount_uploader :cover, CoverUploader
mount_uploaders :photos, PhotoUploader
scope :last_viewed_properties, -> (user_id, limit) do
joins(:viewed_properties)
.merge(ViewedProperty.order(created_at: :desc).where("viewed_properties.user_id = ?", user_id).limit(limit))
end
default_scope { where(:deleted_at => nil) }
before_validation :set_expires_at
scope :not_sold, -> (parent_id: nil) do
if parent_id.present?
left_joins(:property_transaction)
.where(users: {parent_id: parent_id})
.where('transactions.property_id': nil)
else
left_joins(:property_transaction)
.where('transactions.property_id': nil)
end
end
scope :active, -> { where('expires_at >= ?', Time.now) }
scope :expiring_soon, -> { active.where('expires_at <= ?', 7.days.from_now) }
def self.property_types
PROPERTY_TYPES.each_with_index.map { |k, v| [k.to_s, v] }.to_h
end
def photos
property_photos.map(&:image)
end
PROPERTY_TYPES.each do |type|
define_method("#{type}?") do
types.include?(type.to_s)
end
define_method("exclusive_#{type}?") do
types.include?(type.to_s) && types.size == 1
end
end
def property_type
types[0]
end
def property_type=(value)
types[0] = value
end
def slug_text
[street_address, city, zip_code].compact.join(', ')
end
def state_short
STATES_MAP[state] || state
end
def address
['United States', state, county, zip_code, city, street_address].compact.join(', ')
end
def address_changed?
city_changed? || street_address_changed? || state_changed? || county_changed? || zip_code_changed?
end
def property_cover
return self.cover if cover.url
'example/property1.jpeg'
end
def secret_renewal_token
Digest::SHA256.hexdigest("#{user.authenticatable_salt}#{id}#{expires_at}")
end
def listed_at
self[:listed_at] || Date.today
end
private
def listing_limit_exceeded
if user.parent_id.present?
broker_user = User.find(user.parent_id)
current_subscription = broker_user.subscriptions.active.first
current_listing = Property.left_joins(:property_transaction, :user)
.merge(User.all_by_parent_id(user.parent_id))
.where('transactions.property_id': nil)
.count
else
current_listing = Property.left_joins(:property_transaction, :user).where(user_id: user.id).where('transactions.property_id': nil).count
current_subscription = user.subscriptions.active.first
end
unless current_subscription
errors.add(:base, "Your subscription has expired")
return
end
listing_limit = current_subscription.plan.listing_limit
if listing_limit <= current_listing
errors.add(:base, "Your listing limit is exceeded (current limit is: #{listing_limit})")
end
end
def set_expires_at
if listed_at
self.expires_at ||= (listed_at + 30.days)
end
end
end