【问题标题】:Django model inheritance w/ custom inclusion_tags带有自定义包含标签的 Django 模型继承
【发布时间】:2010-10-30 21:23:30
【问题描述】:

我会尽量简化这一点。假设我有以下内容:

models.py

class Person(models.Model):
    name = models.CharField(max_length=255)

    def getRealPerson(self):
        # is there a better way to do this?
        ret = None
        try:
            ret = self.worker
        except:
            try:
                ret = self.retired
            except:
                ret = self
        return ret
class Worker(Person):
    salary = models.IntegerField(default=0)

class Retired(Person):
    age = models.IntegerField()

这个例子对我想要什么并不重要,跟我一起去吧。这样做的目的是让我可以拥有一个主 Person 表来引用所有人。

理想情况下,我希望能够调用 Person 的视图,并为每个类类型以自定义方式列出每个特定的详细信息。我想使用自定义的包含标签来执行此操作。

people.html

{% load people_extras %}
{% for person in people %}
   {% show_person person %}
{% endfor %}

people_extras.py - 模板标签

from django import template

@register.inclusion_tag('worker.html')
def show_worker(person):
    return {'person':person}

@register.inclusion_tag('worker.html')
def show_retired(person):
    return {'person':person}

#How do I write this function and use it as the show_person person tag?
from project.app.models import Worker, Retired
def show_person(person):
    person = person.getRealPerson():
    if isinstance(person, Worker):
        return show_worker # yes, this doesn't work.

我不知道如何让它根据人员类型调用正确的模板。

我不知道如何通过使用 {% ifequal %} 的模板来完成此操作,如下所示:

{% ifequal person.getRealPerson.__class__.__name__ "Worker" %}
    {% show_worker %}
...

我使用模板标签走了我上面写的路线。但是,我不知道在哪里放置逻辑来确定人的类型!

我认为最终我希望能够在 Person 对象上使用通用视图。

如果有更好的方法可以做到这一点,我愿意接受建议,我只想让它发挥作用。

我已经在这里卡了一天多...真的可以使用推力。

【问题讨论】:

    标签: django inheritance django-models django-templates


    【解决方案1】:

    请参阅this answer,了解在对 Person 表进行查询后获取正确人员类型子对象的有效方法。

    一旦你完成了这项工作,你应该能够通过使用多态性来消除模板标签中的大部分复杂性。如果您想使用不同的模板显示每个人类型,请将该模板名称作为模型的类属性,或者甚至只是根据模型名称创建模板名称(使用 person._meta.module_name)。一个简单的模板标签应该能够覆盖所有情况,甚至不知道存在哪些子类的任何细节。 EDIT 这个单一标签不能使用inclusion_tag 装饰器注册,因为您需要动态确定模板名称。但是使用 simple_tag 装饰器很容易编写:

    @register.simple_tag
    def show_person(person):
        t = template.loader.select_template(["%s.html" % person._meta.module_name,
                                             "person.html")
        return t.render({'person': person})
    

    这将使用worker.html 渲染一个Worker,使用retired.html 渲染一个Retired,等等。如果找不到子类型的特定模板,它会回退到默认的person.html。

    【讨论】:

    • 我对这个自定义模板标签的实现有点困惑。 testperson.cast() 返回正确的子对象,但是如何在调用正确的包含标签时使用它。按照我上面设置的方式,show_person 无法被调用(它没有注册)。
    • 这就是我在第二段中提到的。编辑希望让它更清晰。
    【解决方案2】:

    将此 sn-p 用于您的 getRealPerson() Person 模型方法:

    def getRealPerson(self):
        from django.db.models.fields.related import OneToOneRel
        from django.core.exceptions import ObjectDoesNotExist
        for related in self._meta.get_all_related_objects():
            rel_opts_name = related.get_accessor_name()
            if isinstance(related.field.rel, OneToOneRel):
                try:
                    sub_obj = getattr(self, rel_opts_name)
                except ObjectDoesNotExist:
                    pass
                else:
                    return sub_obj
    

    它会返回第一个相关对象(Worker 或 Retired)。

    【讨论】:

      【解决方案3】:

      我使用一种我现在快速组合起来的方法来完成一项非常相似的工作。我有许多模型继承自一个主要的“基本”模型——我可以通过一个唯一的代码访问我数据库中的所有内容(我们称之为“go code”)。要找出父对象的子对象,我会这样做:

      def get_child_types(self):
          return [ c.__name__.lower() for c in self.__subclasses__() ]
      
      def get_child(self):
          for type in self.get_child_types():
              try:
                  o = self.__getattribute__(type)
              except:
                  pass
              else:
                  return o
      

      我知道它很乱,但它工作正常。我将对象的“类型”存储在父对象模型的非规范化字段中,所以我只需要“找到”一次——这减少了对数据库的访问和所需的 CPU 工作。然后,我只需覆盖 save() 以在创建时查找对象类型,并将其存储在该字段中。

      希望对你有帮助!

      【讨论】:

      • 这有一个普遍正确的想法,但它比它需要的更复杂(和更慢)。在创建时,您已经知道正确的类型,因此您不需要通过遍历类型列表并检查每个类型来找到它。见stackoverflow.com/questions/929029/…
      【解决方案4】:

      利用 Django 的 contenttypes framework 来识别您的模型类型。

      查看这个 sn-p:Child aware model inheritance 并在 cmets 中使用 Carl Meyer's 建议(将作业包装在“if not self.id:”中)

      【讨论】:

      • 或者看到这个答案:stackoverflow.com/questions/929029/…
      • 另请注意,rix 根据我的建议修复了他的 sn-p,因此不再需要指出评论。
      • 哦,呃,我已经熬夜了,由于某种原因,我的大脑没有注册 self.pk 是 self.id :)
      猜你喜欢
      • 1970-01-01
      • 2011-08-10
      • 2011-01-23
      • 1970-01-01
      • 2012-12-22
      • 2011-11-29
      • 1970-01-01
      • 2011-01-02
      • 2010-11-21
      相关资源
      最近更新 更多