Click here to Skip to main content
15,881,821 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
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.



What I have tried:

The model for stadium is:
# -*- 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 is:

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





Suppose there are two stadiums, 1. Old Trafford and 2. Etihaad Stadium. They both are neighbouring stadiums with distance of 12.5km between them. If I add Etihaad stadium as neighbour of Old Trafford then Old Trafford should be automatically added as neighbour in Etihaad's model data. Right now, it's now happening with my code. What am I doing wrong? How can self referencing ManyToMany relation be made truly symmetrical in Django? Is there any third party library for this? Also, how to enable data entry in django admin and serialize it in DRF ?
Posted
Updated 24-Jul-19 5:52am

1 solution

They're ALL "neighbors"; the only difference is the "distance".

You're asking the wrong questions.

Given a stadium, find (at run rime) all other stadiums within 10k (for example); not, "who are my neighbors?"

All it requires is Lat/Long and the ability to calculate a distance between 2 points ... no "bidirectional virtual siblings".
 
Share this answer
 
Comments
Member 14539300 24-Jul-19 18:14pm    
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.

Just let me know if two way symmetrical self referencing ManyToMany relation is possible in Django with 'through' intermediate model.

If you run above code, I am able to add or remove neighbours. If B is added as neighbour of A then I am able to see it under A. What I want is that A should be automatically added as neighbour of B.

Thanks.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900