Ruby on rails - Sort data with SQL/ActiveRecord instead of ruby - sql

I'm working with two tables Video and Picture and I would like to regroup them using SQL instead of ruby. This is how I do it now :
#medias = (Video.all + Picture.all).sort_by { |model| model.created_at }
Is their a way to do the same thing only with SQL/ActiveRecord?

Since you don’t have the same columns in each model you could create a polymorphic relationship with a new model called media. Your Videos and Pictures would be associated with this new model and when you need to work on only your media you don’t need to worry about whether it is a video or a picture. I’m not sure if this fits into your schema and design since there is not much info to go on from your post but this might work if you wanted to take the time to restructure your schema. This would allow you to use the query interface to access media. See the Rails Guide here:
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
You can create a media model with all the fields need to satisfy a Video or Picture object. The media model will also have a type field to keep track of what kind of media it is: Video or Picture.

Yes, using ActiveRecord's #order:
#video = Video.order(:created_at)
#pictures = Picture.order(:created_at)
#medias = #video.all + #pictures.all # Really bad idea!
Also calling all on the models like that will unnecessarily load them to memory. If you don't absolutely need all records at that time, then don't use all.

To run sql queries in Rails you could do this:
sql_statement = "Select * from ..."
#data = ActiveRecord::Base.connection.execute(sql_statement)
Then in your view you could simply reference the #data object

Related

Trigger query with ActiveJdbc in controller

I'm running a query using ActiveJdbc
List<Game> games = District.findAll("where createor_id = ?", creatorId);
And according o the documentation the query is triggered when I do this
for (Game game : games) {
//do things with result
}
But I want to put the result in a ModelMap in order to use in in the jstl view (Spring mvc 4). So How can I trigger the query? right now in order to trigger the query I have to do
game.size();
But I guess it's an optimal solution.
You should not worry about when the list will make a trip to the database. If you just pass the games object to the JSP, then it will make DB call during rendering of the page. Also, you do not need to make additional ModelMap, just pass a list to a view.
If you insist on passing maps to JSP, you can do this:
List<Map> games = District.findAll("where createor_id = ?", creatorId).toMaps();
I hope it helps!

Titanium access a model from other controller

I learned that is this how to access a model from other controller,
var book = Alloy.Models.instance('book');
And this is how to access a property of a model,
var name = book.get('name');
However in the console,the name logs [INFO] : { } , meaning this doesn't get its property value, and ofcourse the model has already a data saved on it. Thanks for your help!
You may have to fetch the collection first:
var books = Alloy.Collections.book;
books.fetch();
This will load all the models from the collection so you can use them.
although the above works, there are a few addtional points here.
the call is asynchronous in most cases so you should be getting the model in a callback which is not presented in the code above.
I dont know if fetching the collection everytime you want a model is the correct approach either? If the collection already exists you just need to get the model from the collection just using the id.
depending on the exact use case, you might just want to pass the model as a parameter from one controller to the next

how to access column value after serialization activerecord

