﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
10961	Allow users to override forward and reverse relationships on proxy models with !ForeignKey fields.	Tai Lee	nobody	"I have a few generic models with foreign keys (simplified for this example):

{{{
class Country(turbia_models.Model):
	name = turbia_models.CharField(max_length=50, unique=True)

class State(turbia_models.Model):
	name = turbia_models.CharField(max_length=50)
	country = models.ForeignKey(Country)

class Region(turbia_models.Model):
	name = turbia_models.CharField(max_length=50)
	state = models.ForeignKey(State)

class Suburb(turbia_models.Model):
	name = turbia_models.CharField(max_length=50)
	region = models.ForeignKey(Region)
}}}

I want to create proxy models for each of these when used with particular applications:

{{{
class Venue(models.Model):
	name = models.CharField(max_length=50)
	slug = models.SlugField()
	suburb = models.ForeignKey(Suburb)

class CountryProxy(Country):
	class Meta:
		proxy = True

	@property
	def venue_set(self):
		return Venue.objects.filter(suburb__region__state__country=self)

	def get_absolute_url(self):
		return '/directory/%s/' % self.slug

class StateProxy(State):
	class Meta:
		proxy = True

	@property
	def venue_set(self):
		return Venue.objects.filter(suburb__region__state=self)

	def get_absolute_url(self):
		return '%s%s/' % (self.country.get_absolute_url(), self.slug)

class RegionProxy(Region):
	class Meta:
		proxy = True

	@property
	def venue_set(self):
		return Venue.objects.filter(suburb__region=self)

	def get_absolute_url(self):
		return '%s%s/' % (self.state.get_absolute_url(), self.slug)

class SuburbProxy(Suburb):
	class Meta:
		proxy = True

	@property
	def venue_set(self):
		return Venue.objects.filter(suburb=self)

	def get_absolute_url(self):
		return '%s%s/' % (self.region.get_absolute_url(), self.slug)
}}}

This works if I never use the !ForeignKey fields or reverse relationship managers to get related objects. E.g. if `state` is a !StateProxy object and I try `state.country`, I'll end up with a !Country object instead of a !CountryProxy object. Likewise if `country` is a !CountryProxy object and I try `country.state_set.all()`, I'll end up with a queryset of !State objects instead of !StateProxy objects.

This can be worked around with queries like `Country.objects.get(pk=state.country.pk)` and `State.objects.filter(country=country)`, but this kinda defeats the purpose of the ORM, and is not practical inside templates.


Where you should be able to just pass a single !CountryProxy object to your template and do:

{{{
<h1>{{ country }}</h1>
{% for state in country.state_set.all %}
	<h2>{{ state }}</h2>
	{% for region in state.region_set.all %}
		<h3>{{ region }}</h3>
		<p>
			{% for suburb in region.suburb_set.all %}
				<a href=""{{ suburb.get_absolute_url }}"">{{ suburb }}</a><br>
			{% endfor %}
		</p>
	{% endfor %}
{% endfor %}
}}}

You need to pass all these objects and querysets from the view into the template context in some kind of nested structure.

{{{
def directory(request, slug):
	country = get_object_or_404(CountryProxy, slug=slug)
	state_data = ((state, (
		(region, SuburbProxy.objects.filter(region=region)) for region in RegionProxy.objects.filter(state=state)
	)) for state in StateProxy.objects.filter(country=country))
	render_to_response('directory.html', {'country': country, 'data': data})
}}}

and:

{{{
<h1>{{ country }}</h1>
{% for state, region_data in state_data %}
	<h2>{{ state }}</h2>
	{% for region, suburb_set in region_data %}
		<h3>{{ region }}</h3>
		<p>
			{% for suburb in suburb_set %}
				<a href=""{{ suburb.get_absolute_url }}"">{{ suburb }}</a><br>
			{% endfor %}
		</p>
	{% endfor %}
{% endfor %}
}}}
"	New feature	new	Database layer (models, ORM)	dev	Normal		proxy ForeignKey reverse relationship manager	Raphaël Hertzog Julien Enselme Dmytro Litvinov ivanov17	Accepted	0	0	0	0	0	0
