Search code examples
pythondjangodatabasedata-structures

Django Filter __In Preserve Order


This question is regarding Django filtering, with __In clause. Basically, I am trying to understand how to filter by providing videos.filter(video__in = video_ids) with string ids, so that each row that gets returned has the same order as the order of string ids in the list used for filtering.

For more context, filtering is done using string ids for e.g. ['7C2z4GqqS5E','kTlv5_Bs8aw','OK3GJ0WIQ8s','p8npDG2ulKQ','6ZfuNTqbHE8']. Model video field is video id described above. It is different from the integer id.

The video_id_order used below was something I had tried that gave error Field 'id' expected a number but got '7C2z4GqqS5E', I believe this is not being used correctly, and something is still missing to get the order of results preserved as it is in video_ids. What should be the correct order function in this case or is this approach even correct with Django models to preserve query-set result when __In clause is used?

def get_video_objects(video_ids,count):
    '''
    video_ids are already in the correct order, filter should return rows in the same order
    '''
    videos = Video.objects.all()
    // video_id_order = Case(*[When(id=id, then=pos) for pos,id in enumerate(video_ids)])
    results = videos.filter(video__in = video_ids)
    // results = results.order_by(video_id_order)
    results = list(results[:int(count)]\
    .values('video','title','comment_count','likes'))
    return results

Video model is as following

class Video(models.Model):
    id = models.PositiveBigIntegerField(primary_key=True,serialize=False,auto_created=False)
    video = models.SlugField(unique=True,null=False,primary_key=False)
    title = models.CharField(max_length=200)
    comment_count = models.PositiveIntegerField(default=0)
    likes = models.PositiveIntegerField(default=0)

Solution

  • Try changing "id" param to "video" in the Case() since you are iterating over the strings

    video_id_order = Case(*[When(video=id, then=pos) for pos,id in enumerate(video_ids)])
    results = videos.filter(video__in = video_ids)
    results = results.order_by(video_id_order)
    

    `