Manually set value for primary key when saving data in rails - ruby-on-rails

In my rails app, i am using a legacy database.
class Expression < ActiveRecord::Base
set_table_name "EXPRESSION"
set_primary_key "EXP_ID"
belongs_to :sub, :foreign_key => "EXP_SUB_FK"
end
To save an entry in the 'EXPRESSION' table, i am using the following code in my controller method:
#expression = Expression.create(
:EXP_ID => 7,
:EXP_SUB_FK => 99991886,
:EXP_STRENGTH => 'strong',
:EXP_ADDITIONAL_STRENGTH => 'intense',
:EXP_COMPONENT_ID => 43444
)
I have to manually set the EXP_ID each time i save an entry (i will get the id from another table), but the above code does not save the EXP_ID. All the other values are saved except for the EXP_ID.
If i comment out 'set_primary_key "EXP_ID"' in the Expression model, it works but i need to define EXP_ID as primary key.
Is there a way of allocating a value for a primary key when saving an entry to the dbase?
I would be grateful if anyone can provide me with some hint.

Set the EXP_ID in a before_save filter defined in the the Expression model.
UPDATE:
Added Sample:
before_save :set_exp_id
def set_exp_id
self.exp_id = 5555555
end

Related

RoR Method to search for data in a table and then update another column on another table based on that data

