Static Files Versioning in Django

Installation

It is pretty easy in 2.2+, just add to your settings:

STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'

Don’t forget to set:

DEBUG = False

And do collectstatic. This way Django will run over all of your files and search for them in your statics directory.

ValueError: the file <file_path> could not be found

Be careful with urls – they might work on the website, but it the storage module may not identify the paths correctly. This error occurs:

ValueError: The file 'css/plugins/fonts/fontawesome-webfont.eot' could not be found with <django.contrib.staticfiles.storage.ManifestStaticFilesStorage object at 0x12ae4e220>.

Because it has a path like this

fonts/fontawesome-webfont.eot

You need to make it relative:

../../fonts/fontawesome-webfont.eot

RuntimeError: Max post-process passes exceeded.

If you see an error like this:

Post-processing 'All' failed!
RuntimeError: Max post-process passes exceeded.

Then check your @import statements – remove the commented once and try to find where the import is circular.

Conclusion

Versioning does not tolerate uncarefully made static files. You may spend a few hours removing rudimental imports, files, altering paths, removing urls to non-existent files. That’s totally normal.

A short note about JS

I have been working with JavaScript lately, and I enjoy it so much. It gives me the whole “mobile development vibe” but it is much quicker and easier – no need to build all the time etc. I have to say, it is inclining me to resuming mobile dev activities… not gonna lie.

Just a short note to tell you about the fun I’ve having 😉

How to speed up Django Admin: Foreignkey and ManyToManyField

If you have a Foreignkey or ManyToManyField in your admin form, but you don’t need to change it in any way, replace it with a label. Say, you have a model:

class Student(models.Model):
    ...
    class_obj = models.ForeignKey('Class', on_delete=models.CASCADE)

And in your Django Admin you want to have thic field:

@admin.register(Student)
class StudentAdmin(admin.ModelAdmin):
    fields = ('class_obj',)

There is no problem if you have, say, 10-100 Class instances, but if you have thousands of them – the interface would take a lot of time to load.

Instead of that, make a label property and replace the original field with it.

class Student(models.Model):
    ...
    class_obj = models.ForeignKey('Class', on_delete=models.CASCADE)

    @property
    def class_obj_str(self):
        return self.class.name

And then in the ModelAdmin:

@admin.register(Student)
class StudentAdmin(admin.ModelAdmin):
    readonly_fields = ('class_str',)
    fields = ('class_obj_str',)

Moving Django to a subpath + Nginx + gunicorn (upd. 18.11.21)

upd. I forgot to include the most important line in the Nginx config. Sorry for that. but it does not work for me anymore. I am confused but I am postponing this for a few days to finish top priority tasks.

Super easy, super fun. Let’s base it on this great guide from Digital Ocean.

Say, the subpath you want is /secret/ 👀.

Add it to Nginx config:

server {
    listen 80;
    server_name server_domain_or_IP;

    location /secret/static/ {
        root /home/sammy/myprojectdir;
    }

    location /secret/ {
        rewrite /api(.*) $1 break;   # !!!!!!!!!!!!!! VERY IMPORTANT
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Then, things we will change in settings.py:

  • in Nginx root means that in the directory it will look for a secret/static/ directory. But now you only have a regular static/.
  • Currently your Django generated static links are all example.com/static, we will change that too.

Your new settings variables will be something like this:

STATIC_URL = '/secret/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'secret/static/'

MEDIA_URL = '/secret/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'secret/media/'
FORCE_SCRIPT_NAME = 'secret'

That’s it. Go check all absolute paths that you wrote down in your project and you are good.

get_or_create created 2 instances [Sloppy Development]

I am a woman enough to talk about this. I usually don’t let these things happen, but this is such a small thing that let me down on a really high amount of requests.

The statistics of an API I was using showed that at 00:00 UTC we stopped using it altogether.

So, the culprit is the simplest model ever:

class Date(models.Model):
    date = DateField()

And I only ever created it (or altered) like this:

    date, cr = Date.objects.get_or_create(
        day=f'{this_moment.year}-{this_moment.month}-{this_moment.day}'

As it always happens, 4am Sunday it well went to hell – Django create two instances of ‘2021-08-15’.

Eventually every requests that had this line in it just crashed.

Why did this happen? Well, apparently two instances are not supposed to be made.

My friends and I came to a conclusion that with a very high amount of requests Django missed that tiny bit of time between executing get_or_create method and actually writing an instance to PostgreSQL.

Well good thing - I went to bed late, and my team woke me up much earlier than I would wake up.

What is he solution? If the field is meant to be unique, make it unique:

class Date(models.Model):
    date = DateField(unique=True)

[Note] [Lifestyle] FireFox on Linux + Django + Nginx = 499 ERROR omfg

I spent 6 hours today in Nginx configurations and logs, I learnt so much about Nginx and devops process. And in the end it was all because FireFox browser (I use desktop Ubuntu on my laptop) refused to connect to the server and get the sweet HTML pages I tried to serve it.

In particular, when I renounced attempts to serve HTML pages with explicitly stated location tags and launched my good old trusted Django and it STILL didn’t work (I got 499/403 in the access.log and 404 in the browser), I came to thinking something evil is going on here. It was an unexpected, and a bad one.

The moment I opened it in Google Chrome and it worked, dudes, if I was standing, I would fall that moment on my butt.

I immediately wrote to my Python friends "screw web, I will do webcam now". But, it was a rushed thought I soon gave up. Thankfully, I did not write that to my boss.

[Note] ‘ChatConsumer’ has no attribute ‘as_asgi’ – Sloppy development

Following Channels’ docs I encountered yet another error the solution to what was not obvious right off the bat.

Ditch Channels.

The End.

This was my original post in October 2020 and I stand by it.

You can make your project work with Google Firebase - it handles signals well enough. Yes it is primarily used by mobile developers, but overall it is fine for web too.

I spent trying to install Django Channels on my local Linux 18.04, and I kind of made it work, but then I had to deploy it... And as a professional backender you don't want to let people down.

I decided to try Channels because my Python-related friends talked about it. But days into the trouble I asked my friend about a certain step and he asked me in return "oh so [you use it because] they fixed Channels since two years ago?". I as like ??????????????????. So that moment I ditched it. Too much time wasted, but I like the result I achieved with Firebase.

What I am saying - Firebase is fast and universal. In a matter of days I managed to make and deploy an internal Customer Support chat service that connected mobile app users and a support from the web.</p>

I will be writing a new series on Django + Firebase, and this post is pretty much a short introduction.

Make a JSON from PostgreSQL table (for Django)

First, choose a folder you want to copy the table to and grant postgres user access to it. I chose /home:

sudo chown -R postgres:postgres /home

Then log into postgres user:

sudo su - postgres

Then get to your database. If it is a Django project, you can find the name in settings.py:

psql dbname

Then, if you need your app’s tables, make a table name from these components:

{{ app_name }}_{{ model_name }}

For app userapp and model User, I have userapp_user. So in a /home/users.json I want to copy:

\t
\a
\o /home/users.json
SELECT row_to_json(r) FROM userapp_user AS r;

That’s it.