How Could I store integers (user id's ranging from 1 to 9999) serialized in a database column and retrieve them back?
In my User model I have invites column,
User model
serialize: invites
invites = text field
Now I'm trying to do 2 things:
Append the user_id integer (from 1 to 9999) serialized in a column "invites"
Retrieve all the user id's back from the User.invited column ( deserialize it ? )
From the fine manual:
serialize(attr_name, class_name = Object)
If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object, then specify the name of that attribute using this method and it will be handled automatically. The serialization is done through YAML. If class_name is specified, the serialized object must be of that class on retrieval or SerializationTypeMismatch will be raised.
So, if you want to store an array of integers as a serialized object, then:
class User < ActiveRecord::Base
serialize :invites, Array
#...
end
You'd want the invites column to be a text column in the database (not string!) to avoid running into size issues.
Then you can treat user.invites as a plain Array:
user.invites = [ 1, 2, 3 ]
user.invites.push(11)
That of course doesn't verify that that numbers are valid or that you don't have duplicates (but you could use a Set instead of an Array for that), it also won't prevent you from putting a string in there.
I don't recommend that you do this though, serialization is almost always a mistake that will come back to bite you later. A serialized column is an opaque blob of data as far as the database is concerned: you can't update it in-place, you can't query it, all you can do is pull it out of the database and put it back. serialize uses YAML for serialization and that's an awful format if you need to work with your serialized data inside the database; you can also run into interesting encoding issues during upgrades.
You're better off setting up a traditional association table and a separate model (possibly using has_many ... :through =>) to handle this situation.
Related
I am working on a model in rails, which has a column type that can have more than one values. In other words, as the users enter data in its database, new values might also be entered for that column. So it is like a one-to-many association. However, since the number of values will be very few (maybe less than 10), I did not want to create a separate model for it. By mistake, I had defined it as a string, but now I want to make it an array of strings, which can grow. How can I do that?
Thanks in advance!
What you're looking for is the serialize method.
Simply call serialize :attribute_name, Array in your model.
How do I save multiple values in a single cell record in Ruby on Rails applications?
If I have a table named Exp with columns named: Education, Experience, and Skill, what is the best practice if I want users to store multiple values such as: education institutions or skills in a single row?
I'd like to have users use multiple text fields, but should go into same cell record.
For instance if user has multiple skills, those skills should be in one cell? Would this be best or would it be better if I created a new table for just skills?
Please advise,
Thanks
I would not recommend storing multiple values in the same database column. It would make querying very difficult. For example, if you wanted to look for all the users with a particular skill set, the query would clumsy both on readability and performance.
However, there are still certain cases where it makes sense.
When you want to allow for variable list of data points
You are not going to query the data based on one of the values in the list
ActiveRecord has built-in support for this. You can store Hash or Array in a database column.
Just mark the columns as Text
rails g model Exp experience:text education:text skill:text
Next, serialize the columns in your Model code
class Exp < ActiveRecord::Base
serialize :experience, :education, :skill
# other model code
end
Now, you can just save the Hash or Array in the database field!
Exp.new(:skill => ['Cooking', 'Singing', 'Dancing'])
You can do it using a serialized list in a single column (comma-separated), but a really bad idea, read these answers for reasoning:
Is storing a delimited list in a database column really that bad?
How to store a list in a column of a database table
I suggest changing your schema to have a one to many relationship between users and skills.
Rails 4 and PostgreSQL comes with hstore support out of the box, more info here In rails 3 you can use gem to enable it.
It depends on what kind of functionality you want. If you want to bind the Exp model attributes with a form (for new and update operations) and put some validations on them, it is always better to keep it in a separate table. On the other hand, if these are just attributes, which you just need in database keep them in a single column. There is way by which you can keep the serialized object like arrays and hashes in database columns. Make them a array/hash as per your need and save it like this.
http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize
Serialized attributes, automatically deserializes when they are pulled out of tables and serialized automatically when saved.
In rails-
I need to execute a sql query against the database; the query is not related to any specific Model it can have mix data from multiple tables or from some other table. I have ways to do this=-
first is by executing query agains Model and capture the result like this-
res=User.find_by_sql("select * from customers joins and conditions")
res=User.find_by_sql("select * from [other table] joins and conditions")
Problem with this approach, I am not feeling comfortable with it because in User class objects I am capturing data of other table. like the first query result has data from customer table so in the user object I got customer's attribute. And more interesting problem is- if the resulting query has id attribute then
res.first.id will be id of customer and
if User model has relation with UserRoles model and if I access this relation with res.first.roles then it will fetch roles from UserRole for customer id, which is completely wrong.
And there might be may problems also.
So I think it has lot of chaos.
And good part is we dont not need to deal with connection and result would be an array of objects. so accessing object attributes with res.first.id is easier the hast like row["id"].
and second approach to use ActiveRecord connection and execute the query like
this res = ActiveRecord::Base.connection.query("sql query")
in place of query we can use select_one, select_all and can also make query parameterized.
The problem it has is it returns array of hash, but I need array of objects for easy accessibility in code. So I wrote a class to convert hash to object (I think rail does same thing in background) and is working fine.
So I need some suggestion on both the approach and need to decide which one is better.
First find_by_sql vs select_all
find_by_sql, This method returns an array of objects by initiating them.
users = User.find_by_sql("SELECT * FROM users"); #=> [#, #, #, # ....]
Accessing properties
users[0].name #Getting property in object oriented fashion
select_all, This method returns an array of objects but does not initiate them, and each object represents a row of database.
users = User.connection.select_all("SELECT * FROM users"); #=> #
Accessing properties
users[0]["name"] #Getting property in non-object oriented fashion
Whether find_by_sql is better, because of it is a simple way of custom querying to the database and returns instantiated objects
There might be a group of records in a table where only two fields vary record by record, and rest fields remains same. This calls for a normalization by splitting by a table through a foreign key association. But, in Ruby-on-Rails, it would mean the creation of a model. So, is it still possible to lessen use of disk space?
May be, it is, because it would be reasonable that storing multiple values of one column in a record would require the column to be an array of any type. But declaring the field to be :array type results in an error. So, is there a way to work around it?
After generating a model, open the model's file. Insert one line for each field.
serialize :field_name
But ensure that the fields for which you are serializing, should be of type
:text
or
:string
If they aren't of such primitive data types, i.e. of another type like
:datetime
then it would return an error.
This step is not complete as a whole. You need to do one complementing step: de-serialize, because in the model-level storage, it is stored as a string starting with "---\n-", which is not suitable for array-type operations.
While reading data from the model, you need to perform the following step:
YAML.load(field_name)
where field_name refers to the field that was serialized.
The above step would return an array, on which you can perform normal array operations.
Rails newbie here so just want to be sure I'm doing this right. I have a couple of complex relationships and would like to cache those relationships locally in the mysql row. Think a feature like facebook likes. I have this information currently in a mysql table. I was going to put in a "liked_ids" column that is text and stored as json. And then have an accessor like:
def likes
str=self.liked_ids
b=JSON.parse(str)
return b
end
I have seen some people mention storing as YAML instead but there's a ton of json already being used.
And then when someone submits to the main table (say likes_users), we just add a callback that updates this json field.
Are there alternatives or does this seem like a reasonable idea?
thx
Why not use a table and separate model?
User likes Post
users_posts ---> user_id, post_id
Class user has and belongs to many likes (class override to Post)
I wouldn't store relationship data as a field when you are using a relational database.
You can use the serialize method to define a serialization column.
The serialization is done through YAML.
class User < ActiveRecord::Base
serialize :liked_ids, Array
end
user = User.create(:liked_ids => [1, 2, 3])
User.find(user.id).liked_ids # => [1, 2, 3]
All there's left is to keep the column in sync via callbacks.