Django使用“through”关系表从模型中获取所有相关对象

2024-09-30 22:10:43 发布

您现在位置:Python中文网/ 问答频道 /正文

在我们的应用程序中,我们有几个关系和几个模型,我正在尝试实现一个对象的所有相关对象的通用方法,甚至反向对象。在

如果我从我的模型Pessoa打印._meta.get_fields(),我会得到这些关系字段(我省略了“普通”字段):

在 在

<ManyToManyRel: cadastroimoveis.pessoa>
<ManyToOneRel: cadastroimoveis.pessoa_pessoa>
<ManyToOneRel: cadastroimoveis.pessoa_pessoa>
<ManyToOneRel: cadastroimoveis.pessoa_itr>
<ManyToManyRel: cadastroimoveis.doc>
<ManyToOneRel: cadastroimoveis.doc_pessoa>
cadastroimoveis.Pessoa.relacoes
cadastroimoveis.Pessoa.itrs

这个特定的模型只有M2M关系,并且所有这些关系都包含一个指定的“through”模型Here。在

如您所见,它重复了它们,一个用于模型,一个用于“通过”中间表(我猜也是一个模型)。对于递归关系,它会重复两次。在

我的问题是,有没有办法让这些不再重复?在

一种知道哪些重复字段“指向”最终相同关系的方法(即使它将两个表分隔开)?因为如果through表有字段,我希望以不同的方式显示它们。在

根据Model _meta API文档,您可以使用它来获取所有相关对象:

^{pr2}$

但是“through”表不是自动创建的,而是具体的。在

示例:

<ManyToManyRel: cadastroimoveis.ccir>
<ManyToOneRel: cadastroimoveis.ccir_pessoa>

这两个字段“指向”了相同的关系,一个是中间表,另一个是模型,有没有一种(自动的)方法来知道这两个字段是相关的吗?我找不到他们有什么共同点。在

这样做的原因是,当through表有字段时,我需要对其进行编辑,而不是在模型本身上编辑M2M字段

模型.pyhttp://pastebin.com/szDfhHQ3我尽我所能清理干净了


Tags: 对象方法模型doc关系meta指向m2m
3条回答

对于django1.10,以下代码受BaseModelForm代码(Django original)的启发。在

如果您有以下关系:

class Group(Model):
    field = ....

class Person(Model):
    groups = ManyToManyField(Group, through='Membership')

class Membership(Model):
    person = ForeignKey(Person)
    group = ForeignKey(Group)
    position = TextField(...)

然后,可以这样查询相关字段和属性:

^{pr2}$

其他答案肯定帮了我解决这个问题,特别是在我的例子中,我所有的关系都是M2M,并且有一个through表,而且所有的事情都是用AJAX/Javascript完成的,所以我的答案非常JSON-y

目前,它只获得m2m模型的所有表,因为您必须在其中创建对象才能创建关系,但它可以很容易地扩展到获取所有其他关系

def get_relationships(model):
    fields = list(model._meta.get_fields())

    m2m_fields = {}
    #Getting m2m relationships first
    for i, field in enumerate(fields):
        print(field)
        if field.is_relation:
            if field.many_to_many:
                fields.pop(i)
                try:
                    #If its a forward field, we want the relationship instead
                    if not hasattr(field,'field'):
                        field = field.remote_field
                except AttributeError:
                    pass
                if hasattr(field,'through'):
                    through = field.through
                    #In case of recursive relationships, there will be duplicates so we don't need to do it again
                    if m2m_fields.get(through._meta.model.__name__):
                        continue
                    m2m_fields[through._meta.model.__name__] = {}
                    m2m = m2m_fields[through._meta.model.__name__]
                    #Finding the models which participate in the through table and the direction
                    m2m['owner'] = {'model' : field.model.__name__}
                    m2m['related'] = {'model' : field.related_model.__name__}
                    recursive = False
                    #Checking recursivity, will use this later
                    #Finding field names for the foreignkeys of the through table
                    for through_field in through._meta.get_fields():
                        if not (through_field.related_model is None):
                            if m2m['owner']['model'] == through_field.related_model.__name__ and not m2m['owner'].get('field'):
                                m2m['owner']['field'] = through_field.name
                            elif m2m['related']['model'] == through_field.related_model.__name__ and not m2m['related'].get('field'):
                                m2m['related']['field'] = through_field.name
                        elif not through_field.primary_key:
                            if not m2m.get('rel_fields'):
                                m2m['rel_fields'] = []
                            m2m['rel_fields'].append(through_field.name)
    #Now removing the through tables from the fields list, because they appear as a regular ManyToOne relationship otherwise
    for through_table in m2m_fields.keys():
        name = through_table
        for i, field in enumerate(fields):
            if field.many_to_one:
                if field.__name__ and field.related_model:
                    if field.related_model.__name__ == name:
                        fields.pop(i)
    #Todo : OneToOne and ManyToOne relationships

    return m2m_fields


for key,value in get_relationships(Pessoa).items():
    print(key, " = ", value)

这是一个非常难看的代码,但我不太擅长Python,只是想学习一些东西,但我保证它对我的问题很有帮助

例如,我们有这套模型。我从this django example取的。在

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

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(
        Person,
        through='Membership',
        through_fields=('group', 'person'),
    )

class Membership(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    inviter = models.ForeignKey(
        Person,
        on_delete=models.CASCADE,
        related_name="membership_invites",
    )
    invite_reason = models.CharField(max_length=64)

这个解决方案看起来有点难看,但可以根据您的需要进行优化。在

^{pr2}$

输出

group <ManyToManyRel: m.group>
membership_invites <ManyToOneRel: m.membership>
id m.Person.id
name m.Person.name

如您所见,没有membership字段。在

相关问题 更多 >