Django queryset with "isnull" argument returning duplicates - sql

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.

Related

will_paginate for results of method on an object

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

Rails ActiveRecord::Relation object using including models

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.

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')

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

Rails3: Cascading Select Writer's Block

I have a big, flat table:
id
product_id
attribute1
attribute2
attribute3
attribute4
Here is how I want users to get to products:
See a list of unique values for attribute1.
Clicking one of those gets you a list of unique values for attribute2.
Clicking one of those gets you a list of unique values for attribute3.
Clicking one of those gets you a list of unique values for attribute4.
Clicking one of those shows you the relevant products.
I have been coding Rails for about 4 years now. I just can't unthink my current approach to this problem.
I have major writer's block. Seems like such an easy problem. But I either code it with 4 different "step" methods in my controller, or I try to write one "search" method that attempts to divine the last level you selected, and all the previous values that you selected.
Both are major YUCK and I keep deleting my work.
What is the most elegant way to do this?
Here is a solution that may be an option. Just off the top of my head and not tested (so there is probably a bit more elegant solution). You could use chained scopes in your model:
class Product < ActiveRecord::Base
scope :with_capacity, lambda { |*args| args.first.nil? ? nil : where(:capacity=>args.first) }
scope :with_weight, lambda { |*args| args.first.nil? ? nil : where(:weight=>args.first) }
scope :with_color, lambda { |*args| args.first.nil? ? nil : where(:color=>args.first) }
scope :with_manufacturer, lambda { |*args| args.first.nil? ? nil : where(:manufacturer=>args.first) }
self.available_attributes(products,attribute)
products.collect{|product| product.send(attribute)}.uniq
end
end
The code above will give you a scope for each attribute. If you pass a parameter to the scope, then it will give you the products with that attribute value. If the argument is nil, then the scope will return the full set (I think ;-). You could keep track of the attributes they are drilling down in in the session with 2 variables (page_attribute and page_attribute_value) in your controller. Then you call the entire chain to get your list of products (if you want to use them on the page). Next you can get the attribute values by passing in the set of products and the attribute name to Product.available_attributes. Note that this method (Product.available_attributes) is a total hack and would be inefficient for a large set of data, so you may want to make this another scope and use :select=>"DISTINCT(your_attribute)" or something more database efficient instead of iterating thru the full set of products as I did in the hack method.
class ProductsController < ApplicationController
def show
session[params[:page_attribute].to_sym] = params[:page_attribute_value]
#products = Product.all.with_capacity(session[:capacity]).with_weight(session[:weight]).with_color(session[:color]).with_manufacturer(session[:manufacturer])
#attr_values = Product.available_attributes(#products,params[:page_attribute])
end
end
Again, I want to warn you that I did not test this code, so its totally possible that some of the syntax is incorrect, but hopefully this will give you a starting point. Holla if you have any questions about my (psuedo) code.