I have a simple model like this:
class User < ActiveRecord::Base
serialize :preferences
end
I want to access the raw value from mysql, not value before serialize. Is it possible?
I know I can use
ActiveRecord::Base.connection.execute("select * from users")
But I want to access from the User model.
Updated
Ok, this is what you are looking for:
User.find(params[:id]).attributes_before_type_cast["preferences"][:value]
This will return the string in its serialized form.
That is the closest you can get that I can find, it won't work if you have already gotten the object pulled from the database.
Sorry for misreading your question. You can use this from the User model too.
Leaving the old answer up just in case the other way of doing it is helpful to someone.
Old Answer
Just to be sure I understand the question, you want the raw data from the table. The data that rails serializes and puts in the database.
EX. You put in ['site_id','last_update','last_restart'] and you get "---\n- site_id\n- last_update\n- last_restart\n" and it is put in the database and saved. You want to retrieve this: "---\n- site_id\n- last_update\n- last_restart\n" from the database.
Ok, it took some fanagaling from the database but you can do it like so.
In a project I have a serialized array call devise_table_preferences that lists the preferences to display in a table in a particular order, like so:
user.devise_table_preferences = ['site_id','last_update','last_restart']
The serialized view of it is like so:
"---\n- site_id\n- last_update\n- last_restart\n"
Using your method above, I made a query like so:
preference = ActiveRecord::Base.connection.execute("SELECT devise_table_preferences FROM users WHERE id = #{#user.id}")
It returns an object in the console like so:
preference = #<Mysql2::Result:0x007fe4cdf34850>
Running:
preference.first[0]
Gave me this:
"---\n- site_id\n- last_restart\n"
I know its a big work around but it will definitely give you your data in its serialized way. Hope that it helps you out.
attributes_before_type_cast didn't work for me.
User.first.instance_variable_get(:#attributes)['preferences'].serialized_value
This works even if the object is loaded.
I think these days you want to say the following:
User.find(params[:id]).typecasted_attribute_value('preferences')

Django aggregate query

I have a model Page, which can have Posts on it. What I want to do is get every Page, plus the most recent Post on that page. If the Page has no Posts, I still want the page. (Sound familiar? This is a LEFT JOIN in SQL).
Here is what I currently have:
Page.objects.annotate(most_recent_post=Max('post__post_time'))
This only gets Pages, but it doesn't get Posts. How can I get the Posts as well?
Models:
class Page(models.Model):
name = models.CharField(max_length=50)
created = models.DateTimeField(auto_now_add = True)
enabled = models.BooleanField(default = True)
class Post(models.Model):
user = models.ForeignKey(User)
page = models.ForeignKey(Page)
post_time = models.DateTimeField(auto_now_add = True)
Depending on the relationship between the two, you should be able to follow the relationships quite easily, and increase performance by using select_related
Taking this:
class Page(models.Model):
...
class Post(models.Model):
page = ForeignKey(Page, ...)
You can follow the forward relationship (i.e. get all the posts and their associated pages) efficiently using select_related:
Post.objects.select_related('page').all()
This will result in only one (larger) query where all the page objects are prefetched.
In the reverse situation (like you have) where you want to get all pages and their associated posts, select_related won't work. See this,this and this question for more information about what you can do.
Probably your best bet is to use the techniques described in the django docs here: Following Links Backward.
After you do:
pages = Page.objects.annotate(most_recent_post=Max('post__post_time'))
posts = [page.post_set.filter(post_time=page.most_recent_post) for page in pages]
And then posts[0] should have the most recent post for pages[0] etc. I don't know if this is the most efficient solution, but this was the solution mentioned in another post about the lack of left joins in django.
You can create a database view that will contain all Page columns alongside with with necessary latest Post columns:
CREATE VIEW `testapp_pagewithrecentpost` AS
SELECT testapp_page.*, testapp_post.* -- I suggest as few post columns as possible here
FROM `testapp_page` LEFT JOIN `testapp_page`
ON test_page.id = test_post.page_id
AND test_post.post_time =
( SELECT MAX(test_post.post_time)
FROM test_post WHERE test_page.id = test_post.page_id );
Then you need to create a model with flag managed = False (so that manage.py sync won't break). You can also use inheritance from abstract Model to avoid column duplication:
class PageWithRecentPost(models.Model): # Or extend abstract BasePost ?
# Page columns goes here
# Post columns goes here
# We use LEFT JOIN, so all columns from the
# 'post' model will need blank=True, null=True
class Meta:
managed = False # Django will not handle creation/reset automatically
By doing that you can do what you initially wanted, so fetch from both tables in just one query:
pages_with_recent_post = PageWithRecentPost.objects.filter(...)
for page in pages_with_recent_post:
print page.name # Page column
print page.post_time # Post column
However this approach is not drawback free:
It's very DB engine-specific
You'll need to add VIEW creation SQL to your project
If your models are complex it's very likely that you'll need to resolve table column name clashes.
Model based on a database view will very likely be read-only (INSERT/UPDATE will fail).
It adds complexity to your project. Allowing for multiple queries is a definitely simpler solution.
Changes in Page/Post will require re-creating the view.

Can anyone explain how CDbCriteria->scopes works?

I've just checked the man page of CDbCriteria, but there is not enough info about it.
This property is available since v1.1.7 and I couldn't find any help for it.
Is it for dynamically changing Model->scopes "on-the-fly"?
Scopes are an easy way to create simple filters by default. With a scope you can sort your results by specific columns automatically, limit the results, apply conditions, etc. In the links provided by #ldg there's a big example of how cool they are:
$posts=Post::model()->published()->recently()->findAll();
Somebody is retrieving all the recently published posts in one single line. They are easier to maintain than inline conditions (for example Post::model()->findAll('status=1')) and are encapsulated inside each model, which means big transparency and ease of use.
Plus, you can create your own parameter based scopes like this:
public function last($amount)
{
$this->getDbCriteria()->mergeWith(array(
'order' => 't.create_time DESC',
'limit' => $amount,
));
return $this;
}
Adding something like this into a Model will let you choose the amount of objects you want to retrieve from the database (sorted by its create time).
By returning the object itself you allow method chaining.
Here's an example:
$last3posts=Post::model()->last(3)->findAll();
Gets the last 3 items. Of course you can expand the example to almost any property in the database. Cheers
Yes, scopes can be used to change the attributes of CDbCriteria with pre-built conditions and can also be passed parameters. Before 1.1.7 you could use them in a model() query and can be chained together. See:
http://www.yiiframework.com/doc/guide/1.1/en/database.ar#named-scopes
Since 1.1.7, you can also use scopes as a CDbCriteria property.
See: http://www.yiiframework.com/doc/guide/1.1/en/database.arr#relational-query-with-named-scopes