Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Device Detection Using CSS Media Queries and Flask/Jinja

I'm trying to use CSS media queries with Jinja and Flask to determine device type and serve appropriately sized images based on the device. Requests from mobile phones would get smaller images than desktops, tablets, etc.

The problem is that {{ post|get_image(post, 480) }} gets evaluated independent of the media query thus triggering the get_image custom template filter for each instance. get_image is also where the correct image is rendered for the device, so multiple, perhaps unnecessary, calls can negatively effect performance.

Ideally, only one media query is enacted with one call to get_image, but the challenge is that the media query and Jinja are completely independent of each other in that one knows nothing about the other.

I'd really appreciate alternative approaches or suggestions to improve my approach. Thanks in advance!

Here's an example of the media queries:

  @media only screen and (max-device-width: 480px) {
    .post {
      background: url("{{ post|get_image(post, 480) }}") no-repeat center center fixed; 
    }
  }

  @media only screen and (max-device-width: 1224px) {
    .post {
      background: url("{{ post|get_image(post, 1224) }}") no-repeat center center fixed; 
    }
  }
like image 442
Raj Avatar asked Jan 26 '26 11:01

Raj


1 Answers

Change your setup to be something like this:

.post {
    background: none no-repeat center center fixed;
}

@media only screen and (max-device-width: 480px) {
    .post {
      background-image: url("{{ get_image_url(post, 480) }}"); 
    }
}

@media only screen and (max-device-width: 1224px) {
    .post {
      background-image: url("{{ get_image_url(post, 1224) }}"); 
    }
}

The URLs you are generating might look something like this:

/posts/<int:post_id>/background?s=NNNN

Then, set up your proxy server to serve such images out of a cache directory and to forward them on to your image generation controller if the image does not exist:

# Fake server configuration
# for no server known to man
location /posts/(:post-id:\d+)/background {
    try_files /cache/:post-id:_\d+\.png or @flask-server  
}

Finally, in your Flask controller generate the image, save it to your cache directory, and serve it back to the requester:

@app.route("/posts/<int:post_id>/background")
def generate_image(post_id):
    # Assume that if we got here the image does not yet exist
    dimensions = int(request.args.get("s", 800), 10)

    image_data = generate_image(post_id, dimensions)

    image_name = "{}_{}.png".format(post_id, dimensions)
    image_path = os.path.join("/cache", image_name)

    with open(image_path, "wb") as image:
        image.write(image_data)

    return Response(image_data, content_type="image/png")

If the image generation part is too expensive to even contemplate doing in the controller you can instead return 204 No-Content with Cache-Control set to private and Expires set in the past and put the image generation task into an out-of-process queue. Then just poll that endpoint in your JavaScript code until the image is ready to be served by your proxy server.

like image 197
Sean Vieira Avatar answered Jan 29 '26 00:01

Sean Vieira