- Author:
- christhekeele
- Posted:
- May 22, 2012
- Language:
- Python
- Version:
- 1.3
- Score:
- 2 (after 2 ratings)
Code for a RelatedMixin I whipped up, useful in instances where you wish to expose details of a single object, including a related group of owned objects, in the same view. Works well with Django's generic DetailView and UpdateView, or any subclass of SingleObjectMixin.
It's a little cleaner than overriding get_context_data differently for every model you want to expose, uses only('id')
on querysets it doesn't need anything but relational data from, and makes pulling ownership out of distantly related objects much easier.
Supports simple nested hierarchies of models, ie:
-
View a company and all people belonging to it Detail(Company < People)
-
Edit a company and all computers belonging to its members Update(Company < People < Computers).
Tested with non-generic One-To-Many and reverse object_sets only.
Just provide an OrderedDict called related_chain
in your DetailRelatedView that describes the progression of querysets to follow, in the format:
model=Foo,
relation_chain=OrderedDict([
('foreign_key_from_foo_to_bar',Bar.objects.all()),
('foreign_key_from_baz_to_bar',Baz.objects.all())
])
It also takes two optional attributes:
context_list_name="baz_list"
which provides an alias for the final related object_list
(default=related_list
), and
keep_intermediaries=True
which, if providing a list deeper than one relation, also passes any intermediary related lists into the context, named after the connecting foreign key, like bar_list
(default=False).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | ### View implementation in a views.py
class RelatedMixin(object):
relation_chain = None
context_list_name = 'related_list'
keep_intermediaries = False
def get_context_data(self, **kwargs):
context = super(RelatedMixin, self).get_context_data(**kwargs)
assert hasattr(self, 'object'), "RelatedMixin must be used with a view containing an instance of a model named 'object',\nnormally a subclass of SingleObjectMixin."
assert hasattr(self, 'relation_chain'), "RelatedMixin must supply an OrderedDict named 'relation_chain' of fk/queryset tuples honing in on the desired object_list\n(refer to http://djangosnippets.org/snippets/2756/)"
ids = [self.object.id]
current_qs = not self.keep_intermediaries
for fk, qs in self.relation_chain.iteritems():
qs = qs.filter(**{fk+"__in":ids})
ids = qs.only('id')
if self.keep_intermediaries:
if current_qs:
context[fk+"_list"] = current_qs
current_qs = qs
context['object_list'] = context[self.context_list_name] = qs
return context
### Example mixin application
from mixins.views import RelatedMixin
from django.views.generic import DetailView, UpdateView
class DetailRelatedView(RelatedMixin,DetailView):
pass
class UpdateRelatedView(RelatedMixin,UpdateView):
pass
### Example usage in a urls.py
from django.conf.urls import patterns, url
from extra.views import DetailRelatedView
from collections import OrderedDict
from organization.models import Company, People, Computers
urlpatterns = patterns('organization.views',
## One level deep
## yields 'object'='company' and 'object_list'='related_list' in context
url(r'^companies/(?P<pk>\d+)/people/$',
ShowRelatedView.as_view(
template_name="company/related/people.html",
model=Company,
relation_chain=OrderedDict([('company',People.objects.all())])
),
name='people_for_company'
),
## Two levels deep, non-standard fk on Computers, extra context variable
## yields 'object'='company', and 'object_list'='computer_list' in context
url(r'^companies/(?P<pk>\d+)/computers/$',
ShowRelatedView.as_view(
template_name="company/related/computers.html",
model=Company,
context_object_name='company',
relation_chain=OrderedDict([ ('company',People.objects.all()) , ('owner',Computer.objects.all()) ])
context_list_name='computer_list',
),
name='computers_for_company'
),
## Three levels deep, keep intermediary object lists, don't use alias for final 'object_list'
## yields 'object'='company', 'owner_list', 'computer_list', and 'object_list' (of Software objects) in context
url(r'^companies/(?P<pk>\d+)/software_downloads/$',
ShowRelatedView.as_view(
template_name="company/related/software.html",
model=Company,
context_object_name='company',
relation_chain=OrderedDict([ ('company',People.objects.all()) , ('owner',Computer.objects.all()) , ('computer',Software.objects.all()) ])
context_list_name=None,
keep_intermediaries = True,
),
name='software_downloads_for_company'
),
)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 4 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.