Member 14539300 Ответов: 1

В django, как создать самореферентные, действительно симметричные отношения manytomany с некоторыми дополнительными полями под этими отношениями(в 2019 году)?


I’ve a model that gives details about a stadium. Once user is on the page of stadium, there’ll be list of other stadiums which are in neighbouring vicinity to current stadium, along with their distances from current stadium. I want to implement this in django. So, suppose there are two stadiums , Stadium A and stadium B which are closer to each other. Then adding stadium B as neighbour of stadium A should automatically create stadium A as neighbour of stadium B. This is different than facebook friends or twitter followers. In facebook, symmetrical relationship is not established as long as friend request isn’t accepted by the person whom request has been sent. So, person A can be said as friend of person B when he sends friend request to person B but reverse is not true if person B does not accept the request. In twitter followers, person A may follow person B but reverse may not be true. So, those are not truly symmetrical relationships. But, in my case, if two stadiums are in close proximity then they are each other’s neighbours. Adding one as neighbour of another should automatically create reverse relation. I am not able to implement this in Django. I’ve referred similar questions and referred article https://charlesleifer.com/blog/self-referencing-many-many-through/ on this. I’ve created a model where neighbour can be added but it’s not creating reverse relationship.



Что я уже пробовал:

Модель для стадиона такова:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericRelation
from autoslug import AutoSlugField


class Stadium(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    neighbours = models.ManyToManyField(
        'self', through='Neighbour', symmetrical=False, related_name='related_to+')
    name = models.CharField(max_length=65)
    capacity = models.PositiveIntegerField()
    built = models.CharField(max_length=20, blank=True, null=True)
    slug = AutoSlugField(
        populate_from="name")

    def add_neighbour(self, stadium, distance_in_length, distance_in_time, symm=True):
        neighbour, created = Neighbour.objects.get_or_create(
            from_stadium=self, to_stadium=stadium, distance_in_length=distance_in_length, distance_in_time=distance_in_time)
        if symm:
            # avoid recursion by passing 'symm=False'
            stadium.add_neighbour(self, distance_in_length,
                                  distance_in_time, False)
        return neighbour

    def remove_neighbour(self, stadium, distance_in_length, distance_in_time, symm=True):
        Neighbour.objects.filter(
            from_stadium=self,
            to_stadium=stadium,
            distance_in_length=distance_in_length,
            distance_in_time=distance_in_time).delete()
        if symm:
            # avoid recursion by passing `symm=False`
            stadium.remove_neighbour(self, distance_in_length,
                                     distance_in_time, False)

    def get_neighbours(self, distance_in_length, distance_in_time):
        return self.neighbours.filter(
            to_stadium__distance_in_length=distance_in_length,
            to_stadium__distance_in_time=distance_in_time,
            to_stadium__from_stadium=self)

    def __str__(self):
        return self.name


class Neighbour(models.Model):
    from_stadium = models.ForeignKey(Stadium, related_name='from_stadium')
    to_stadium = models.ForeignKey(Stadium, related_name='to_stadium')
    distance_in_length = models.DecimalField(
        max_digits=5, decimal_places=2, blank=True, null=True)
    distance_length_units = (
        ('km', 'KiloMeters'),
        ('m', 'meters')
    )
    distance_length_unit = models.CharField(
        max_length=3, choices=distance_length_units, blank=True, null=True)
    distance_in_time = models.DecimalField(
        max_digits=4, decimal_places=2, blank=True, null=True)
    distance_time_units = (
        ('hours', 'Hrs'),
        ('minutes', 'minutes')
    )
    distance_time_unit = models.CharField(
        max_length=7, choices=distance_time_units, blank=True, null=True)

    class Meta:
        unique_together = ('from_stadium', 'to_stadium')


Admin.py есть:

from django.contrib import admin
from .models import (
    Stadium,
    NearByFacilities,
    Neighbour
)

class NeighbourInline(admin.StackedInline):
    model = Neighbour
    fk_name = 'from_stadium'


class StadiumAdmin(admin.ModelAdmin):
    inlines = [NeighbourInline]


admin.site.register(Stadium, StadiumAdmin





Предположим, что есть два стадиона: 1. Олд Траффорд и 2. Стадион Этихад. Оба они являются соседними стадионами с расстоянием между ними 12,5 км. Если я добавлю стадион Etihaad в качестве соседа Олд Траффорда, то Олд Траффорд должен быть автоматически добавлен в качестве соседа в модельных данных Etihaad. Прямо сейчас это происходит с моим кодом. Что я делаю не так? Как можно сделать самореферентное отношение ManyToMany действительно симметричным в Django? Есть ли для этого какая-нибудь сторонняя библиотека? Кроме того, как включить ввод данных в django admin и сериализовать его в DRF ?

1 Ответов

Рейтинг:
2

Gerry Schmitz

Все они - "соседи", разница лишь в "расстоянии".

Ты задаешь неправильные вопросы.

Учитывая стадион, найдите (в run rime) все другие стадионы в пределах 10k (например); а не "кто мои соседи?"

Все, что для этого требуется, - это Lat/Long и возможность вычислить расстояние между 2 точками ... никаких "двунаправленных виртуальных братьев и сестер".


Member 14539300

No, we are not using google maps. Another important things: there is going to be information about only specific stadiums in database and not all the stadiums. Stadium model is not only about neighbours. Their main purpose is different. Just assume, you are having detailed information about stadium A on a page dedicated to stadium A. Suppose there are 3 neighbours to this stadium, let's name them B,C,D. If we've selected only B & C for our site and not D then only B &C will have their information in database and not D. In this scenario, B & C should have hyperlink to their detailed pages on page of A under the section 'other nearby stadiums'. This is the reason above model is the way it is. At this point, there is no way to change whole core approach to the problem.

Просто дайте мне знать, возможно ли двустороннее симметричное самореферентное отношение ManyToMany в Django с "сквозной" промежуточной моделью.

Если вы запускаете вышеописанный код, я могу добавлять или удалять соседей. Если B добавляется как сосед A, то я могу видеть его под A. Я хочу, чтобы A автоматически добавлялся как сосед B.

Спасибо.