Edit models with complex primary key with Active Admin - ruby-on-rails

I'm having a trouble with opening AA edit-page for a model, which has a lot of associations.
What I had it's like 50 selects, opening at once. And this page turns to be deadly slow.
After reading this ActiveAdmin: How to handle large associations I considered to use select2 instead of usual select, but things get even worse.
That was because most of the time Rails spent in generating views, not in querying database. So with fancy select2 it reasonably spends even more time in views.
With that knowledge in mind, I decided to not have select inputs on that page at all. So I'll edit "main" object on that slow page, but connected with has_and_belongs_to_many objects should be edited separately.
But after that decision I've faced with a trouble: how should I edit tables with a complex primary key: not just id, but :person_id and :organization_id.
AA by default generates urls like that: /admin/person_organizations/:id/edit, but I need something like this: /admin/person_organizations/:person_id/:organization_id/edit
Any ideas?

ActiveAdmin should be able to handle custom primary keys by default. Just be sure that you add the definition to your model like this:
class Person < ActiveRecord::Base
self.primary_key = 'person_id'
end

After a while I've decided that I don't even need to have multiple keys here since Rails generates artificial id field for habtm tables. And as my goal was to edit this table, I've finished with standard ways of doing this.

Related

creating database fields without „nifty generators” rails

Is there a way in Rails to manipulate database fields and corresponding accessor methods without the „nifty generators” ?
I want users, insofar they are privileged, to be able to manipulate the database structure, that is, at least, to add or delete columns. The privileged user should have the possibility to „Add new” some columns.
Say I have an Object/Table artist and it should “dynamically” receive columns as "date of birth", "has played with", "copies sold"
Not sure if it's a dup. It takes a preliminary decision whether Rails discourages from letting the user do this to begin with or or not. (if that's the case => certainly some noSQL solution)
In pure ruby at least it is easy to dynamically add an attribute to an existing Model/Class like this
Test.class_eval do; attr_accessor "new_attribute"; end
and
Test.new.new_attribute = 2
would return => 2 as expected
In order to create or manipulate a customized input mask / model: can I not manually go the same way the generators go and manually call ActiveRecord::Migration methods like add_column as well as create getter/setter-methods for ORM ?
If yes or no, in both cases, which are they to begin with?
Thanks!
I am not aware of any elegant way to allow an Object to dynamically create new columns. This would not be a good application design and would lead massive inefficiency in your database.
You can achieve a similar type of functionality you seek using ActiveRecord associations in Rails. Here's a simple example for your Artist model using a related Attributes table.
class Artist < ActiveRecord::Base
has_many :attributes
end
class Attribute < ActiveRecord::Base
belongs_to :artist
end
With this association, you can allow your Artist class to create/edit/destroy Attributes. ActiveRecord will use foreign keys in the database to keep track of the relationship between the two models.
If that doesn't work for you, your next best option is to look into NoSQL databases, such as MongoDB, which allow for much more flexibility in the schema. With NoSQL, you can facilitate the insertion of data without a predefined schema.

Rails 3.1 Dependent / Cascading Dropdowns

