результаты поиска из метода модели Django, включая результаты предыдущих вызовов этого же метода - PullRequest
1 голос
/ 29 октября 2019

У меня есть модель Django, которая отслеживает идентификаторы сообщений электронной почты, когда сообщение проходит через разные серверы. У него есть поле models.ForeignKey, поэтому мы можем связать их вместе, чтобы следовать по полному пути сообщения через множество серверов.

Модель имеет метод 'get_children ()', который рекурсивно ищет все сообщения, происходящие от данного сообщения.

class Relay(models.Model):
    hostname = models.CharField(max_length=48)
    qid = models.CharField(max_length=24)
    received_from = models.ForeignKey(
        "self",
        blank=True,
        null=True,
        default=None,
        on_delete=models.SET_DEFAULT
    )
    def get_children(self, parent_messages=set()):
        for r in Relay.objects.filter(received_from=self):
            r.get_children(parent_messages)
        parent_messages.add(self)
        p = list(parent_messages)
        parent_messages = set()
        return p

Если я запускаю один запрос из оболочки Django, она работаеткак и ожидалось. «Соло» сообщения возвращают только сами. Также встречаются сообщения с несколькими дочерними / дочерними сообщениями.

    >>> r = Relay.objects.get(qid='xo2')
    >>> r.get_children()
    [<Relay: server2 foo>, <Relay: server3 bbb>, <Relay: server1 xo2>]

Если я уничтожу и перезапущу оболочку, следующий запрос будет работать как положено, получая одно сообщение

    >>> r = Relay.objects.get(qid='singletonMsg')
    >>> r.get_children()
    [<Relay: server5 singletonMsg>]

Ноесли я неоднократно запускаю get_children () в течение одного сеанса оболочки Django, он всегда включает результаты предыдущих вызовов get_children ().


    >>> r = Relay.objects.get(qid='singletonMsg')
    >>> r.get_children()
    # expected result
    [<Relay: server5 singletonMsg>]  
    >>>
    >>> r = Relay.objects.get(qid='xo2')
    >>> r.get_children()
    # unexpected result - should not include singletonMsg 
    [<Relay: server2 foo>, <Relay: server3 bbb>, <Relay: server5 singletonMsg>, <Relay: server1 xo2>]
    >>>
    >>> r = Relay.objects.get(qid='singletonMsg')
    >>> r.get_children()
    # unexected result - should just return singletonMsg ??
    [<Relay: server2 foo>, <Relay: server3 bbb>, <Relay: server5 singletonMsg>, <Relay: server1 xo2>]

Я первоначально возвращал набор «parent_messages» из функции. Я попытался вернуть [m для m в parent_messages] и текущий list (), думая, что это проблема закрытия, но не повезло. Я в тупике. Заранее спасибо за любой совет.

1 Ответ

1 голос
/ 29 октября 2019

Когда вы пишете код:

def get_children(self, parent_messages=set()):
    ...

Вы определяете заданный объект в памяти, который называется parent_messages. Поскольку заданные объекты изменяемые , каждая ссылка на parent_messages фактически ссылается на один и тот же объект, и каждое изменение, сделанное в parent_messages, изменяет один и тот же объект в памяти. Когда вы делаете дополнительный вызов get_children, вы по-прежнему обращаетесь к исходному объекту parent_messages, который вы определили.

См. Аргумент Mutable по умолчанию для более подробного объясненияthis.

Теперь, чтобы ответить на ваш вопрос, я настоятельно рекомендую использовать django-mptt (pip install django-mptt). Это значительно упростит код, необходимый для выполнения этого:

from mptt.models import MPTTModel

class Relay(MPTTModel):
    hostname = models.CharField(max_length=48)
    qid = models.CharField(max_length=24)
    received_from = models.ForeignKey(
        "self",
        blank=True,
        null=True,
        default=None,
        on_delete=models.SET_DEFAULT
    )

выполнение:

>>> r = Relay.objects.get(qid='xo2')
>>> r.get_descendants()
[<Relay: server2 foo>, <Relay: server3 bbb>, <Relay: server1 xo2>]

get_descendants получает всех дочерних элементов r и всех их дочерних элементов и т. Д. ,Если вы хотите обратиться только к прямым потомкам r, используйте вместо этого get_children.

...