Skip to content
This repository has been archived by the owner on Dec 12, 2021. It is now read-only.

Associations in conditions hash #374

Closed
RKushnir opened this issue May 18, 2011 · 9 comments
Closed

Associations in conditions hash #374

RKushnir opened this issue May 18, 2011 · 9 comments

Comments

@RKushnir
Copy link

Hi,
I have Photo and Group model. Photo has_and_belongs_to_many :groups.

I need to allow user view photos which do not belong to any group, so I thought I would write can :read, Photo, :groups => {:id => nil}, but it uses joins on association. Therefore, resulting SQL looks like

SELECT * FROM photos INNER JOIN groups_photos INNER JOIN groups

which obviously always returns empty set.

What I actually need is a scope Photo.includes(:groups).where(:groups => {:id => nil}), which produces

photos LEFT OUTER JOIN groups_photos LEFT OUTER JOIN groups

and returns what expected. Is there a clean way to define such ability with cancan?

@ryanb
Copy link
Owner

ryanb commented May 21, 2011

You can pass in a scope but you'll need to define the behavior in Ruby as well.

can :read, Photo, Photo.includes(:groups).where(:groups => {:id => nil}) do |photo|
  photo.groups.empty?
end

It may not be the cleanest, but does that work for you?

@RKushnir
Copy link
Author

Yes! Thank you.
I had to merge some other conditions manually, as it doesn't allow rule merging now. But it works, and without making unnecessary queries on each page load(as I was trying to have it done).

BTW, are there any reasons preventing from using includes instead of joins in model_adapter? IMHO, with includes it would be more intuitive. Currently, rules with joins unwittingly restrict scope when combined with other rules.

@ryanb
Copy link
Owner

ryanb commented May 21, 2011

The includes option does eager loading (loading in the associated record's information) and this is often not necessary if you aren't displaying that information. CanCan's accessible_by isn't intended to do eager loading and one can always add the includes scope if they need eager loading.

It is unfortunate that includes and joins do a different join. This may be considered a bug in Active Record? I would need to do some research.

@ahawkins
Copy link

Upvote for outer joins! Very annoying to work around.

@clyfe
Copy link

clyfe commented Dec 27, 2011

@the8472
Copy link

the8472 commented Apr 5, 2012

And a more lightweight hack (which also requires squeel): https://gist.github.com/2312198

For Rails >= 3.1 only another approach is also possible: Generate the query as usual, extract the arel ast for the joins, convert them from inner to outer joins and then merge the joins back in. That wouldn't require squeel.

@derekprior
Copy link
Collaborator

Marking this a documentation bug. The workaround Ryan originally offered should be documented with the Scope moved to the class that it queries. I think Ryan's reasoning for not doing includes by default is sound.

@derekprior
Copy link
Collaborator

Documentation added (see Hash of Abilities):
https://github.com/ryanb/cancan/wiki/Defining-Abilities

@pcreux
Copy link

pcreux commented Jul 23, 2013

I ended up monkey patching cancan to run conditions in subqueries instead of building outer joins.

https://gist.github.com/pcreux/6064275

yan13to pushed a commit to paupauorg/cancan that referenced this issue May 30, 2017
…-issues-lint-unused-block-argument

Fix rubocop Issues: Lint/UnusedBlockArgument
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants