Does anyone know how Rails orders a model's attributes natively?
My issue is this:
I have a model I have been using for a long time called Device.
In devices/show.html.erb I show its attributes using something akin to:
<% #device.attributes.each do |k,v| %>
<tr>
<td><%= k %></td>
<td><%= v %></td>
</tr>
<% end %>
I have decided to add an attribute that is related to the 5th attribute in the model's attributes, but when I run the create_column migration for it, it appears at the end of this list (as it is the last attribute to be added).
I suspect Rails orders its attributes by column-creation time, as I have attempted to move the column to the correct place in my database, and declare the attribute sooner in my attr_accessible list, to no avail. Moving the column in schema.rb and rebuilding the database would probably work, but this is something I can't do. I could hack it into the right spot in the view, but I'm wondering if there is a better solution first.
Is there any way I can do this without enforcing ordering across the whole attribute list?
How about doing something like this:
1- Get the column_names (if you don't want to do it manually)
column_names = Device.column_names.inject([]) { |arr,e| arr.push(e) }
2- Modify the order that you want (i.e, a column name that you care about)
3- Evaluate each on #device
column_names.each_with_object({}) { |m, hash| hash[m] = #device.send(m) }
Rails migrations lets you specify where to add a column with the :after option:
add_column :your_table, :column_name, :data_type, after: :related_column
This could help. But, as this is a presentation concern, I'd order the attributes in a helper.
Related
I'm printing a list, ordered as such:
<% #users.order(:number).each do |u| %>
<%= u.name %>
<% end %>
But some users will have the same value of :number, and in that case the records appear to be ordered based on time_created. How can I set a fallback or secondary ordering system to be used when records have the same value of the attribute used to order them?
You can just keep adding on to the order method.
#users.order(:number, :name, :last_logged_in, :id)
If the numbers are the same, it will break the tie with name, then last_logged_in, then id. This is all done in the database for you.
If you already had the data in memory, then you could use Ruby's sort_by method.
You're probably going to want to use sort_by instead of order.
#users.sort_by {|u| [u.number, u.second_option] }
that will sort first by number and then if number matches it will pull whatever the second option is and compare by that. Technically you can throw as many things to sort by as you want into that array.
Edit: Actually, there's a similar question already answered here
I'm brand new to ROR and very new to programming.
I'm working on a DB and I want entering information to be easy and user friendly. In my index pages for my tables any foreign keys are shown as the id. I would like to learn how to have it display a different column value instead of the id. For example company_name instead of company_id.
From my very little experience I would guess that the .map method would be used. I'm not really sure how though. I've already messed around with it for a while with no success.
The lower half of one of my table's index.html looks like this:
<% #venture_rounds.each do |venture_round| %>
<tr>
<td><%= venture_round.raise_amount %></td>
<td><%= venture_round.company_id %></td>
<td><%= venture_round.investor_id %></td>
</tr>
What can I do to have it grab a value from the company and investor table and show it, instead of the id for those tables?
I hope this question makes sense.
Make sure your VentureRound model has company and investor as defined children
class VentureRound < ActiveRecord::Base
belongs_to :company, :investor
end
Read the information as such
venture_round.company.location # or whatever attributes you're seeking
venture_round.investor.name
I have a RoR application, and the scaffold generation gave me new and edit actions for my model. In the views, there is a _form.html.erb. This form has inputs for two fields that also happen to be attr_accessible but no inputs for fields like created_at, updated_at and id.
I want to generate some custom other forms and views for all my models. I want to know how to programmatically find the fields that should be set manually, and which fields are set by Rails. If I have a model Widget, then Widget.columns gives all the fields.
How does Rails scaffold generation know which fields to put in the form. Also, is there code which determines how "created_at" and "updated_at" are set? Or is this done by the DB?
Update:To clarify, I want to do to things specifically:
I am generating forms for uploading a large number of rows/entities. So I need to know which fields to include in the form. Is it always Widget.accessible_atributes the fields to be set manually?
I need to know which fields to include automatically. If I use the new method of the model, created_at, updated_at, and id are set automatically. I want to load 1000's of rows in the table I will do something like and SQL load file command and I think I need to set created_at and updated_at but not id. How do I know which fields to set. I am looking for some hypothetical method like
>> Widget.auto_columns
# returns {:created_at => '04/12/2013 9:29pm', :updated_at => '04/12/2013'}
Update 2: Trying to see how scaffolding is done I see that in gems/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb, the code uses something like an attributes array that contains only the attributes to show in the form.
<% attributes.each do |attribute| -%>
<div class="field">
<%%= f.label :<%= attribute.name %> %><br />
<%%= f.<%= attribute.field_type %> :<%= attribute.name %> %>
</div>
<% end -%>
<div class="actions">
<%%= f.submit %>
</div>
<%% end %>
I think the attributes array is being generated in scaffold_generator.rb with thor's argument method.
How does Rails scaffold generation know which fields to put in the
form.
When generating a scaffold, you generate it like this::
rails generate scaffold TableName attribute:type_of_attribute attribute2:type_of_attribute
And you can concatenate as many attributes as you wish.
Also, is there code which determines how "created_at" and
"updated_at" are set? Or is this done by the DB?
The timestamps (created_at, updated_at) and id are set by Rails automatically.
They will always be added to your database (unless you want to remove the timestamps in the migration files manually, but the id will always stay).
Also you can set some fields in your Models (Tables) that will be automatically generated, but you have to write code for that.
About accessible attributes, although i don't know the use of it:
Photo.accessible_attributes.each { | attribute | puts attribute if attribute.length > 0 }
For some reason it gives me the first value "", so i just escaped it like that.
Also more on attributes and accessibility and counting columns:
Retrieving Rails Model Column Names
How can you discover accessible attribute names from a model instance?
I'm trying to set up something like a shopping cart, and what I have is a form to choose how many of each size shirt someone may want to buy. I plan on having XXS, XS, S, M, L, XL, and XXL, and they choose a quantity of each and add it to the cart. The simplest way I could think of doing this was to have a t.integer "size_??" for each size in a database for 'tshirt', and after the user clicks add to cart, that digit is then stored in the session until their payment is approved, in which case it is then saved to the database. Then someone could view the database later to see what orders were placed. (This would be for making custom shirts, so there would be no 'examples' saved on the database that need to be viewed or listed).
My first question is if that is correct. I thought of using a "Shirts has_many Sizes" approach, but I'm not the most familiar with it, and if all I am trying to achieve is to obtain a quantity, I feel like that might be a little overkill.
My second question is this: if I have a form to select the quantity of each, how do I assign the values to the session variable. My first thought was an approach like this:
new_order.html.erb:
<%= form_for(:shirt, :url => {:action => 'save_to_session'}) do |f| %>
<table summary="New shirt order form">
<tr>
<th>Size S:</th>
</tr>
<tr>
<td><%= f.select(:size_s, 0..99) %> </td>
</table>
<%= submit_tag("Place order") %>
<% end %>
and on the controller side
def new_order
#temp_order = Shirt.new
#temp_order.size_s = 0
end
def save_to_session
session[:shirt_size_s] = #temp_order.size_s
redirect_to(:action => 'show_session')
end
def show_session
end
where show_session just spits out the session[:shirt_size_s]
The error Im getting with that is
undefined method `size_22' for nil:NilClass
Perhaps this is taboo, as it seems to be a very simple approach, but this will not change the session value. If I add the following line to save_to_session it works, so I'm thinking perhaps save_to_session doesn't have access to #temp_order, but I'm still in the learning phase and don't exactly know what is going wrong here.
session[:shirt_size_s] = 12
Your #temp is an instance variable normally used for populating the page and not used for retrieving data. You want to user the params[:field1] for that where field1 would be the name of your select.
I am developing RoR application that works with legacy database and uses ActiveScaffold plugin for fancy CRUD interface.
However one of the tables of my legacy db has composite primary key. I tried using Composite Keys plugin to handle it, but it seems to have conflicts with ACtiveScaffold: I get the following error:
ActionView::TemplateError (Could not find column contact,type) on line #3 of ven
dor/plugins/active_scaffold/frontends/default/views/_form.rhtml:
1: <ol class="form" <%= 'style="display: none;"' if columns.collapsed -%>>
2: <% columns.each :for => #record do |column| -%>
3: <% if is_subsection? column -%>
4: <li class="sub-section">
5: <h5><%= column.label %> (<%= link_to_visibility_toggle(:default_visible =
> !column.collapsed) -%>)</h5>
6: <%= render :partial => 'form', :locals => { :columns => column } %>
vendor/plugins/active_scaffold/lib/data_structures/sorting.rb:16:in `add'
while having in model code smth like:
set_primary_keys :contact, :type
I highly appreciate any idea how I can get composite keys capability with ActiveScaffold.
I think your best bet may be checking the ActiveScaffold Google Group as it's monitored by core developers of ActiveScaffold and they would ultimately be able to solve your problem and explain why composite keys with the plugin won't work with ActiveScaffold.
Good luck and be sure to post a follow-up if you do get results from the Google Group (which I have posted on before and received feedback very quickly).
One quick result I did find was this.
What I did was to create a facade class that does not inherit from
ActiveRecord then make the "id" show the primary key. In my case the
primary key was computed from other data and could change as a result
of an edit, so I had to override ActiveScaffold in a few places to
allow for the primary key changing after an update. But, all in all
it works and is fairly straightforward. Start with an empty class
and just resolve messages that are not understood. In your case you
could even just redirect all messages to a wrapped ActiveRecord while
replacing the id and id= methods, and filtering the [] and []= methods.
That may do the trick for you.
No, I have not received any reply from the group and I am not sure if ActiveScaffold is actively maintained yet.
After some time playing with ActiveScaffold, I ended up implementing my own CRUD interface from the scratch.
I have this working, with read-only models, using ActiveScaffold on a legacy DB.
The trick was to override the default 'id' field in the model and return a concatenated PK string.
If that's good enough, then here you go:
class CPKReadonlyModel < ActiveRecord::Base
set_primary_key :id_one # only half of it, but id overridden below...
def id
self.id_one.to_s + ',' + self.id_two.to_s
end
def readonly?
true
end
def before_destroy
raise ActiveRecord::ReadOnlyRecord
end
def delete
raise ActiveRecord::ReadOnlyRecord
end
def self.delete_all
raise ActiveRecord::ReadOnlyRecord
end
end
The controller has the following in the active_scaffold config block:
config.actions.exclude :create, :update, :delete