В пользовательской сквозной модели для отношения M2M .add(), .create() и .remove() отключены.

В данный момент я пытаюсь использовать .add() (или что-то еще) и ловить и иметь дело с AttributeError для этих пользовательских отношений M2M.

Есть ли «официальный» способ идентификации настраиваемой сквозной модели, используя Meta API или иным образом? На этом этапе моей обработки я предпочел бы рассматривать все пользовательские сквозные отношения как можно более обобщенно (а не множество if m2m_field.related.through == FooBar операторов)

(Решение было найдено для Django 1.8, но я начинаю вознаграждение за 2.2)

2
Steven 29 Авг 2017 в 16:03

3 ответа

Лучший ответ

Выглядит как будто

m2m_field.related.through._meta.auto_created is False

Делает работу

1
Steven 29 Авг 2017 в 18:04

Для Django 2.2 вы можете напрямую проверить, создана ли сквозная модель или нет

Это можно проверить с помощью прямой проверки, как показано ниже

# check for the auto_created value
m2m_field.through._meta.auto_created == False

Чтобы проверить это, я создал несколько примеров предложений с несколькими полями M2M, одно с настраиваемым сквозным полем, а другое с классом по умолчанию.

from django.db import models
import uuid


class Leaf(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(null=True, max_length=25, blank=True)


# Create your models here.
class MainTree(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    new_leaves = models.ManyToManyField(Leaf, through='LeafTree')
    leaves = models.ManyToManyField(Leaf, related_name='main_branch')


class LeafTree(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    tree = models.ForeignKey(MainTree, on_delete=models.CASCADE)
    leaf = models.ForeignKey(Leaf, on_delete=models.CASCADE)

Тестирование нашего метода

In [2]: from trees.models import MainTree                                                                                                                                                                

In [3]: m = MainTree()                                                                                                                                                                                   

In [4]: m.leaves.through._meta                                                                                                                                                                           
Out[4]: <Options for MainTree_leaves>

In [5]: m.leaves.through._meta.auto_created                                                                                                                                                              
Out[5]: trees.models.MainTree

In [6]: m.new_leaves.through._meta.auto_created                                                                                                                                                          
Out[6]: False

In [7]: m.new_leaves.through._meta.auto_created == False                                                                                                                                                 
Out[7]: True

In [8]: m.leaves.through._meta.auto_created == False                                                                                                                                                     
Out[8]: False
0
StarLord 7 Окт 2019 в 19:01

В Django 2.2 методы .add(), .create() и т. Д. Могут работать с пользовательской моделью through, если вы предоставите соответствующие значения для обязательных полей промежуточной модели, используя through_defaults :

Из документации. :

Вы также можете использовать add (), create () или set () для создания отношений, если вы указываете through_defaults для любых обязательных полей:

>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})

Вы можете предпочесть создание экземпляров промежуточной модели напрямую.

Поведение метода .remove() требует небольшого внимания:

Если настраиваемая сквозная таблица, определенная промежуточной моделью, не обеспечивает уникальности для пары (model1, model2), допускающей несколько значений, вызов remove() удалит все промежуточные экземпляры модели:

>>> Membership.objects.create(person=ringo, group=beatles,
...     date_joined=date(1968, 9, 4),
...     invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>

Что касается поля _meta, я не смог получить к нему доступ из поля m2m, но приведенная выше часть документации, кажется, позволяет избежать "гимнастики" доступа к _meta.
Если я найду что-нибудь интересное, я обновлю свой ответ соответственно.

1
John Moutafis 7 Окт 2019 в 18:29