41 | 41 | ⠀ | |
42 | 42 | # https://docs.djangoproject.com/en/dev/ref/settings/#append-slash | |
43 | 43 | APPEND_SLASH = True |
Stating defaults add complexity when reading the code but does not change Django's behaviour.
Read more- | APPEND_SLASH = True |
44 | 44 | ⠀ | |
45 | 45 | # Application definition |
62 | 62 | 'whitenoise.middleware.WhiteNoiseMiddleware', |
The order of middleware affections the outcome. Some middleware are dependant on the functionality of other middleware. For example a middleware that requires usage of request.session should come after the SessionMiddleware.
Read moreYour website is vulnerable to a number of common hacker attacks because MIDDLEWARE
setting is missing django.middleware.security.SecurityMiddleware
.
+ | 'django.middleware.security.SecurityMiddleware', | |||
Expand 12 lines ... |
63 | 63 | 'django.contrib.sessions.middleware.SessionMiddleware', | |
64 | 64 | 'django.middleware.cache.UpdateCacheMiddleware', | |
65 | 65 | 'django.middleware.common.CommonMiddleware', | |
66 | 66 | 'django.middleware.cache.FetchFromCacheMiddleware', | |
67 | 67 | 'django.middleware.csrf.CsrfViewMiddleware', | |
68 | 68 | 'django.contrib.auth.middleware.AuthenticationMiddleware', | |
69 | 69 | 'django.contrib.messages.middleware.MessageMiddleware', | |
70 | 70 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', | |
71 | 71 | ] | |
72 | 72 | ||
73 | 73 | ROOT_URLCONF = 'config.urls' | |
74 | 74 | ||
75 | 75 | TEMPLATES = [ |
DIRS must be absolute paths. Relative paths will not work.
Read more76 | 76 | { | |
77 | 77 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', | |
78 | 78 | 'DIRS': ['core/templates'], | |
79 | 79 | 'APP_DIRS': True, | |
80 | 80 | 'OPTIONS': { | |
81 | 81 | 'context_processors': [ | |
82 | 82 | 'django.template.context_processors.debug', | |
83 | 83 | 'django.template.context_processors.request', | |
84 | 84 | 'django.template.context_processors.i18n', | |
85 | 85 | 'django.contrib.auth.context_processors.auth', | |
86 | 86 | 'django.contrib.messages.context_processors.messages', | |
87 | 87 | 'staticpages.context_processors.get_pages', | |
88 | 88 | 'staticpages.context_processors.get_categories', | |
89 | 89 | 'staticpages.context_processors.get_urls', | |
90 | 90 | ], | |
91 | 91 | }, | |
92 | 92 | }, | |
93 | 93 | ] | |
94 | 94 | ||
95 | 95 | # Static files served with Whitenoise and AWS Cloudfront |
119 | 119 | SECURE_CONTENT_TYPE_NOSNIFF = True | |
120 | 120 | ||
121 | 121 | USE_I18N = True |
Stating defaults add complexity when reading the code but does not change Django's behaviour.
Read more- | USE_I18N = True |
122 | 122 | ⠀ | |
123 | 123 | DIRECTORY_CONSTANTS_URL_GREAT_DOMESTIC = env.str( |
148 | 148 | SSO_PROFILE_URL = 'https://profile.com' |
Your website is vulnerable because the CSRF_COOKIE_SECURE
setting is not set - so hackers have an easier time stealing your CSRF cookies on HTTP connections, allowing them to circumvent your CSRF protection.
Your website is vulnerable because the SESSION_COOKIE_SECURE
setting is not set - so hackers have an easier time stealing your users' session cookies on HTTP connections.
+ | |
+ | |
+ | SESSION_COOKIE_SECURE = os.getenv('SESSION_COOKIE_SECURE_ENABLED') != 'False' |
+ | |
+ | CSRF_COOKIE_SECURE = os.getenv('CSRF_COOKIE_SECURE_ENABLED') != 'False' |
5 | 5 | ⠀ | |
6 | 6 | class StudentPerformance(models.Model): | |
7 | 7 | stident_id = models.CharField(max_length=100, primary_key=True, unique=False) |
Primary key must be unique by definition: it's a field that uniquely identifies the record.
Read moreStating defaults add complexity when reading the code but does not change Django's behaviour.
Read more- | stident_id = models.CharField(max_length=100, primary_key=True, unique=False) | |||
+ | stident_id = models.CharField(max_length=100, primary_key=True) | |||
Expand 2 lines ... |
8 | 8 | registration_id = models.CharField(max_length=10, db_index=True) | |
9 | 9 | academic_year = models.IntegerField() |
14 | 14 | courses_registration_validated = models.NullBooleanField(null=True) |
NullBooleanField
is deprecated and will be removed a future version of Django.
- | courses_registration_validated = models.NullBooleanField(null=True) |
+ | courses_registration_validated = models.BooleanField(null=True) |
3 | 3 | from django.forms import widgets | |
4 | 4 | ||
5 | 5 | from config import settings |
Using from django.conf import settings
simplifies the code and makes it more maintainable.
- | from config import settings |
+ | from django.conf import settings |
6 | 6 | ⠀ | |
7 | 7 | ⠀ |
18 | 18 | return widgets.Media( | |
19 | 19 | js=( | |
20 | 20 | 'admin/js/vendor/jquery/jquery{extra}.js', |
An f-string will not work if the f
prefix is missing.
- | 'admin/js/vendor/jquery/jquery{extra}.js', | |||
+ | f'admin/js/vendor/jquery/jquery{extra}.js', | |||
Expand 2 lines ... |
31 | 31 | path("submit_job_raw/", views.View3.as_view(), name="submit-job"), |
URL names must be unique otherwise reverse('url_name')
and {% url 'url_name' %}
will link to the "wrong" page half of the time.
16 | 16 | user = PlatformUser.objects.get(pk=1) | |
17 | 17 | ||
18 | 18 | if user.group.id != get_request_user_organisation_id(request): |
When working with foreign keys, accessing the related field will result in a database read. That can be eliminated by using *_id
, which is the foreign key value that Django has already cached on the object to make this scenario more efficient.
- | if user.group.id != get_request_user_organisation_id(request): | |||
+ | if user.group_id != get_request_user_organisation_id(request): | |||
Expand 3 lines ... |
19 | 19 | raise PermissionDenied() | |
20 | 20 | ||
21 | 21 | return Response() |
25 | 25 | ⠀ | |
26 | 26 | def post(self, request, pk): | |
27 | 27 | if PlatformUser.objects.filter(group=1).count() > 0: |
Comparing queryset.count()
is less efficient than checking queryset.exists()
, so use querySet.count()
if you only want the count, and use queryset.exists()
if you only want to find out if at least one result exists.
- | if PlatformUser.objects.filter(group=1).count() > 0: | |||
+ | if PlatformUser.objects.filter(group=1).exists(): | |||
Expand 2 lines ... |
28 | 28 | raise PermissionDenied() | |
29 | 29 | return Response() | |
30 | 30 | ⠀ |
34 | 34 | ⠀ | |
35 | 35 | def post(self, request, pk): | |
36 | 36 | if PlatformUser.objects.filter(group=1): |
This can load every row in the database table into memory because the queryset is evaluated. Checking if a queryset is truthy/falsey is much less efficient than checking queryset.exists()
.
- | if PlatformUser.objects.filter(group=1): | |||
+ | if PlatformUser.objects.filter(group=1).exists(): | |||
Expand 2 lines ... |
37 | 37 | raise PermissionDenied() | |
38 | 38 | return Response() | |
39 | 39 | ⠀ |
45 | 45 | user = PlatformUser.objects.all().order_by('?')[0] |
Using order_by('?')
can be very inefficient if you have lots of rows in the table. Moving the randomness to the application layer will probably give significant a performance improvement.
- | user = PlatformUser.objects.all().order_by('?')[0] |
+ | user = PlatformUser.objects.all()[random.randint(0, PlatformUser.objects.count() - 1)] |
23 | 23 | operations = [migrations.RunPython(migrate_unlocks)] |
It's good to, as a minimum, specify noop
in RunPython
so the migration can be skipped when going backwards, and even better to specify a function that undoes the migration.
- | operations = [migrations.RunPython(migrate_unlocks)] |
+ | operations = [migrations.RunPython(migrate_unlocks, migrations.RunPython.noop)] |