Netzke Grid filtering - netzke

I have an issue related to filtering data in Netzke Grid.
column :user_id do |c|
c.editor = {xtype: :combobox, editable: false, min_chars: 2}
end
It is mentioned in the doc that,
A hash that will override the automatic editor configuration. For example, for one-to-many association column you may set it to {min_chars: 1}, which will be passed to the combobox and make it query its remote data after entering 1 character (instead of default 4).
Seems {min_chars: 1} is not working as expected.

Please see example below for simple Customers grid and let me know if it works for you. Netzke way is to use __ (double underscore) to define one-to-many associations. This gives you combobox and all necessary data bindings. I tried different ways to make min_chars property work, but it all failed. Could be a bug. In the end, the only thing that worked is to do it from init_component method.
class Customers < Netzke::Basepack::Grid
def configure(c)
super
c.model = 'Customer'
c.columns = [
{ name: :name, header: 'Customer Name' },
{ id: :country__name, name: :country__name, header: 'Country' }
]
end
js_configure do |c|
c.init_component = <<-JS
function() {
this.callParent();
Ext.ComponentManager.get('country__name').editor.minChars = 2;
}
JS
end
end

Related

ActiveRecord: How to order and retrieve records in a greatest-n-per-group situation

I'm stuck with a classic greatest-n-per-group problem, where a cat can have many kittens, but I'm usually just interested in the youngest.
I already do know how to build a scope and a has_one relation for the Cat.
My question: Is there a way to...
list all cats' names together with their youngest kittens' names...
while at the same time ordering them by their respective youngest kitten's name...
...using just a single SELECT under the hood?
What I got so far:
class Cat < ApplicationRecord
has_many :kittens
has_one :youngest_kitten, -> { merge(Kitten.youngest) }, foreign_key: :cat_id, class_name: :Kitten
scope :with_youngest_kittens, lambda {
joins(:kittens)
.joins(Kitten.younger_kittens_sql("cats.id"))
.where(younger_kittens: { id: nil })
}
end
class Kitten
belongs_to :cat
scope :youngest, lambda {
joins(Kitten.younger_kittens_sql("kittens.cat_id"))
.where(younger_kittens: { id: nil })
}
def self.younger_kittens_sql(cat_field_name)
%{
LEFT OUTER JOIN kittens AS younger_kittens
ON younger_kittens.cat_id = #{cat_field_name}
AND younger_kittens.created_at > kittens.created_at
}
end
end
When I run Cat.with_latest_kittens.order('kittens.name').map(&:name) everything looks fine: I get all the cats' names with just a single SELECT.
But when I run Cat.with_latest_kittens.order('kittens.name').map {|cat| cat.youngest_kitten.name}, I get the right result too, but a superfluous additional SELECT per cat is executed. Which is just logical, because the with_youngest_kittens doesn't know it should populate youngest_kitten. Is there a way to tell it or am I going about this all wrong?
I think adding an includes to your :with_youngest_kittens scope will fix the problem. Try changing the scope to
scope :with_youngest_kittens, lambda {
includes(:youngest_kitten)
.joins(:kittens)
.joins(Kitten.younger_kittens_sql("cats.id"))
.where(younger_kittens: { id: nil })
}
This should prevent Rails from making a separate database query for every kitten.
I found a solution that produces no extra SELECT, however it is quite ugly, so I'll actually go for localarrow's solution as it's more readable!
I thought I'd still post it for the sake of completeness (If someone needs the few ms extra performance):
First I add custom tailored select fields for each kitten column to the Cat.with_youngest_kitten scope:
scope :with_youngest_kittens, lambda {
kitten_columns = Kitten
.column_names
.map { |column_name| "kittens.#{column_name} AS `youngest_kittens.#{column_name}`" }
.join(', ')
joins(:kittens)
.joins(Kitten.latest_outer_join_sql("cats.id"))
.where(later_kittens: { id: nil })
.select("cats.*, #{kitten_columns}")
}
Then I override the has_one youngest_kitten relation with a method, that retrieves those custom selects and calls super if no data has been retrieved:
def youngest_kitten
return super if self[:'youngest_kittens.id'].nil?
kitten_hash = Hash[Kitten.column_names.collect { |column_name| [column_name, self[:"youngest_kittens.#{column_name}"]] }]
kitten_hash[:cat] = self
Kitten.new(kitten_hash)
end

How do I prevent an ActiveResource model from making a request?

