I'm using Padrino with DataMapper, and I'm trying to make a migration for adding an association to a model. For example, I begin with this:
class User
include DataMapper::Resource
property :id, Serial
property :name, String
end
class Post
include DataMapper::Resource
property :id, Serial
property :title, String
property :body, Text
end
class Comment
include DataMapper::Resource
property :id, Serial
property :name, String
end
And I end with the following:
class User
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :posts
end
class Post
include DataMapper::Resource
property :id, Serial
property :title, String
property :body, Text
belongs_to :user
has n, :comment
end
class Comment
include DataMapper::Resource
property :id, Serial
property :name, String
belongs_to :post
end
I already have the migration for creating the three tables, but I do not for adding the associations. What would the code be for creating the migration for the associations?
DataMapper.auto_upgrade! will add new FK properties
auto_upgrade is nice, but won't allow incremental step back.
migration 3, :create_products do
up do
modify_table :post do
add_column :user_id, Integer
end
modify_table :comment do
add_column :post_id, Integer
end
end
down do
modify_table :post do
drop_column :user_id, Integer
end
modify_table :comment do
drop_column :post_id, Integer
end
end
end
that's it.
Related
At the moment I have the database model up and can populate the models with data:
user.persons << person
group.functions << function
group.classificationlevels << clasfication
user.groups << group
These are the models I am using at the moment for getting the data associated with each other:
module Core_authentication
class User
include DataMapper::Resource
property :id, Serial
property :username, String, :required => true, :unique => true
property :password, BCryptHash, :required => true
property :email, String, :format => :email_address, :required => true
property :created_at, DateTime
property :updated_at, DateTime
#Creating join tables to link to group and person information
has n, :persons, :through => Resource
has n, :groups, :through => Resource
def username= new_username
super new_username.downcase
end
end
class Group
include DataMapper::Resource
property :id, Serial
property :groupname, String, :required => true
#Another jointable link group to link to functions and classification levels
has n, :functions, :through => Resource
has n, :classificationlevels, :through => Resource
has n, :users, :through => Resource
def groupname= new_group
super new_group.downcase.capitalize!
end
end
class Person
include DataMapper::Resource
property :id, Serial
property :firstname, String
property :lastname, String, :required => true
property :adress, String
property :postcode, String, :length => 6, :required => true
property :telefoon, String
property :created_at, DateTime
property :updated_at, DateTime
has n, :users, :through => Resource
def firstname= new_firstname
super new_firstname.downcase.capitalize!
end
def lastname= new_lastname
super new_lastname
end
end
class Function
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :groups, :through => Resource
end
class Classificationlevel
include DataMapper::Resource
property :id, Serial
property :levelcode, Integer
property :name, String
has n, :groups, :through => Resource
end
end
I want to get a user's groups they are a member of, and the classification level that is associated with each group.
I tried multiple ways of doing this, and also looked around on the web, but I cannot find a clear explanation on how I must do this, and so I can't get it working.
The documentation for Datamapper (section for "Has, and belongs to, many (Or Many-To-Many)") has some hints, here's a simplified example of your models:
require 'sqlite3'
require 'dm-core'
require 'dm-sqlite-adapter'
require 'dm-migrations'
DataMapper.setup(:default, 'sqlite3:m2m.sqlite3')
class User
include DataMapper::Resource
property :id, Serial
has n, :persons, :through => Resource # => PersonUser
has n, :groups, :through => Resource # => GroupPerson
end
class Group
include DataMapper::Resource
property :id, Serial
has n, :functions, :through => Resource # => FunctionGroup
has n, :classificationlevels, :through => Resource # => GroupClassificationlevel
has n, :users, :through => Resource # => GroupUser
end
class Person
include DataMapper::Resource
property :id, Serial
has n, :users, :through => Resource # => PersonUser
end
class Function
include DataMapper::Resource
property :id, Serial
has n, :groups, :through => Resource # => FunctionGroup
end
class Classificationlevel
include DataMapper::Resource
property :id, Serial
has n, :groups, :through => Resource # => GroupClassificationlevel
end
And an example of using them (if you put both of these snippets in a file and run them, you can see the output):
DataMapper.finalize
DataMapper.auto_migrate!
user = User.create
group = Group.create
# link them by adding to the relationship
user.groups << group
user.save
p user.groups # => [#<Group #id=1>]
Im building a report system which uses a sort of meta question model. Questions are previusly saved in the database, and then depending of the type of report some questions are taken from the database.
Wanting to keep things DRY, i'm trying to figure out a way to pass the information of the Variable model to my report_header with no avail.
In the new action i have:
reportBody = #report_head.report_bodies.build(:variable_id => a.id)
#report_head.variables #modified, thx.
all i need is to pass the attributes from the Variable to report_head in a DRY way.
If you need to know my models:
class Variable < ActiveRecord::Base
attr_accessible :id,:break_point, :description, :name, :time_frequency, :v_type
has_many :report_bodies
has_many :report_heads, :through => :report_bodies
end
class ReportHead < ActiveRecord::Base
attr_accessible :email, :name , :report_bodies_attributes, :report_bodies, :variables_attributes
has_many :report_bodies
has_many :variables, :through => :report_bodies
accepts_nested_attributes_for :report_bodies
end
class ReportBody < ActiveRecord::Base
attr_accessible :report_head_id, :variable_value, :variable_id, :variables_attributes, :report_heads
belongs_to :report_head
belongs_to :variable
end
Update
I updated the model as suggested, and modified the way to call the variables. However im still confused about how to use it in the view, if i do something like:
<%= f.fields_for :variables do |variable| %>
<%= variable.text_field :name, :value => :name, :class => 'text_field' %>
<% end %>
it prints a string instead of the actual name.
You have define wrong name association, your association of ReportBody should be:
belongs_to :report_head
belongs_to :variable
This is not correct:
#report_head.report_bodies.build(:variable_id => a.id,:report_head_id =>#report_head.id)
chang it to:
#report_head.variables.build(:variable_id => a.id)
it's better, you don't have to set report_head_id. And this is wrong:
#report_head.report_bodies.variables
If you want to get all variables belong to #report_head, you just need using:
#report_head.variables
When should I use "accepts_nested_attributes_for"? In the example below, I can do a successful "user.microposts.create" without needing "accepts_nested_attributes_for" in the User model.
class Micropost < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :microposts
end
accepts_nested_attributes_for is just a shortcut. It defines a dynamic attribute {field_name}_attributes so that if you have a form you can include nested attributes and have it automatically assign them to an association. Like so :
form_for :object do |f|
f.text_field :attr1
f.text_field :attr2
f.fields_for :association_attributes do |g|
g.text_field :nested1
g.text_field :nested2
end
end
This posts with parameters {object: {attr1: val, attr2: val, association_attributes: {nested1: val, nested2: val}} and adding accepts_nested_attributes_for :association to your class makes the whole thing work without any extra code.
I'm trying out the new Rails gem http://activeadmin.info/ and it's working great! However I can't find any documentation on how to use it across associations. For example:
class Membership < ActiveRecord::Base
belongs_to :course
belongs_to :person
class Course < ActiveRecord::Base
has_many :memberships
has_many :people, :through => :memberships
class Person < ActiveRecord::Base
has_many :memberships
has_many :courses, :through => :memberships
The membership join table includes some extra data as well (ie: attendance). I'm trying to show the membership with both the course and student name - and allow filtering / sorting on those names. As far as I have found, Active Admin doesn't work across associations. Has anyone else been successful in doing that, or found another gem that does? Thanks so much!
ingredient.rb
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :products, :join_table => :ingredients_products
end
product.rb
class Product < ActiveRecord::Base
has_and_belongs_to_many :ingredients, :join_table => :ingredients_products
end
don't forget the migrations for the joining table (:id to false!)
class CreateProductsIngredients < ActiveRecord::Migration
def self.up
create_table :ingredients_products,:id => false do |t|
t.integer :product_id
t.integer :ingredient_id
t.timestamps
end
end
def self.down
drop_table :products_ingredients
end
end
Now define the form in you ActiveAdmin resource, override the default
ActiveAdmin.register Product do
form do |f|
f.inputs "Details" do
f.input :product_name
f.input :brand
f.input :ingredients # don't forget this one!
end
end
I've been playing with ActiveAdmin for a while now, here's how I managed to get associations to work in Indexes and Forms.
I've just guessed some of your model columns below. Also note, in the form. The 'person' section will show all the columns for editing, whereas the 'course' section will just show the specified column.
ActiveAdmin.register User do
index do
column :id
column :name
column :attendance
column :person do |membership|
membership.person.name
end
column :course do |membership|
membership.course.name
end
default_actions
end
form do |f|
f.inputs "Membership" do
f.input :name
f.input :created_at
f.input :updated_at
end
f.inputs :name => "Person", :for => :person do |person|
person.inputs
end
f.inputs :name => "Course", :for => :course do |course|
course.input :name
end
f.buttons
end
end
I haven't tested this, but you should be able to apply these ideas to your case. It's working for mine.
Update: I've just read your question again and noted that you're wanting to be able to sort on the association column. I've just checked my implementation and this indeed is not working. My answer may be useless to you but I'll leave it here anyway (might help someone else).
I've just started using this gem myself, and while I haven't gotten around to showing association information, here's how you create a form for associations:
form do |f|
f.inputs
f.has_many :associations do |association|
association.inputs
end
f.buttons
end
That will give you a basic form with scaffolding.
ingredient.rb
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :products, :join_table => :ingredients_products
end
product.rb
class Product < ActiveRecord::Base
attr_accessible ingredient_ids
has_and_belongs_to_many :ingredients, :join_table => :ingredients_products
end
migration_xxx.rb
class CreateProductsIngredients < ActiveRecord::Migration
def self.up
create_table :ingredients_products,:id => false do |t|
t.integer :product_id
t.integer :ingredient_id
t.timestamps
end
end
def self.down
drop_table :products_ingredients
end
end
products.rb
ActiveAdmin.register Product do
form do |f|
f.inputs "Details" do
f.input :product_name
f.input :brand
f.input :ingredients
end
end
...
end
I get this error "WARNING: Can't mass-assign protected attributes: races_attributes"
, when following this http://railscasts.com/episodes/196-nested-model-form-part-1 on rails 3.
Where Races are a component of Events. This is my models/race.rb:
class Race < ActiveRecord::Base
belongs_to :event
attr_accessible :name, :unit
end
This is my models/event.rb:
class Event < ActiveRecord::Base
has_many :races, :dependent => :destroy
accepts_nested_attributes_for :races
attr_accessible :name, :date, :description, :location_name, :address_one, :address_two, :city, :state, :zip, :active, :races_attributes
end
Any Ideas?
Shorter than using attr_accessible, safer than using whitelist_attributes: attr_protected
Just indicate the protected attributes, and Rails will infer that all others can be mass-assigned:
class MyClass < ActiveRecord::Base
attr_protected :id
end
(I always have way more attributes that I want mass-assigned than the ones I want protected.)
attr_accessible specifies that you can not mass-assign attributes, using save method, for example. So, if you change an attribute that is not defined with attr_accessible, you will get a warning because it will not actually be saved in the database.