How to set a foreign key in rails? - mysql

I'm trying to save a object that has a foreign key attribute. I do not understand why it's not working. The foreign key is defined as not null in the database.
class Store < ActiveRecord::Base
attr_accessible :latitude, :longitude, :description
validates :store_group, :presence => true
validates :description, :presence => true, :length => {:maximum => 500}
validates :latitude, :presence => true
validates :longitude, :presence => true
belongs_to :store_group
end
class StoreGroup < ActiveRecord::Base
attr_accessible :name, :description, :image
validates :name, :presence => { :message => "Store group can not be empty" }
end
So, I'm trying to save a store:
group = StoreGroup.new(:name=>"name",:description=>"description",:image=>"image")
store = Store.new(:store_group=>group,:latitude=>1,:longitude=>1,:description=>"description")
store.save
However, MySQL raises an exception:
Mysql2::Error: Column 'store_group' cannot be null: INSERT INTO `stores` (`created_at`, `store_group`, `description`, `latitude`, `longitude`, `updated_at`) VALUES ('2013-02-17 04:09:15', NULL, 'description', 1.0, 1.0, '2013-02-17 04:09:15')
Why?
Thanks in advance :)

You are trying to create/save store_group object through store. Thus use:
accepts_nested_attributes_for :store_group
in your Store model
Read here about accepts_nested_attributes_for

First off, it may be easier in the long run if you add a has_many :stores to StoreGroup, for instance if you ever want to retrieve all of the stores that belong to a particular StoreGroup. Secondly, you should add the store via its StoreGroup, and since you already have an association there it's fairly straightforward (note the change to Store.create):
group = StoreGroup.create(name: "name", description: "description", image: "image")
group.stores << Store.create(lat: 1, long: 1, desc: "description")
This method will automatically set :store_group_id and save the new Store instance as a "child" of its StoreGroup. Note that you'll also want to change your code to account for existing StoreGroups, so that you can add stores to existing StoreGroups later. Using .first_or_create with a .where(...) clause is idiomatic for rails 3.2.x, though there are dynamic finders with create powers in previous versions of Rails (find_or_create_by_name_and_store_group_id, as an example).
Lastly, remove validates :store_group because association validations don't work that way. If you really must, use validates :store_group_id.

Related

How to validate uniqueness of field based on another table