I am getting to grips with Rails 3.1, and I am hoping that someone can point me in the direction of a Gem that will allow me to use dependent selects on a form (or indicate how this is best done in Rails 3.1). I have come across the chained_selects plugin, but that seems to rely on prototype, so it is not ideal in 3.1.
The simplest example of this is car makes/models:
I have 3 models: vehicleMake, vehicleModel and vehicleTrim. I also have assignment tables vehicleMake_vehicleModel and vehicleModel_vehicleTrim, which specify what models are appropriate for each make etc.
I have a vehicle model which I am trying to populate with a make, model and trim. The vehicle model belongs_to vehicleMake, vehicleModel and vehicleTrim.
How can I ensure that the dropdown for model only shows models for the make that is selected (and thus for trim)? As a second point, how can I validate this in my vehicle model?
Thanks!
I don't know of any jQuery plugins that do that off the top of my head. But really it's just a series of Ajax calls.
When an option is selected from the Make drop down, you send that to the server (via Ajax), get the associated Models back, and populate the next drop down with those options. Then repeat for Trim.
As for validation, you'll probably want to use validates_inclusion_of or just write it manually:
validate :model_matches_make?
def model_matches_make?
unless Make_Model.where(make: self.make).map(&:model).includes?(self.model)
errors.add(:make, "is not valid for your model")
end
end
(using map feels wrong there so maybe there's a better way)

Ruby on Rails working with pre-existing database. Rails makes everything plural

What is the deal with this? I'm working with a pre-existing that I did not do myself. Everything in the database is labeled in singular form. user, security, spec, etc. I guess the right way would be users, securities, specs. At least that's what ruby on rails try's to lookup when I generate a scaffold .
How do I specifically state to use user instead of users in the sql. I don't see anywhere in my project where it is looking up the sql. I mean if my model is user you would think it would try to lookup user. Instead of users.
Thanks for any help.
You need set_table_name :name_of_the_table in your model (source).
So:
class User < ActiveRecord::Base
set_table_name :user
end
The reason they use plural for the table and singular for the model is because an instance of the model represents one user, whereas the table contains all the users. It's just to make it more readable and logical.
You can specifiy the table name:
How do I explicitly specify a Model's table-name mapping in Rails?

Live Search / auto_complete + HABTM = possible?

I am attempting to add in a form field that should allow me to add a record into a join table. The table name contains the ids of the following:
log_id
node_id
So naturally, my models is setup as follows:
class Log
has_and_belongs_to_many :nodes
end
class Node
has_and_belongs_to_many :nodes
end
The objective is that when I create a log, I should be able to associate it with an number of nodes (ergo, servers). And since there is a lot of nodes on hand, it seems to make sense to have a textfield where when you enter a node name, it will pop-up a list of nodes to choose from. However, I am having some difficulty getting that accomplished.
I know how to use the autocomplete plugin, (that had came with Rails), but it seems to only accept a string and not with the id - and apparently not across models. I know how to do an AJAX search (though I am not that familiar with Javascript), but again, getting that ID becomes an issue.
I think that in either case, I may be able to figure how to get that value and put that in - the uncertainty is whether one or the other is the correct approach to getting that value. Which one should I concentrate on? Or is HABTM even appropriate in this?

Calling ActiveRecord's #relationship_ids = [1,2,3] saves immediately. Any workarounds?

I've come across an oddity in ActiveRecord's #relationship_ids method (that's added automatically when you declare 'has_many'), which saves immediately for existing records, which is causing me some issues, and I wonder if anyone had any useful advice.
I'm running Rails 2.3.5.
Consider this simple scenario, where an article has_many tags, say:
a = Article.first
a.name = "New Name" # No save yet
a.author_id = 1 # No save yet
a.tag_ids = [1,2,3] # These changes are saved to the database
# immediately, even if I don't subsequently
# call 'a.save'
This seems surprising to me. It's specifically causing problems whilst trying to build a preview facility - I want to update a bunch of attributes and then preview the article without saving it - but in this instance the tag changes do get saved, even though no other fields do.
(Of possible relevance is that if 'a' is a new article, rather than an existing one, things behave as I'd expect - nothing is saved until I call 'a.save')
I have a fairly nasty workaround - I can override the tag_ids= method in my model to instead populate an instance variable, and actually save the related models in a before_save callback.
But I'd love to know of a simpler way than me having to do this for every model with a has_many relationship I'd like to create a preview facility for.
Does anyone have any fixes/workarounds/general advice? Thanks!
There's a reason things are this way. It's called foreign keys. In a has many relationship, the information that links to the model that has many is stored outside of that model as a foreign key.
As in Articles, has many tags. The information that links a tag to an article is stored either in the tags table or in a join table. When you call save on an article you're only saving the article.
Active record modifies those other records immediately. Except in the case where you're working with a new article that hasn't been saved yet. Rails will delay creating/updating the associated records if it doesn't know which id to place in the foreign key.
However, if you're modifying existing records, the solution you've decided on is really all that you can do. There's an even uglier hack using accepts_nested_attributes_for, but it's really not worth the effort.
If you're looking to add this behaviour to many models but not all models, you might want to consider writing a simple plugin to redefine the assigment the method you need and add the call back in a single class method call. Have a look at the source of something like acts_as_audited to see how it's done.
If you're looking to add this behaviour to all models, you can probably write a wrapper for has_many to do that.

Resources