Django使用'through'关系表

时间:2017-01-11 19:07:14

标签: python django django-models python-3.5 django-1.10

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

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

<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关系,并且所有这些关系都包含指定Here的“直通”模型。

正如你所看到的,它重复它们,一个用于模型,一个用于“通过”中间表(我猜也是模型)。在递归关系的情况下,它重复两次。

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

一种了解哪些重复字段最终指向相同关系的方法(即使它会阻塞两个表)?因为如果直通表有字段,我想以不同的方式显示它们。

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

[
    f for f in MyModel._meta.get_fields()
    if (f.one_to_many or f.one_to_one)
    and f.auto_created and not f.concrete
]

但是'through'表不被认为是auto_created并且具体。

示例:

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

这两个字段'指向'相同的关系,一个是中间表,另一个是模型,是否有(自动)方式知道这两个是相关的?我找不到他们分享的任何属性。

这是因为当直通表有字段时,我需要在其上编辑而不是模型本身的M2M字段

Models.py http://pastebin.com/szDfhHQ3我尽力清理

3 个答案:

答案 0 :(得分:1)

对于Django 1.10,以下代码受BaseModelForm代码(Django原创)的启发。

如果您有以下关系:

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

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

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

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

opts = Person._meta
for f in chain(opts.many_to_many, opts.virtual_fields):
    if f.rel.through:
        # this would return "group"
        attr_field = f.m2m_reverse_field_name()

        # this is the Membership class (a class instance)
        m2m_model = f.rel.through

        # this would return "person"
        join_field = field.m2m_field_name()

        # to get all "Membership" objects for "person" personXY
        qs_filter = {join_field: personXY}
        qs = m2m_model.objects.filter(**qs_filter)

        # get the PKs of all groups where personXY is a member of
        lookup_by_pk = '{}__pk'.format(attr_field)
        current_pks = qs.values_list(lookup_by_pk, flat=True) if qs.exists() else []

答案 1 :(得分:1)

例如我们有这套模型。我从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)

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

def get_through_field(f):                                                                                              
    opts = f.through._meta                                                                                             
    if f.through_fields:
        return opts.get_field(f.through_fields[1])                                                                     
    for field in opts.fields:                                                                                          
        rel = getattr(field, 'remote_field', None)                                                                     
        if rel and rel.model == f.model:                                                                               
            return field

model = models.Person

rels = dict(
    (f.field, f) for f in model._meta.get_fields()
    if f.is_relation
)

excludes = set()
for f in model._meta.get_fields():
    if f.many_to_many:
        through = get_through_field(f)
        excludes.add(rels[through])

for f in model._meta.get_fields():
    if f not in excludes:
        print f.name, f

<强>输出

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

如您所见,没有membership字段。

答案 2 :(得分:1)

其他答案肯定帮我解决了这个问题,特别是在我的情况下,我的所有关系都是M2M并且有一个直通表,所有内容都是用AJAX / Javascript完成的,所以我的答案非常JSON-y。

现在它只能通过m2m模型的所有表来获取,因为你必须在其中创建对象以创建关系,但它可以很容易地扩展为获得所有其他关系

kubectl get service MY-DEPLOYMENT-NAME

这是一个丑陋的代码,但我不擅长Python,只是想学习东西,但我保证它对我的问题有点像魅力