I want to validate roll_no field in students model based on student_section model field section,
student_section.rb
class StudentSection < ActiveRecord::Base
validates :standard_id, :presence=> {:message=>" cannot be blank"}
validates :section_id, :presence=> {:message=>" cannot be blank"}
validates :student_id, :presence=> {:message=>" cannot be blank"}
end
I add validation in student.rb as
class Student < ActiveRecord::Base
validates :student_id, :presence=> true
validates :student_name, :presence=> true
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
validates :phone, :length=>{:in => 8..15}
validates :admission_no, :uniqueness=> { scope: :org_id}
validates :roll_no, :uniqueness=> { scope: #student_section.section_id}
end
but it throws unknown field section_id
First, you should make a relationship between Student and StudentSection. If I understand you correctly, you may create a one-to-many relation.
After that your validation should work fine.
However, if you will search for more complex validation logic - you may use validates_with statement (validates_with at apidock.com)

validating uniqueness within a has_many through

I have a User that can have many Restaurants. I can also have multiple users.
I'd like to have it so that if User A creates Restaurant A, he should NOT be able to make another restaurant with the same name.
However, if User B goes to create Restaurant A, that should be allowed but still cannot make another Restaurant A afterwards.
I have the following has_many through relationship:
restaurant.rb
has_many :ownerships
has_many :users, :through => :ownerships
# This following ensures uniqueness of the name within the
# Restaurants table regardless of who the User is that created it.
validates :name, presence: true, uniqueness: true
user.rb
has_many :ownerships
has_many :restaurants, :through => :ownerships
ownership.rb
belongs_to :restaurant
belongs_to :user
What I've Tried
1. Adding :uniqu => true
I've tried adding :uniq => true to the restaurant.rb file so it looks like this:
has_many :ownerships
has_many :users, :through => :ownerships, :uniq => true
And removing uniqueness: true from the validation so it looks like this:
validates :name, presence: true
But that doesn't do anything useful.
2. Adding validation within ownership.rb
I've tried adding the validation to the ownership.rb file as such:
validates :restaurant, uniqueness: {:scope => :user}
But I get:
NoMethodError in RestaurantsController#create
undefined method `text?' for nil:NilClass
And I can't seem to tell it to look for the restaurant name within the scope of user either within this validation.
3. Creating before_create callback function
In my restaurant.rb file, I declared the following:
before_create :check_uniqueness
def check_uniqueness?
user = User.find_by_id(self.user_ids)
isUnique = false
user.restaurants.each do |restaurant|
if !Restaurant.find_by_name(self.name).nil? # Restaurant w/ same now found
isUnique = false
else
isUnique = true
end
return isUnique
end
end
My assumption is that before the restaurant record is created, it'll do this check_uniqueness check and if the function returns false, it'll not save.
But I'm getting the following error when I hit the submit button:
NameError in RestaurantsController#create
undefined local variable or method `check_uniqueness' for #<Restaurant:0x007f95a16d10f8>
Working Solution
Thanks to Robert Chuchro's help below, I was able to get the validation to work. Here's what I did:
restaurant.rb
before_create :unique_per_user?
def unique_per_user?
user = User.find_by_id(self.user_ids)
restaurant = user.restaurants.find(:all, :conditions => ["name = ?", self.name])
if restaurant.size > 0
self.errors.add(:name, ": You've already created a restaurant with this name.")
end
return (restaurant.size <= 0)
end
You can try to define a method to do this in your restaurant model
def unique_per_user?
#get user trying to create restaurant, either by paramter or association
#check if any of the user's current restaurant names match this name (return true/false)
end
now whereever you define a new restaurant check if its unique_per_user? before deciding to save it.

Rails 3.1 Qeurying the database encrypted with attr_encrypted gem (where clause on encrypted fields)

I have encrypted a field in the table using the attr_encrypted gem. Now I want to query on that particular field comparing it with a value I am retrieving from a form. How can I do this?
EDIT : I need to query on a number of encrypted fields. Eg: searching on encrypted_email, encrypted_name etc. (using OR condition in where clause)
attr_encrypted intercepts find_by methods, so you should be able to do this:
class User < ActiveRecord::Base
attr_encrypted :email, :key => 'a secret key'
attr_encrypted :password, :key => 'some other secret key'
end
User.find_by_email_and_password('test#example.com', 'testing')
This is rewritten as
User.find_by_encrypted_email_and_encrypted_password('ENCRYPTED EMAIL', 'ENCRYPTED PASSWORD')
class User < ActiveRecord::Base
attr_encrypted :email, :key => 'a secret key'
end
If you want to write a query to retrieve user whose email is 'abc#xyz.com' then you can do either
User.where(encrypted_email: User.encrypt_email('abc#xyz.com'))
or
User.scoped_by_email('abc#xyz.com') # works only for dynamic scope methods

:include searches with the wrong value

I have a number of models in a Rails project that are linked to a user, and I'm running into a loading problem I try to get all of the user's data displayed on a page.
My model declarations look sort of like this:
class User < ActiveRecord::Base
validates_presence_of :server_user_id
has_many :setup_notes, :foreign_key => "server_user_id"
has_many :closure_votes, :foreign_key => "user_id"
end
class SetupNote < ActiveRecord::Base
belongs_to :user, :foreign_key => "server_user_id"
end
I should note that in the SQL table for closure votes, user_id is the same value as server_user_id in the table for users.
When I try using the :include symbol in a Rails query, it ends up using user.id for the search value when I need to find closure votes and setup notes through user.server_user_id. In other words,
me = User.first(:conditions => ["server_user_id = ?", 12610], :include => :setup_notes)
generates the MySQL query
SELECT `setup_notes`.* FROM `setup_notes` WHERE (`setup_notes`.server_user_id = 1)
Which returns an empty set. Is there any way to format the Rails query/models to send the relevant SQL query, or do I need to write a raw query for all of the models associated with the users (and if that's the case, how do I do that?)
Thanks!
If I understand correctly (that User has a server_user_id field), in the User model you have to set the :primary_key option like so:
class User < ActiveRecord::Base
validates_presence_of :server_user_id
has_many :setup_notes, :foreign_key => "server_user_id", :primary_key => "server_user_id"
has_many :closure_votes, :foreign_key => "user_id", :primary_key => "server_user_id"
end

Datamapper Many-To-Many relation

Article has many authors, editors, translaters, etc. All of class Person.
Is it possible to generate join model? Or, in this case, solution is to create each join models manually.
class Article
include DataMapper::Resource
property :id, Serial
property :published_date, Date
property :status, Enum[ :pending, :accepted, :declined, :modified, :new ], :default => :new
property :visible, Boolean, :default => false
timestamps :at
has n, :authors, 'Person', :through => Resource
end
class Person
include DataMapper::Resource
property :id, Serial
property :name, String
property :about, Text
property :gender, Enum[ :male, :female ]
property :birthday, Date
timestamps :at
has n, :articles, :through => Resource
end
looks like it impossible. Only manual model creation.