问题
I'm creating a simple CMS in django, with multiple "modules" (each as a django app). I've set up the following models:
class FooObject(models.Model):
id = models.SlugField(primary_key=True)
name = models.CharField(max_length=255)
creator = models.ForeignKey(auth.models.User, editable=False, related_name="createdby")
class FooPage(FooObject):
content = models.TextField(blank=True, null=True)
@models.permalink
def get_absolute_url(self):
return ('page', (), {'page_id':self.id}
class FooSubitem(FooObject):
parent = models.ForeignKey(FooPage, related_name='subitems')
In each of the modules, I create a subclass of FooPage, and at least one subclass of FooSubitem, e.g.
# in FooBlog.models
class FooBlog(FooPage):
owner = models.ForeignKey(auth.models.User, editable=False)
@models.permalink
def get_absolute_url(self):
return ('blog', (), {'blog_id':self.id})
class FooPost(FooSubitem):
post_time = models.DateTimeField(auto_now_add=True)
and
# in FooGallery.models
class FooGallery(FooPage):
location = models.CharField(max_length=255)
@models.permalink
def get_absolute_url(self):
return ('gallery', (), {'gallery_id':self.id})
class FooImage(FooSubitem):
image_file = models.ImageField(upload_to='foogallery')
These are simplifications, but should give you a good idea of what I'm trying to do. In the admins for FooPost and FooImage, I restrict the parent selection list to their corresponding parent pages.
My problem arises when I try to use these in a template. In each view, I have the following:
page_list = FooPage.objects.all()
which returns a list of all FooPages, of both FooBlog and FooGallery types. However, when I iterate through this list:
{% for page in page_list %}{{ page.get_absolute_url }}{% endfor %}
it returns the 'page' url pattern, not the 'blog' or 'gallery' url pattern.
How do I make this work without having to rewrite the code when I want to add a FooCalendar module later on? I want to make sure this works with any possible module.
Thanks,
- Lexo
回答1:
The classic solution to this problem tends to be adding a ContentType to the superclass which stores the type of subclass for that instance. This way you can rely on a consistent API that returns the related subclass object of the appropriate type.
回答2:
You can avoid adding a content type field by using the InheritanceManager from django-model-utils.
Then, if you call .select_subclasses
on a queryset, it will downcast all of the objects, for example:
FooPage.objects.select_subclasses().all()
回答3:
FooPage.objects.all()
returns all the objects of type FooPage
, these objects will be mix of underlying db table rows for FooPage, FooBlog, FooGallery. To get the correct URL you should get the FooBlog or FooGallery object e.g.
page.fooblog.get_absolute_url()
it may throw FooBlog.DoesNotExist
error if page is simply a page object i.e created via FooPage, so to get correct urls you may do something like this
urls = []
for page in FooPage.objects.all():
try:
page = page.fooblog
except FooBlog.DoesNotExist:
pass
urls.append(page.get_absolute_url())
alternatively you may try to make FooPage a abstractclass if you do not want FooPage to be a real table.
来源:https://stackoverflow.com/questions/3176731/django-inheritance-and-permalinks