DataMapper many-to-many association - sql

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>]

Related

Aliasing a referenced relationship field in Mongoid

In the Mongoid model below, how do I alias the belongs_to relationship field?
class Contact
field :nm, :as => :name, :type => String # field aliasing
embeds_one :address, :store_as => :ad # embedded document aliasing
belongs_to :account # referenced relation doesn't support store_as
end
I want to store the account id in a field called ac instead of account_id.
You can use :foreign_key to specify the mongodb field name.
belongs_to :account, foreign_key: :ac
However, if you want to use account_id, you need to declare its alias:
alias :account_id :ac
or defining account_id before belongs_to:
field :account_id, as: :ac
Mongoid allows to use arbitrary name for a relationship by using of 'inverse_of'
If an inverse is not required, like a belongs_to or has_and_belongs_to_many, ensure that :inverse_of => nil is set on the
relation. If the inverse is needed, most likely the inverse cannot be
figured out from the names of the relations and you will need to
explicitly tell Mongoid on the relation what the inverse is.
So, for use 'ac' as an alias it's necessary to add inverse_of:
class Contact
field :nm, :as => :name, :type => String # field aliasing
embeds_one :address, :store_as => :ad # embedded document aliasing
belongs_to :ac, class_name: 'Account', inverse_of: :contact
end
class Account
has_one :contact, class_name: 'Contact', inverse_of: :ac
end

cant setup an active admin resource form with has many :through assoc

I'm working on a rails (3.7.8) app and using active admin to manage resources for the ff models:
class AdminUser < ActiveRecord::Base
has_many :user_article_categories, :include => :article_categories
has_many :article_categories, :through => :user_article_categories,
:source => :admin_user
has_many :articles, :through => :user_article_categories,
:source => :admin_user
# ...
end
class UserArticleCategory < ActiveRecord::Base
belongs_to :admin_user
belongs_to :article_category
attr_accessible :admin_user_id, :article_category_id, :included
attr_accessor :included
after_find :set_included
private
def set_included
self.included = "1"
end
# ...
end
the "included" attribute was based on a solution presented here
class ArticleCategory < ActiveRecord::Base
has_many :user_article_categories, :include => :admin_users
has_many :admin_users, :through => :user_article_categories,
:source => :article_category
has_many :articles, :through => :user_article_categories,
:source => :article_category
# ...
end
but I seem not to get setting up (correctly) a form for admin_users, such that creating a new admin_user would have all article_categories displayed as a list of checkboxes
while a persisted admin_user for update would have all article_categories checkboxes displayed but wit all previously set article-categories checked, so that an update would remove unchecked checkboxes and add newly checked ones to what goes to the join-table
for admin/admin_users.rb I create the form as follows, this does not work, though it renders correctly, any help will be appreciated
form do |f|
if f.object.persisted? and current_admin_user.id == f.object.id
f.inputs "Admin Details" do
f.input :email
f.inputs :for => user_article_categories do |usr_art_catr|
usr_art_catr.input :article_category_id, :hidden
usr_art_catr.input :included
end
end
else
f.inputs "Admin Details" do
f.input :email
f.input :superuser, :label => "Super User Priveleges"
f.input :article_categories, :as => :check_boxes,
:collection => ArticleCategory.select("id, name")
end
end
f.buttons
end
Actually, to display a list of checkboxes of all article_categories and check all already checked article categories for a given admin_user on update.
Formtastic, when rendering the show form for the form's object, calls a method provided on the form object via
f.input :method_to_be_called, :as => :checkboxes
which formtastic would compare its result with a collection provided via
the
:collection => any_valid_ruby_object
but both should return the same kinds; array/array or hash/hash, whatever, to determine which checkboxes should be checked, by performing a difference on the two collections.
The method called by formtastic could be an instance method on admin_user that queries the join-table, to determine which checkboxes should be checked and builds an array of that from the related article_categories table or returns an empty array when there is none.
This allows formtastic do what is right, as least in this context. This solution makes the "included" attribute on user_article_categories (the join-table) redundant!

Options for validates_with

I'm not able to access the values, passed as option in 'validates_with'
My model:
class Person < ActiveRecord::Base
include ActiveModel::Validations
attr_accessible :name, :uid
validates :name, :presence => "true"
validates :uid, :presence => "true"
validates_with IdValidator, :attr => :uid
My Custom Validator:
Class IdValidator < ActiveModel::Validator
def validate(record)
puts options[:attr]
...
...
end
end
For testing purpose, I'm printing "options[:attr]" and all I see is ":uid" in the terminal and not the value in it. Please help!
When you pass in :attr => :uid, you're just passing in a symbol. There's no magic happening hereā€”it just takes the hash of options you've attached and delivers it as the options hash. So when you write it, you see the symbol you've passed.
What you probably want is
Class IdValidator < ActiveModel::Validator
def validate(record)
puts record.uid
...
...
end
end
Because validates_with is a class method, you can't get the values of an individual record in the options hash. If you are interested in a more DRY version, you could try something like:
class IdValidator < ActiveModel::Validator
def validate(record)
puts record[options[:field]]
end
end
class Person < ActiveRecord::Base
include ActiveModel::Validations
attr_accessible :name, :uid
validates :name, :presence => "true"
validates :uid, :presence => "true"
validates_with IdValidator, :field => :uid
end
Where you pass in the name of the field you want evaluated.

One-To-Many-Through adding join model instances

My models:
class Test
include DataMapper::Resource
property :id, Serial
property :name, String, :default => ''
has n, :test_visits
has n, :visits, :through => :test_visits
# ...
end
class Visit
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :test_visits
has n, :tests, :through => :test_visits
# ...
end
class TestVisit
include DataMapper::Resource
property :result, String
belongs_to :test, :key => true
belongs_to :visit, :key => true
end
Why this code raises an SaveFailureError?:
#visit.test_visits.clear
#results.each do |test, result|
#visit.test_visits.new(:test => test, :result => result)
end
#visit.save
where variable #results is Hash (keys: Test, values: String)
It raises error because child objects are not saved. Try this:
#results.each do |test, result|
TestVisit.create(:visit => #visit, :test => test, :result => result)
end

DataMapper Association Migrations

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.