LINUX.ORG.RU

Помогите «приготовить» activerecord statement

 , , squeel,


0

1

понадобилось добавить еще пару условий для select`а. но так как все условия у меня опциональны, я все пихаю в хеш, и подчищаю его перед тем как скармливаю where`ру. но для условий типа '>=' такой фокус не проходит. гугл мне подсказал о Squeel. но он чето не колется

NoMethodError (undefined method `gteq' for :price:Symbol)

а с другой стороны я посмотрел на код, который уже есть и мне мое програмерское чувство эстетики сказало «you are doing it wrong». поэтому я пришел к вам, боги Р^HЖД и красных камней за мудрыми советами.

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

      format.json do                                                            
        order = [                                                               
          ["rooms", "ASC"],                                                     
          ["apartment_type_id", "ASC"],                                         
          ["floor_num", "ASC"],                                                 
          ["space", "DESC"],                                                    
          ["created_at", "DESC"],                                               
        ]                                                                       
                                                                                
        query_params = {                                                        
          :price => {},                                                         
          :floor_num => {},                                                     
          :rooms => params[:rooms],                                             
          :district_id => params[:district_id],                                 
                                                                                
          :agent_id => params[:agent_id],                                       
          :ad_type_id => params[:ad_type_id],                                   
          :ad_status_id => params[:ad_status_id],                               
          :realty_type_id => params[:realty_type_id],                           
        }                                                                       
                                                                                
        if params['price-min']                                                  
            query_params[:price][:price.gteq] = params['price-min']             
        end                                                                     
        if params['price-max']                                                  
            query_params[:price][:price.lteq] = params['price-max']             
        end                                                                     
                                                                                
        if params['floor-min']                                                  
            query_params[:floor_num][:floor_num.gteq] = params['floor-min']     
        end                                                                     
        if params['floor-max']                                                  
            query_params[:floor_num][:floor_num.lteq] = params['floor-max']     
        end                                                                     
                                                                                
        # FIXME js null here                                                    
        if query_params[:agent_id].include?('null')                             
          index = query_params[:agent_id].index { |item| item == 'null' }       
          query_params[:agent_id][index] = nil                                  
        end                                                                     
                                                                                
        # fix for empty items                                                   
        query_params.delete_if { |key, value| value.nil? || value.empty? }      
                                                                                
        p query_params                                                          
                                                                                
        render json: Advert                                                     
            .where(query_params)                                                
            .order(order.map {|o| o.join(' ')}.join(', '))                      
            .map { |advert| expand_advert_object(advert) }                      
      end                                                                       
★★★★★

Последнее исправление: ZuBB (всего исправлений: 1)

пока переписал так. вроде работает так, как нужно. но все равно простыня не нравится

            format.json do
                order = [
                    ["rooms", "ASC"],
                    ["apartment_type_id", "ASC"],
                    ["floor_num", "ASC"],
                    ["space", "DESC"],
                    ["created_at", "DESC"],
                ]

                query_parts = []
                query_params = []

                unless params[:ad_type_id].blank?
                    query_params << params[:ad_type_id].compact
                    query_parts << 'ad_type_id IN (?)'
                end

                unless params[:ad_status_id].blank?
                    query_params << params[:ad_status_id].compact
                    query_parts << 'ad_status_id IN (?)'
                end

                unless params[:realty_type_id].blank?
                    query_params << params[:realty_type_id].compact
                    query_parts << 'realty_type_id IN (?)'
                end

                unless params[:agent_id].blank?
                    query_part = ['(']
                    params[:agent_id].compact

                    # FIXME js null here
                    if params[:agent_id].include?('null')
                        params[:agent_id].delete_if { |i| i == 'null' }

                        query_part << 'agent_id IS ? OR'
                        query_params << nil
                    end

                    query_part << 'agent_id IN (?) )'
                    query_params << params[:agent_id]
                    query_parts << query_part.join(' ')
                end

                unless params[:rooms].blank?
                    query_params << params[:rooms]
                    query_parts << 'rooms = ?'
                end

                unless params[:district_id].blank?
                    query_params << params[:district_id]
                    query_parts << 'district_id = ?'
                end

                unless params[:price_min].blank?
                    query_params << params[:price_min]
                    query_parts << 'price_min >= ?'
                end

                unless params[:price_max].blank?
                    query_params << params[:price_max]
                    query_parts << 'price_max =< ?'
                end

                unless params[:floor_min].blank?
                    query_params << params[:floor_min]
                    query_parts << 'floor_min >= ?'
                end

                unless params[:floor_max].blank?
                    query_params << params[:floor_max]
                    query_parts << 'floor_max =< ?'
                end

                p query_params
                p query_parts
                query_params.unshift(query_parts.join(' and '))


                render json: Advert
                .where(query_params)
                .order(order.map {|o| o.join(' ')}.join(', '))
                .map { |advert| expand_advert_object(advert) }
            end
ZuBB ★★★★★
() автор топика

Я честно пытался вникнуть в твой код и понять, чего же ты хочешь, но не смог :) Если проблема ещё актуальна, переформулируй как-то. И погугли scope'ы, может пригодится.

Alve ★★★★★
()
Ответ на: комментарий от Alve

см только код №2, так как он (наверное) более понятен.

как ты наверное понял это часть метода, которая отдает все данные модели в виде json`а, предварительно «отфильтрировав» их с помощью параметров, которые прислал клиент.

пример 1

Started GET "/entities/adverts.json?ad_status_id[]=1&realty_type_id[]=1&ad_type_id[]=1&agent_id[]=null&agent_id[]=1&agent_id[]=9" for 127.0.0.1 at 2013-02-17 17:42:28 +0200
Processing by AdvertsController#index as JSON
  Parameters: {"ad_status_id"=>["1"], "realty_type_id"=>["1"], "ad_type_id"=>["1"], "agent_id"=>["null", "1", "9"]}

пример 2

Started GET "/entities/adverts.json?rooms=2&district=&price-min=&price-max=&floor-min=&floor-max=" for 127.0.0.1 at 2013-02-17 18:49:25 +0200
Processing by AdvertsController#index as JSON
  Parameters: {"rooms"=>"2", "district"=>"", "price-min"=>"", "price-max"=>"", "floor-min"=>"", "floor-max"=>""}

в 1м случае это обычный list (все параметры опциональны, если ничего не пришло (ни одного параметра) - отдать нужно всё), а во втором — search (как минимум один параметр должен присутствовать (так мне кажется, иначе в чем смысл)). очень возможно, что количество параметров для поиска возрастет, а значит простыня увеличится.

есть какой-то простой и елегантный способ переписать всю эту лапшу на что-то покороче и не очень запутанное?

о scope читал, но я не уверен как оно здесь мне поможет. можна пример?

ZuBB ★★★★★
() автор топика
Ответ на: комментарий от ZuBB

# В модели scope :floor_max, lambda { |id| where('floor_max =< ?', id) }

# В контроллере def some_action

@adverts = Advert.all # не будет выполнен сразу

@adverts = @adverts.floor_max(params[:floor_max]) if params[:floor_max].present?

render json: @adverts

end

В роутах только надо настроить params'ы, чтобы вместо ID не пришло что-то вредное.

Alve ★★★★★
()
Ответ на: комментарий от Alve

спасибо, идею понял

В роутах только надо настроить params'ы, чтобы вместо ID не пришло что-то вредное.

опять же можна более развернуть твой ответ

ZuBB ★★★★★
() автор топика
Ответ на: комментарий от ZuBB

resources :companies, :constraints => { :id => /([^\/?]+)/ }

при задании роута рельсы позволяют регуляркой указать, что именно принимать в качестве параметра, например, чтобы id передавалось только если там число, и не передавалось ничего плохого, вроде 'delete from blabla;' :)

Alve ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.