I have a program i am making changes to for my work. I know what i must do but i am having problems getting the code to work. I come from a Java and C background.
i have two tables one table called customprojecschedule_lines has a project_id,workorder_base_id, and other various columns column.
The other table called customschedule has an id, workorder column and various other columns.
I have a method and variable called work order.
I am trying to get an SQL statement that will do that like this:
class Customschedule < ActiveRecord::Base
set_table_name "customschedules"
after_create :build_customprojectschedule_lines
has_many :customprojectschedule_lines, :dependent => :destroy
has_one :projectschedule_cost
delegate :est_cost, :act_cost, :to => :projectschedule_cost, :allow_nil => true
attr_accessor :workorder_base, :lots
def workorder
customschedule.where(:id => customprojectschedule_lines.product_id)
end
def workorder=(wo)
#workorder_base = wo
customprojectschedule_lines.each do |pl|
pl.update_attributes({:workorder_base_id => wo})
end
end
def build_customprojectschedule_lines
lines = #lots.split(',').inject([]) do |lines, lot_id|
line = customprojectschedule_lines.find_or_initialize_by_workorder_lot_id(lot_id)
if line.new_record?
p workorder_base
line.workorder_base_id = #workorder_base
line.line_no = lot_id
line.workorder_split_id = 0
end
lines << line
end
customprojectschedule_lines.replace(lines)
end
Basically what i would like is that whenever a user enters a workorder on the form number goes into the database gets the stored values with that record and then retrieves the ID(from that record) and puts that same id in my other table.
However, i keep getting this error:
undefined local variable or method `customschedule' for #
<Customschedule:0x00000005542040>
Before when i was trying things i kept getting a weird looking select statement saying that Customschedule ID was null.
We use oracle here.
This is my first time posting on here so please let me know if i need anything else.
Any help is greatly appreciated.
I think all you need is to upcase the first letter in this line
customschedule.where(:id => customprojectschedule_lines.product_id)
change it to
Customschedule.where(:id => customprojectschedule_lines.product_id)

Hash with 'nil' value when using `group` and `count` in query

In Rails, I get a hash using includes:
<% #teste = UserProfile.includes(:mobile_models).group(:name).count %>
The problem is that includes generates a hash like the following:
{nil=>4774, "2610"=>7, "2626"=>4, "2630"=>5, "2760"=>4, "3250"=>3, "355"=>5, "3I607 BlackJack"=>5, "5230"=>13, "5235"=>4, "5310"=>5, "5500"=>5, "5800 Xpress Music"=>16, "6020"=>4, "6120c"=>4, "6131"=>4, "7210"=>5, "A1200r"=>5, "A1900"=>5, "AIKO 70"=>5, "B3410W Ch#t"=>4, "beTouch E100"=>4, "BlackBerry 8320 (Curve)"=>10,....
In my database, I don't find any mobile record with the name "nil". Checking my database, I can't find what might be producing this nil.
The other goal is to sum all values, like this:
<%= sum = #teste.values.sum %>
But when I do this, the 'nil' is added too.
---Update
models/UserProfile
class UserProfile < ActiveRecord::Base
has_and_belongs_to_many :mobile_models, join_table: 'user_profiles_mobile_models', order: 'name'
models/MobileModel
class MobileModel < ActiveRecord::Base
belongs_to :mobile_maker
Because you are grouping by :name, some of the MobileModel or UserProfile objects have the name attribute set to nil. You will need to check both as without seeing the model definition, I can't tell which model has the :name property you are grouping on. If you can share the model code, I can be more explicit.
If both models have a name attribute, you can be more explicit in your group statement:
UserProfile.includes(:mobile_models).group('mobile_models.name')
or...
UserProfile.includes(:mobile_models).group('user_profiles.name')
Also, if a number of your users do not have any mobile_models to include, I believe they will get dumped into the nil grouping as well.
You are getting that hash because of group(:name).
That means you have 4774 records who's name is nil.

How do I keep has_many :through relationships when serializing to JSON and back in Rails 4.0.3?

How do I convert to JSON and back and keep the relationships? It thinks they don't exist when I un-parcel the object!
irb(main):106:0* p = Post.last
=> #<Post ...
irb(main):107:0> p.tags
=> #<ActiveRecord::Associations::CollectionProxy [#<Tag id: 41, ...
irb(main):109:0* p.tags.count
=> 2 #### !!!!!!!!!!!!
irb(main):110:0> json = p.to_json
=> "{\"id\":113,\"title\":... }"
irb(main):111:0> p2 = Post.new( JSON.parse(json) )
=> #<Post id: 113, title: ...
irb(main):112:0> p2.tags
=> #<ActiveRecord::Associations::CollectionProxy []>
irb(main):113:0> p2.tags.count
=> 0 #### !!!!!!!!!!!!
Here is the model
class Post < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
What someone suggested, but doesn't work
irb(main):206:0* Post.new.from_json p.to_json(include: :tags)
ActiveRecord::AssociationTypeMismatch: Tag(#60747984) expected, got Hash(#15487524)
I simulated the exact same scenario like yours and found out:
Whenever a model(Post) has a has_many through association then upon creating an instance of that Model i.e., Post passing a Hash for eg: Post.new( JSON.parse(json) ) or Post.new(id: 113) seems like Rails treats them differently although they are pointing to the same record.
I ran the following commands in the sequence as given below:
p = Post.last
p.tags
p.tags.count
json = p.to_json
p2 = Post.new( JSON.parse(json) )
p2.tags
p2.tags.count ## Gives incorrect count
p3 = Post.find(JSON.parse(json)["id"]) ### See notes below
p3.tags
p3.tags.count ## Gives the correct count
Instead of creating a new instance of Post using Hash directly, I fetched the record from database using the id obtained from deserializing json. In this case, the instance p3 and instance p2 refer to the same Post but Rails is interpreting them differently.
Disclaimer: This is not, in any way, an ideal solution (and I would call it down-right cheesy), but its about the only thing I've been able to come up with for your scenario.
What Kirti Thorat said is correct; when you have a dependent object, Rails expects the association in the hash to be of that specific class (in your case, a Tag object). Hence the error you're getting: Tag expected...got Hash.
Here comes the cheesy part: One way to properly deserialize a complex object is to leverage the accepts_nested_attributes_for method. By using this method, you'll allow your Post class to properly deserialize the dependent Tag key-value pairs to proper Tag objects. Start with this:
class Post < ActiveRecord::Base
accepts_nested_attributes_for :tags
# rest of class
end
Since accepts_nested_attributes_for searches for a key with the word _attributes for the given association, you'll have to alter the JSON when it is rendered to accommodate this by overriding the as_json method in your Post class, like so:
def as_json(options={})
json_hash = super.as_json(options)
unless json_hash["tags"].nil?
json_hash["tags_attributes"] = json_hash["tags"] # Renaming the key
json_hash.delete("tags") # remove the now unnecessary "tags" key
end
json_hash # don't forget to return this at the end
end
Side note: There are lots of json building gems such as acts_as_api that will allow you to remove this as_json overriding business
So now your rendered JSON has all the Post attributes, plus an array of tag attribute key-value pairs under the key tags_attributes.
Technically speaking, if you were to deserialize this rendered JSON in the manner suggested by Kirti, it would work and you would get back a properly populated active record object. However, unfortunately, the presence of the id attributes in both the parent Post object, and the dependent tag objects means that active record will fire off at least one SQL query. It will do a quick lookup for the tags to determine if anything needs to be added or deleted, as per the specifications of the has_many relationship (specifically, the collection=objects part).
Since you said you'd like to avoid hitting the database, the only solution I've been able to find is to render to JSON in the same way leesungchul suggested, but specifically excluding the id fields:
p_json = p.to_json(except: [:id], include: {tags: {except: :id}})
If you then do:
p2 = Post.new(JSON.parse(p_json))
You should get back a fully rendered Post object without any DB calls.
This, of course, assumes you don't need those id fields. In the event you do...frankly I'm not certain of a better solution other than to rename the id fields in the as_json method.
Also note: With this method, because of the lack of id fields, you won't be able to use p2.tags.count; it will return zero. You'll have to use .length instead.
You can try
p2.as_json( :include => :tags )
When you call
p2.tags
you get correct tags but p2 is not saved in the database yet. This seems the reason for
p2.tags.count
giving a 0 all the time.
If you actually do something like:
p2.id = Post.maximum(:id) + 1
p2.tags #Edit: This needs to be done to fetch the tags mapped to p from the database.
p2.save
p2.tags.count
You get the correct count

mongoid batch update

I'd like to update a massive set of document on an hourly basis.
Here's the
fairly simple Model:
class Article
include Mongoid::Document
field :article_nr, :type => Integer
field :vendor_nr, :type => Integer
field :description, :type => String
field :ean
field :stock
field :ordered
field :eta
so every hour i get a fresh stock list, where :stock,:ordered and :eta "might" have changed
and i need to update them all.
Edit:
the stocklist contains just
:article_nr, :stock, :ordered, :eta
wich i parse to a hash
In SQL i would have taken the route to foreign keying the article_nr to a "stock" table, dropping the whole stock table, and running a "collection.insert" or something alike
But that approach seems not to work with mongoid.
Any hints? i can't get my head around collection.update
and changing the foreign key on belongs_to and has_one seems not to work
(tried it, but then Article.first.stock was nil)
But there has to be a faster way than iterating over the stocklist array of hashes and doing
something like
Article.where( :article_nr => stocklist['article_nr']).update( stock: stocklist['stock'], eta: stocklist['eta'],orderd: stocklist['ordered'])
UPDATING
You can atomically update multiple documents in the database via a criteria using Criteria#update_all. This will perform an atomic $set on all the attributes passed to the method.
# Update all people with last name Oldman with new first name.
Person.where(last_name: "Oldman").update_all(
first_name: "Pappa Gary"
)
Now I can understood a bit more. You can try to do something like that, assuming that your article nr is uniq.
class Article
include Mongoid::Document
field :article_nr
field :name
key :article_nr
has_many :stocks
end
class Stock
include Mongoid::Document
field :article_id
field :eta
field :ordered
belongs_to :article
end
Then you when you create stock:
Stock.create(:article_id => "123", :eta => "200")
Then it will automaticly get assign to article with article_nr => "123"
So you can always call last stock.
my_article.stocks.last
If you want to more precise you add field :article_nr in Stock, and then :after_save make new_stock.article_id = new_stock.article_nr
This way you don't have to do any updates, just create new stocks and they always will be put to correct Article on insert and you be able to get latest one.
If you can extract just the stock information into a separate collection (perhaps with a has_one relationship in your Article), then you can use mongoimport with the --upsertFields option, using article_nr as your upsertField. See http://www.mongodb.org/display/DOCS/Import+Export+Tools.

Rails: Serialize value as comma-separated and not as YAML

I'm looking for a way to store a serialized value of eg. IDs in a column. In before claims that this is not an optimal design: the column is used for IDs of associated records, but will only be used when displaying the record - so no queries are made with selection on the column and no joins will be made on this column either.
In Rails I can serialize the column by using:
class Activity
serialize :data
end
This encodes the column as YAML. For legacy sake and since I'm only storing one dimensional arrays containing only integers, I find it more suitable to store it as a comma-separated value.
I've successfully implemented basic accessors like this:
def data=(ids)
ids = ids.join(",") if ids.is_a?(Array)
write_attribute(:data, ids)
end
def data
(read_attribute(:data) || "").split(",")
end
This works pretty fine. However I'd like to add array-like methods to this attribute:
activity = Activity.first
activity.data << 42
...
How would I do this?
You can do it with composed_of feature as explained in this post.
It should be something like:
composed_of :data, :class_name => 'Array', :mapping => %w(data to_csv),
:constructor => Proc.new {|column| column.to_csv},
:converter => Proc.new {|column| column.to_csv}
after_validation do |u|
u.data = u.data if u.data.dirty? # Force to serialize
end
Haven't tested it though.
You can use serialize with a custom coder in rails 3.1.
See my answer to this question. :-)

Resources