Skip to content

Commit e263efc

Browse files
committed
Sneak in some utilities
1 parent 9e24ed0 commit e263efc

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

feincms3_sites/fields.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import inspect
2+
3+
from django import forms
4+
from tree_queries.fields import TreeNodeForeignKey
5+
6+
7+
def _variable_from_stack(name, must_exist=()):
8+
"""
9+
We want to filter objects related to a particular site so that content
10+
managers can only select objects from the same site as the object which is
11+
currently being edited. However, there's no sensible way of passing the
12+
edited object down to the form or choice field without doing it by hand for
13+
each and every model admin and inline instance. This isn't just tedious:
14+
The bigger problem is that errors will happen. Therefore, this helper can
15+
be used to fetch a variable from any parent frame. Specifying only the
16+
variable name feels a bit brittle, therefore the additional
17+
``must_exist`` argument can be used to require several other variables
18+
to exist in the matching scope. This still isn't 100% safe but it works
19+
well enough.
20+
"""
21+
22+
_sentinel = object()
23+
# Start inspecting from the caller of the function which called us
24+
frame = inspect.currentframe().f_back.f_back
25+
try:
26+
while frame:
27+
obj = frame.f_locals.get(name, _sentinel)
28+
if obj is not _sentinel and all(v in frame.f_locals for v in must_exist):
29+
return obj
30+
frame = frame.f_back
31+
finally:
32+
# Delete the frame reference to make the GC's job easier
33+
del frame
34+
35+
36+
class AutomaticSiteRestrictionChoiceField(forms.ModelChoiceField):
37+
def __init__(self, queryset, *args, **kwargs):
38+
obj = _variable_from_stack("obj", ("object_id", "to_field"))
39+
queryset = queryset.filter(site_id=obj.site_id if obj else None)
40+
super().__init__(queryset, *args, **kwargs)
41+
42+
43+
class AutomaticSiteRestrictionForeignKey(TreeNodeForeignKey):
44+
"""
45+
The Site foreign key field has to be called ``site`` on all participating objects
46+
"""
47+
48+
def formfield(self, **kwargs):
49+
kwargs.setdefault("form_class", AutomaticSiteRestrictionChoiceField)
50+
return super().formfield(**kwargs)

0 commit comments

Comments
 (0)