Is there a way to restrict what hstore columns can be saved? I've got below code for doing this so far:
store_accessor :widget_locations, :left_area1, :mid_area1, :left_area2, :mid_area2, :right_area2
but this seems to still allow other key names to be saved ie. middle_area123
Also how am I able to update hstore like update_attributes or update?
I could be wrong but my guess is that you are making calls on widget_locations like
item.widget_locations[:left_area1] = thing
If so, you should change that to
item.left_area1 = thing
because you told the store_accessor to create attributes :left_area1, :mid_area1, :left_area2, :mid_area2, :right_area2 that will be serialized to database column :widget_locations. Now these attributes will behave like normal attributes, so you can put validations on them etc.
This also allows you to update an item as usual:
item.update(name: 'Test', left_area: 'garden', mid_area: 'livingroom')
The catch with a hstore is that accessing the serialized column will allow you to add new unknown attributes, so it is best to directly access the attributes you explicitly specified.
Related
I'm using Mongoid in a Rails project (both 4.0.x), and I've got a document with a hash field that stores some schema-less data.
class Thing
field :name, type: String
field :mass, type: Integer
field :info, type: Hash
end
With this setup, I can query for things, say, that have a key :endDate like so:
Thing.where("info.endDate"=>{'$exists'=>true})
And that's all nice and handy. Using a hash field for this :info field is nice because what I want to store doesn't have a fixed schema and varies from one thing to another.
Ok, but, I can't use the same dot syntax to $set key/value pairs in the :info hash. [1]
thing.set("info.endDate"=>Time.now)
Raises a Mongoid::Errors::UnknownAttribute error.
It tells me I'd have to include Mongoid::Attributes::Dynamic in my model to do this, but that doesn't seem right to me. The point of the hash field type seems to be to allow you to work with data that doesn't have a fixed schema. It doesn't seem like I should have to include a special "dynamic attributes" module to use hash fields.
So right now, I'm updating values using regular old [] syntax, and then calling save on the model, like so:
thing.info[:endDate] = Time.now
thing.save
But a lot of the time it happens that it would be nicer to just $set the value. Is there some other syntax for setting hash field values? Am I wrong about the above error message and Dynamic Attributes being wrong-headed? Am I stuck doing two step updates to hash fields for now?
[1] admittedly, I've recently migrated from mongomapper, and so my expectations of this syntax are partly set by having been able to do this previously in mongomapper.
The thing with Hash field is, it can be dynamic as much as you want. Therefore to prevent polluting your DB schema with unintended fields caused by bugs in your code this functionality is disabled by default.
No you are not stuck using 2-step updates for your hashes at all!
[],[]= are the shortcuts for read_attribute() and write_attribute() and should be used if you don't include Mongoid::Attributes::Dynamic. If you try to use $set without enabling dynamic attributes you will get a no-method error because it does not see your dynamic attributes as defined attributes.
If you'll read the source of Mongoid::Attributes::Dynamic then you'd find that this is required to add the dynamic attributes functionality.
To update the values by including Mongoid::Attributes::Dynamic you need to follow these steps:
thing = Thing.first
thing.set("info.endDate" => Time.now)
thing.reload # This will update the current variable
Otherwise if you need you can easily skip this and do the value update by 2-step method
I hope this sheds some light over your query.
Source:
Rails mongoid dynamic fields - no method error
Dynamic attributes with Rails and Mongoid
I think you pass parameter in wrong way. Replace arrow symbol with comma
You can change to this and it will work
thing.set("info.endDate", Time.now)
There is phone attribute in customer model with our rails 3.2.12 app. We would like to remove the phone attribute sometime when retrieving customers. Here is what we did:
#customers = Customer.all
#customers.delete :phone
However there is error:
delete_all doesn't support limit scope
What's the right way to remove an attribute from a model object? Thanks.
You can use Customer.select('name, address') to retrieve only the fields you want. Noting that you pass a string with comma separated field named.
This will generate an SQL request like this
SELECT customer.name, customer.address FROM customer
You then get only the data you want without deleting it from the database (which is what your original call is trying to do).
My original response showed incorrect use of pluck, which only works for a single column.
I know that you are using Rails 3.2.12, but in Rails 4 pluck also works with multiple columns, allowing something like
Customer.pluck(:name, :address)
I have a Model representing a Pricing Table with lots of entries and i want to offer the possibility to create a new Pricing with values from an existing entry.
Does anyone know how to do this, then Sequel is in use?
I tried dup and clone but in both cases the id is still there from the existing Model and thus will update the existing entry.
If i try to set the id by hand i get following error:
Sequel::InvalidValue: nil/NULL is not allowed for the id column
So i need a to find a Way to create a Model which is new but has prefilled values without having them to set in the code by hand.
Any ideas?
found it:
new_pricing = Pricing.new(oldprice.attributes.tap{|attr| attr.delete("id")})
i get the attributes from the old model as hash, then remove the id and create a new Model by passing the attributes except the id.
The model.attributes solution didn't work for me. Sequel models have to_hash which is roughly equivalent, but to_hash doesn't return deserialized values. If you are using serializers (for jsonb fields, etc), simply passing a to_hash to new will fail because the values are not yet deserialized.
Here's the solution that works for me:
user = User.find(id: 123)
# freeze to avoid accidentally modifying the original user
user.freeze
# duplicate the record, deserialize values, and delete the primary key
# deserialization is useful if your model is using jsonb fields
record_copy = user.to_hash.merge(user.deserialized_values)
record_copy.delete(:id)
duplicate_user = User.new
# pass the has via `set_all` to avoid initialization callbacks
duplicate_user.set_all(record_copy)
# ... other important callbacks
duplicate_user.save
I am using Rails 3.2.8 to build a "product set" builder that mirrors Google Analytics' Custom Profile builder. For example, a user may define a product set as follows:
(Category = 'Printers') and ((Name contains 'Wireless') or (Name contains 'Wifi'))
My product data is stored in Postgres (9.1.4) using an HStore column to store the dynamic product attributes. I have built a form that can construct the query using Arel but am stuck on the following requirements:
1. The query must be serialized to the database. I can store the .to_sql string but am then stuck with...
2. I must be able to reconstruct the user's form for later editing, as these are not one-time searches but rather shared queries.
How can I serialize in such a way that I can easily reconstruct the user defined query?
Couldn't you just serialize the parameters that rails gets from the form?
You'd have a hash with the keys and values from the user inputted form, and could easily feed it back into whatever logic you use to query the database from said form, or further process it.
You can have persistent model for saving the user's options about the search.
When you need to run the query, you will get the user's saved options and will pass them to custom method that use ransacker. You can access values in the hstore like this:
Arel::Node::InfixOperator.new('->>', 'hstore_column_name', Arel::Nodes::Quoted.new('key_in_hstore'))
Is there any gem/plugin for ruby on rails which gives the ability to define custom fields in a model at runtime with no need to change the model itself for every different field.
I'm looking for something like Redmine acts_as_customizable plugin which is packaged as a gem usable in the rails way, i.e.
gem 'gemname'
rails g something
rails db:migrate
class Model < ActiveRecord::Base
acts_as_something
end
Here are the CustomField and the CustomValue classes used in Redmine.
Edit:
Since my question is not clear I add a brief use case which explains my need better:
I want users to be able to design their own forms, and collect data
submitted on those forms. An important decision is the design of how
these custom dynamic records are stored and accessed.
Taken from here, in this article approach the problem with different ideas, but they all have drawbacks. For this reason I'm asking if the issue has been approached in some gem with no need to rethink the whole problem.
I'm not aware of a gem that does this, but serialize works quite well and it's a built-in. You get a NoSQL-ish document store backed by JSON/YAML.
If you allow user to create a custom form, you can pass nested arrays et cetera directly into the attribute. However, if you need to validate the structure, you're on your own.
I'm afraid it could be tricky and complicated to do it in ActiveRecoand (generally in standard relational database). Take a look at http://mongoid.org/docs/documents/dynamic.html - this mechanism is using nosql feature.
You can also may try the following trick:
1/ Serialize a hash with your custom fields in the database column, for example { :foo => 'bar', :fiz => 'biz' }
2/ After load a record from database do some metaprogramming and define corresponding methods on the record's singleton class, for instance (assume that custom fields are stored and serialized in custom_fields column):
after_initialize :define_custom_methods
# ..or other the most convinient callback
def define_custom_methods
# this trick will open record's singleton class
singleton_class = (class << self; self; end)
# iterate through custom values and define dynamic methods
custom_fields.each_with_key do |key, value|
singleton_class.send(:define_method, key) do
value
end
end
end
Since rails 3.2 you can use store method. Just include following in your model:
store :properties, accessors: [:property1, :property2, :property3...]
You only need to change your model once (to add properties field to db table). You can add more properties later without altering the schema.
The way this works is by serializing properties hash into YAML and saving it into database. It it suitable for most cases, but not if you'd like to use these values in db queries later.
I don't know a gem, but this can be accomplished be creating a table called custom_fields with a name column and possibly a datatype column if you wanted to restrict fields by datatype.
Then you create a join table for a custom field to your desired table and a value and do whatever validations you want.