0

I have two models as below:

class Stop(models.Model):
    """
    Showing bus stops in İzmir.
    """

    code = models.PositiveIntegerField(
        unique=True,
        primary_key=True,
        verbose_name="Code"
    )

    label = models.CharField(
        null=False,
        blank=False,
        max_length=64,
        verbose_name="Label"
    )

    coor = ArrayField(
        models.FloatField(),
        size=2,
        verbose_name="Coordination"
    )

    class Meta:
        verbose_name = "Stop"
        verbose_name_plural = "Stops"
        ordering = ["label"]

    def __str__(self):
        return self.label

class Route(models.Model):
    """
    Bus routes of İzmir.
    """

    code = models.PositiveSmallIntegerField(
        unique=True,
        primary_key=True,
        verbose_name="Code"
    )

    stops = models.ManyToManyField(
        Stop,
        null=True,
        blank=True,
        related_name="routes",
        verbose_name="Stops"
    )

    terminals = ArrayField(
        models.CharField(
            null=False,
            blank=False,
            max_length=32,
        ),
        size=2,
        default=[],
        verbose_name="Terminals"
    )

    departure_times = ArrayField(
        ArrayField(
            models.TimeField(
                null=False,
                blank=False
            ),
            null=True,
            default=[]
        ),
        default=[],
        size=6,
        verbose_name="Departure Times"
    )

    class Meta:
        verbose_name = "Route"
        verbose_name_plural = "Routes"
        ordering = ["code"]

    def __str__(self):
        return "{}: {} - {}".format(str(self.code), self.terminals[0], self.terminals[1])

As you can see, Route has a ManyToManyFields which takes Stop instances.

I put the instances with a script which it scraps a couple of web page, it seems I will use crontab to keep them updated. In the data I am scraping, Stop objects are ordered. The thing is, there are no significant filter to sort e.g. a Stop instance comes after another.

Django (or Django Rest Framework) returns Stop instances of Route instance in alphabetic order, e.g.

{
    "code": 285,
    "terminals": [
        "EVKA 1",
        "KONAK"
    ],
    "stops": [
        40586,
        40633,
        12066,
        40645,
        40627,
        40647,
        40588,
        40592,
        40623,
        40016,
        40506,
        40508,
        40528,
        40462,
        40631,
        40014,
        40619,
        40530,
        12060,
        40661,
        40504,
        40488,
        40653,
        40590,
        40512,
        40464,
        10240,
        10036,
        12068,
        40514,
        40510,
        40658,
        40002,
        40649,
        12070,
        40004,
        40010,
        40656,
        12064,
        40614,
        40012
    ],
    ...
}

In which stops[0] returns a Stop instance beginning with A and sorts like that.

So, is there a way to order like a list in Python? Like, there is no significant point, you just append to the end and return so.


Environment

  • python 3.5.1
  • django 1.9.7
  • djangorestframework 3.3.3
  • psycopg2 2.6.2
  • postgresql 9.5
Eray Erdin
  • 2,633
  • 1
  • 32
  • 66
  • If you put a time stamp on the `Stop` model, then you could order based on that time stamp. – Aaron Lelevier Jul 20 '16 at 21:05
  • The thing is, a `Stop` instance might belong to more than one Route objects. So even if I put timestamp to it, the `timestamp` field of a `Stop` instance would change when it comes to build another `Route` instance. – Eray Erdin Jul 21 '16 at 05:06

1 Answers1

1

The position of a stop is relative to a Route, e.g. one stop can be first for route 1, 2nd for route 2 and etc. So this is a perfect example that you need more metadata about the Route-Stop relation. Djagno solves this by letting you provide a Intermediate Table with two ForeignKey and the metadata you need for the relation.

class Stop(models.Model):
    #...

class Route(models.Model):

    #...

    stops = models.ManyToManyField(Stop, through='RouteStop', blank=True, related_name="routes", verbose_name="Stops")


class RouteStop(models.Model):
    stop = models.ForeignKey(Stop)
    route = models.ForeignKey(Route)
    position = models.PositiveSmallIntegerField()

    class Meta:
        unique_together = (("stop", "route"),)

Now when you get Routes you can order route.stops by RouteStop.position, something like:

Route.objects.all().prefetch_related(
    Prefetch('stops', queryset=Stop.objects.all().order_by('routestop__position'))
)
Todor
  • 15,307
  • 5
  • 55
  • 62
  • Should your `route = models.ForeignKey(Stop)` line be `route = models.ForeignKey(Route)`? Or maybe I am wrong and code is valid? – Eray Erdin Jul 21 '16 at 05:12
  • It also raises when I try to migrate: `ValueError: Cannot alter field izmir.Route.stops into izmir.Route.stops - they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)` – Eray Erdin Jul 21 '16 at 05:21
  • Yes, it should be `Route` (a typo clearly). About the migration, it won't happen automatically, you will need to make a [data migration](http://stackoverflow.com/questions/33257530/how-to-add-through-option-to-existing-manytomanyfield-with-migrations-and-data-i) – Todor Jul 21 '16 at 05:49