I have some ActiveResource models like so:
class Meeting
has_many :sections
end
class Section
has_many :items
end
class Items
has_many :items
end
When I do a find on a Meeting, I get a json response like:
{
id: 1, ...
sections: [
{
id: 1, ...
items: [
{id: 16, ...
items: [
{id: 534, ...
items: [
{id: 8976, ...},
{id: 8977, ...}
]}
]}
]
}
}
And run the following (heavily edited) routine:
#meeting = Meeting.find(#meeting_guid)
import_sections
private
def import_sections
#meeting.sections.each do |section|
new_section = Item.find_or_initialize_by(foreign_guid: section.guid)
new_section.update!(layout: :section)
import_items(section, new_section)
end
end
def import_items(item, parent)
item.items.each do |item|
position = mema_item.orderpreference.to_i + 1
sub_item = Item.find_or_initialize_by(foreign_guid: item.guid)
sub_item.update!(layout: :item, title: item.name, parent: parent)
import_items(item, sub_item)
end
end
When it hits Items 8976 and 8977, it makes requests to /items?item_id=8976 and /items?item_id=8977. Is there a way to prevent it from making these requests when it hits the end of the last child? I don't think I'll ever call Items directly, so can I disable requests on the Item model or something?
I feel silly for not catching this, but I was able to solve the problem quite simply, albeit not through an ActiveResource feature. On the recursive import_items call, I check for if item.attributes.key? 'items', essentially just checking the attributes on the parent object without going into the relationship and creating an api call.

Rails - Serialize related data

I've got two models:
class Continent < ActiveRecord::Base
has_many :countries
end
class Country < ActiveRecord::Base
belongs_to :continent
end
I created controller like:
class ContinentsController < ApplicationController
def index
#continents = Continent.all
render json: #continents
end
end
and serializer:
class ContitnentSerializer < ActiveModel::Serializer
attributes :name, :countries
end
Here my issue begins. I'd like to serialize only countries with given condition where value comes from HTTP GET params. E.g country inside serializer should be displayed only if population is more than params[:population]. The problem is inside serializer we don't have access to params to examine that.
[
{
name: 'Europe'
countries: [
{
name: 'Italy',
population: 1000000
}
]
},
{
name: 'Africa'
countries: [
]
}
]
I've tried to join table with condition but it seems be not working.
#continents = Continent.all.joins("LEFT JOIN countries ON countries.continent_id = continents.id AND countries.population > #{params[:population]}")
Create a scope and call the scope with param value from controller:
scope :population_more_than, ->(population) {all.joins("LEFT JOIN countries ON countries.continent_id = continents.id AND countries.population > ?", population)}
Now call it from controller instead of Continent.all
Continent.population_more_than(params[:population])
You can try
#continents = Continent.all
#continents.num_population = params[:population]
render json: #continents.to_json(methods: :countries_with_population_gt)
in your Continent model
attr_accessor :num_population
def countries_with_population_gt(num_population=0)
countries.where('population > ?', #num_population)
end
Basically, you need to select only Continents that fall under specific rule. If this is a frequently used filter, then I would go with the Babar's suggestion and create a scope.
If this is a one time selection, then I prefer simply do filtering right there without cluttering up my models with non-frequently used scopes.
Continent.joins(:countries).where("countries.population > :population", population: params[:population])
# Or event shorter
Continent.joins(:countries).where("countries.population > :population", params)

How do I clear a Model's :has_many associations without writing to the database in ActiveRecord?

For the sake of this question, let's say I have a very simple model:
class DystopianFuture::Human < ActiveRecord::Base
has_many :hobbies
validates :hobbies, :presence => {message: 'Please pick at least 1 Hobby!!'}
end
The problem is that when a human is updating their hobbies on a form and they don't pick any hobbies, there's no way for me to reflect this in the code without actually deleting all the associations.
So, say the action looks like this:
def update
hobbies = params[:hobbies]
human = Human.find(params[:id])
#ideally here I'd like to go
human.hobbies.clear
#but this updates the db immediately
if hobbies && hobbies.any?
human.hobbies.build(hobbies)
end
if human.save
#great
else
#crap
end
end
Notice the human.hobbies.clear line. I'd like to call this to make sure I'm only saving the new hobbies. It means I can also check to see if the user hasn't checked any hobbies on the form.
But I can't do that as it clears the db. I don't want to write anything to the database unless I know the model is valid.
What am I doing wrong here?
Initialy I also did this same way. Then found out one solution for this issue.
You need to do something like this
params[:heman][:hobby_ids]=[] if params[:human][:hobby_ids].nil?
Then check
if human.update_attributes(params[:human])
Hope you will get some idea...
EDIT:
Make hobbies params like this
hobbies = { hobbies_attributes: [
{ title: 'h1' },
{ title: 'h2' },
{ title: 'h3', _destroy: '1' } # existing hobby
]}
if Human.update_atttributes(hobbies) # use this condition
For this you need to declare accepts_nested_attributes_for :hobbies, allow_destroy: true in your Human model.
See more about this here http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
You can try https://github.com/ryanb/nested_form for this purpose..

simple_form selected: by name

I have a simple_form collection which contains a list of languages. I want to select 'German' by default, but the selected: option in simple_form requires an id. I could obtain the id of 'German' but would hope that wasn't necessary.
= f.association :language, selected: // not sure what to put here
This works, but stinks (I will NOT be using such atrocious code):
= f.association :language, selected: Language.where("name = 'German'").first.id
I would hope for something like:
= f.association :language, selected: { |lan| lan.name == 'German' }
Every example I've found during the last hour involves the id. Not one example of how to select via the name.
Yeah, SimpleForm can accept proc for selected option. In your case the code is:
= f.association :language, selected: lambda { |lan| lan.name == 'German' }