I am building a rails 6 app and I am stuck on an issue.
Let's say I have a table tasks which has 3 fields:
id
state
description
The description field is in fact a rich_text field thanks to ActionText.
My problem is that I have a csv file composed of 3 columns (id, state, description) and, during initialization of the app, I want to populate my database with it, using bulk import.
Normally, if description was a normal column, I would do something like this:
Task.insert_all(
# My csv converted in array of hashes [{state: YY, description: ZZZZZZZ}]
)
But as description is not really an attribute of the table tasks, it won't work. How can I still use bulk import to import large set of data, but still use action_text fields?
Right now, I am forced to use "one by one" insertions which takes a very long time!
Thank you for any leads you can bring.
I think the easiest way to do this is by splitting your input like this:
# Tasks
tasks = { id: XX, state: YY, ... }
# Descriptions
descriptions = { record_type: 'Task', record_id: XX, name: 'description', body: 'Actual body' }
Then you can do something like this:
Task.insert_all(tasks)
ActionText::RichText.insert_all(descriptions)
Is this what your are looking for?
========================================================
Update:
Just to clarify how this works, we need to understand that RichText model works as any other model in Rails, but serializing the information as showed here: https://github.com/rails/rails/blob/master/actiontext/app/models/action_text/rich_text.rb#L11
To see what is really being extracted from the database we can use the helper *field*_before_type_cast. For example:
descriptions = [ { record_type: 'Task', record_id: XX, name: 'description', body: '<p>EXAMPLE</p>' } ]
ActionText::RichText.insert_all(descriptions)
ActionText::RichText.last.body => <ActionText::Content....>
ActionText::RichText.last.body_before_type_cast => '<p>EXAMPLE</p>'
Also, keep in mind that you can have one rich text per record (is a 1 - N polymorphic association). So, if you try to insert a second description for your Task it won't work
Related
Let's say I have a User with attributes name and badge_number
For a JavaScript autocomplete field I want the user to be able to start typing the user's name and get a select list.
I'm using Materialize which offers the JS needed, I just need to provide it the data in this format:
data: { "Sarah Person": 13241, "Billiam Gregory": 54665, "Stephan Stevenston": 98332 }
This won't do:
User.select(:name, :badge_number) => { name: "Sarah Person", badge_number: 13241, ... }
And this feels repetitive, icky and redundant (and repetitive):
user_list = User.select(:name, :badge_number)
hsh = {}
user_list.each do |user|
hsh[user.name] = user.badge_number
end
hsh
...though it does give me my intended result, performance will suck over time.
Any better ways than this weird, slimy loop?
This will give the desired output
User.pluck(:name, :badge_number).to_h
Edit
Though above code is one liner, it still have loop internally. Offloading such loops to database may improve the performance when dealing with too many rows. But there is no database agnostic way to achieve this in active record. Follow this answer for achieving this in Postgres
If your RDBMS is Postgresql, you can use Postgresql function json_build_object for this specific case.
User.select("json_build_object(name, badge_number) as json_col")
.map(&:json_col)
The whole json can be build using Postgresql supplied functions too.
User.select("array_to_json(array_agg(json_build_object(name, badge_number))) as json_col")
.limit(1)[0]
.json_col
[EDIT] I am using the React and Rails differently and not using the react-rails gem...
Ok so i have my api written in Rails and it is formatted like this:
data: {
comments: [
{
comment: 'Lorem Ipsum',
user_id: 1
},
{
comment: 'dolor sit',
user_id: 2
},
{
comment: 'amet',
user_id: 3
}
]
}
now, in my react view, i want to have somewhat like, <%= User.find_by_id(params[:id]).name %>. What am I thinking is just to add posted_by attribute to table and add it as a key in api, having the string value of the user_id already set up with the controller(rails approach) and another one is to write another request(react approach) that will just fetch the name but i think its too much. What is the better approach?
In my opinion, it really depends on the numbers. You don't want to make, say, 15 requests if you have 15 comments on that page. My general proposing would be to keep this as close to JSON API as possible. However, if you want to stick to your current case, I would have two datasets:
1.Comments - which would be something like you have already
2.Users - Array of users, with all required fields (like name).
So that you would do only two requests initially.
Ok so this might be late. I just added a relationship between user and comments and added posted_by: Comment.user.name in the json response
I am practicing with "Rails: Up and Running" book. There is written that database fills id attributes by itself if not added. Here's an example.
some_items.yml:
one:
name: stringOne
two:
name: stringTwo
Ok. Now, I'm making migration and loading data with "rake db:migrate" and "rake db:fixtures:load".
Then I am trying to check what table "some_items" contains. While sending SQL query to SQLite3 console I'm receiving output like this:
298486374|stringTwo|2015-04-06 14:00:33|2015-04-06 14:00:33
980190962|stringOne|2015-04-06 14:00:33|2015-04-06 14:00:33
I am wondering is SQLite3 fills ID with random numbers? And if so, why not in normal ascending order like 1, 2, ...? And again - if so, why Rails doesn't create ID attribute in YAML testing files to show it in proper order?
I am wondering is SQLite3 fills ID with random numbers?
No, Rails is doing it.
And if so, why not in normal ascending order like 1, 2, ...?
I believe that Rails does it for fixture in order to be as much platform-independent as possible and keep it simple.
And again - if so, why Rails doesn't create ID attribute in YAML testing files to show it in proper order?
If you want to have sequencial IDs, you can have them in your fixtures files:
one:
id: 1
name: stringOne
two:
id: 2
name: stringTwo
I want to use search condition with included relations, just like below
Post.includes(:tags).where( tags: { title: '%token%' }).all
The posts and tags table has been associated with a 3rd table named post_tag_relations.
The schema is like below:
posts
id: pk
title: string
content: text
tags
id: pk
title: string
post_tag_relations
id: pk
tag_id: integer
post_id: integer
The syntax only works with equal condition, I really dont know how to use LIKE search condition.
When using Post.joins(:tags) and Tag.area_table[:title].matches('%token%') it will works fine, but some post that has no tags will not be fetch out.
Could anyone help me? Thanks a lot.
UPDATE:
The Rails version is 4.1.
I want to search the post like posts.title LIKE '%token%' OR tags.title LIKE '%token%', so if use Post.joins(:tags) will not be functional if some posts have no tags. So I need use Post.includes(:tags) instead.
UPDATED AGAIN:
looks cannot use one-query to fetch, so I had already try another database schema...
Why not do this:
Post.includes(:tags).where(Tag.arel_table[:title].matches('%token%').or(Tag.arel_table[:post_id].eq(nil)))
Since ruby-on-rails-2 the joins operation is used in all cases before the includes operation during performance, but since includes uses LEFT OUTER JOIN operator, you should use exactly it. May be you need also to use not LEFT, but FULL join. So try this with arel gem:
class Post
scope :with_token(token), -> do |token|
re = Regexp.union(token).to_s
cond = Arel.sql("title REGEXP ? OR content REGEXP ?", re, re)
includes(:tags).where(Tag.arel_table[:title].eq(token).or(cond))
end
end
Of course original condition could be replaced to use LIKE operator:
class Post
scope :with_token(token), -> do |token|
includes(:tags).where(arel_table[:title].matches("%#{token}%")
.or(arel_table[:content].matches("%#{token}%")
.or(Tag.arel_table[:title].eq(token))))
end
end
NOTE: If there are some errors, provide please result SQL.
Something like this:
Post.includes(:tags).where( "tags.title LIKE ?", "%#{token}%" )
could work.
(The syntax might be a little wrong, sorry, but you get the idea)
I have an unusual need for my application.
I want users to be able to set defaults that their users can extend to make their own options
Basically, users will customize their own control panels for use by their end users.
Example
John is my user, he creates two defaults:
name: 'Age'
value: 21
and
name: 'Subscribe to newsletter'
value: false
To be clear, this is being created by my user, John in my Rails application. I am not hardcoding the above data in my model. The default is the model and it has a name and a value
Now Suzie will see her own version of John's control panel with these two defaults and extend them with her own options
name: 'Age'
value: 18
and
name: 'Subscribe to newsletter'
value: false
I understand that ruby has its own native way to extend objects from classes, but creating a class from a record is hurting my brain. Is there an elegant way to do this?
I agree with the comment that you don't need to create any new classes.
Often, when dealing with default values, the behavior of Hash#merge is useful.
default_values.merge(user_values) will give you a single hash that contains all of the values, but where they conflict, will take those from the second hash. See Hash#merge.
Without knowing exactly what your database schema looks like, and assuming that john and suzie are User objects with a relationship to Defaults and Options, then it could be something like:
default_values = Hash[*john.defaults.map{ |d| [d.name, d.value] }.flatten]
user_values = Hash[*suzie.options.map{ |o| [o.name, o.value] }.flatten]
default_values.merge(user_values)