I'm working on a rails app that searches on a database of users. Because the search is fairly complex I have a search model that stores the different search parameters and a results method in that model that actually puts together the query and returns an array of objects.
Since there is no relationship between search and the objects returned in results, is it still possible to paginate the results with will_paginate? Am I going about this the wrong way?
It should be possible to do what you do, without needing to paginate the array. According to the docs:
# paginate in Active Record now returns a Relation
Post.where(:published => true).paginate(:page => params[:page]).order('id DESC')
# the new, shorter page() method
Post.order('created_at DESC').page(params[:page])
(( from https://github.com/mislav/will_paginate/wiki ))
... so you should be able to create your "where" even as a model method and then paginate the results of the model method.
You can paginate the array, if you absolutely have to, according to these instructions:
https://github.com/mislav/will_paginate/wiki/Backwards-incompatibility#willpaginatecollection
Related
I need to retrieve information from two separate models which are similar but not the same. I am trying to do things like
I have looked into a few methods however they return an array of active objects rather than an
ActiveRecord::Relation which is required for many of the features of my app to work.
Is there any way to return an ActiveRecord::Relation object containing a union of both tables?
I have tried things like
#group = Mymodel.find_by_sql("SELECT id FROM Mymodels
UNION SELECT id FROM AnotherModels")
and also explored using the Model.where method however cannot return an ActiveRecord::Relation
EDIT:
Just to be clear I need to return ActiveRecord::Relation that is a union or a merge of the two tables
Have you tried MyFirstModel.joins(:my_second_models)? Check out details joins in the API here.
EDIT: Single Table Inheritance is a better solution to this problem. See comments below.
Try something like this:
Model.joins(:other_model).where("attr1" = :attr1,
{ attr1: "example" }).group(:attr1)
Since you commented about where, I added the where method on the call. You can also group everything using :group in the end.
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
Lets say I have a model, Foo, which is big and has lots of components. For a given Ajax query I'm only interested in one particular attribute, bar, which is a column in the foos table.
Is there a simple way I could load just that attribute, and not bother with retrieving the rest of the record? For instance if all I want to know is the bar for Foo with id#__, how could I retrieve that?
You can return only specific columns by calling the select method with a string containing the attributes you want to return. For your example:
Foo.select('bar').first #<Foo bar: 1>
Keep in mind that these objects will act like normal ActiveRecord objects but return nil for any field you did not select, so take care using this functionality.
You can call select on the class name itself or any Relation, so you can chain together the ActiveRecord calls you usually use like where, etc.
I prefer this
User.where(:id => user_id).pluck(:user_name).first #'tom'
Foo.where(:age => 23).pluck(:user_name) #['tom', 'jerry', ...]
Foo.where(<condition>).select('fieldname')
Example
results = Foo.where('is_active = ?', true).select('bar')
Access the selected fields as:
results.map {|res| res.bar} returns an array of bar's
pluck(*column_names)
doc: http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-pluck
e.g. Foo.pluck(:bar)
pick(*column_names) select just one top row's columns, docs
Similar to pluck but fetch only one row
I want to only return items that don't have associated images. My relationship is something like this:
class Post(models.Model):
....fields
class Photo(models.Model):
post=models.ForeignKey(Post,blank=True,null=True)
photo=models.FileField(upload_to="pics")
def __unicode__(self):
return str(self.post)
I put together the following query to return Post instances where Photo is not null:
posts=Post.objects.filter(photo__photo__isnull=False)
The problem is that it's returning multiple copies of each Post instance per the number of Photo instances that are related to the Post instance. In other words, one post has 5 photos and it is therefore returning five copies in the queryset. I've looked through the documentation and this is a bit tricky. I ended up using distinct(), but I assume that I can make it work immediately.
Thanks
To return posts that don't have associated photos, use the following query:
posts=Post.objects.filter(photo__isnull=True)
Later in your question you are using isnull=False. As you say, the resulting queryset will return each post once for every photo which is attached to it. To only include each post once in the queryset, use distinct.
posts=Post.objects.filter(photo__isnull=False).distinct()
I'm not sure why you query photo__photo__isnull in you're query -- My answer assumes you should use photo__isnull.
I'm not sure what you mean by "but I assume that I can make it work immediately", but using either distinct(), or order_by() should be the solution to your problem.
I'm aware that I can search queries by calling where() on a model as follows:
Post.where(:title => 'My First Post')
However, what if I don't know if the user wants to filter out the search parameters?
For example, I have a search form that has an optional title field. If the title is filled, the form should search for a title. If it is not, however, the form should just return all fields.
I tried doing something along the lines of
search = Post.all
if params[:title].present?
search.where(:title => params[:title])
end
However, Rails immidiately returns the result of the search when I call Post.all and I cannot further add conditions/
How do I do this?
Thanks!
.all is a 'finisher' method for arel and causes the query to actually be called (same goes for .each and .first). So, if you want to be able to keep building up the scope conditionally, .all should be the last thing called, if you want to call it at all.
Given that there really isn't much in the way of relations to chain here, it would seem that a simple one-liner with a ternary operator might do:
#posts = params[:title].present? ? Post.where(:title => params[:title]) : Post.all
... because when there is no :where clause scope, you couldn't later chain it to :all, anyway.