Django/Best Practices for Structuring a Django Project.md
... ...
@@ -0,0 +1,458 @@
1
+https://itnext.io/best-practices-for-structuring-a-django-project-23b8c1181e3f
2
+
3
+# Best Practices for Structuring a Django Project | by eyong kevin | ITNEXT
4
+[
5
+
6
+![eyong kevin](https://miro.medium.com/v2/resize:fill:88:88/1*q4RW7ZWB93wmoN_ZJZlPWg.jpeg)
7
+
8
+
9
+
10
+](https://medium.com/@tonyparkerkenz?source=post_page---byline--23b8c1181e3f--------------------------------)
11
+
12
+[
13
+
14
+![ITNEXT](https://miro.medium.com/v2/resize:fill:48:48/1*yAqDFIFA5F_NXalOJKz4TA.png)
15
+
16
+
17
+
18
+](https://itnext.io/?source=post_page---byline--23b8c1181e3f--------------------------------)
19
+
20
+Best Practices for Structuring a Django Project
21
+
22
+In this article, I’ll walk you through a boilerplate I created for my Django projects. It uses best practices and will permit you to build industrial standard Django projects.
23
+
24
+Instead of struggling with your Django project structures. Rather, spend your energy building exciting features.
25
+
26
+> It has received much appreciation from my peers and students from DCI and CF.
27
+>
28
+> **NB**: Check my [Django boilerplate](https://github.com/Eyongkevin/django-boilerplate) from github
29
+
30
+We will cover the following;
31
+
32
+* Starting with a virtual environment
33
+* Creating Django Project
34
+* Requirement
35
+* Settings
36
+* Django Applications
37
+* Command shortcuts with Makefile
38
+
39
+Starting with a virtual environment (VE)
40
+----------------------------------------
41
+
42
+> A Virtual Environment is a lightweight Python installation with its own package directories
43
+
44
+Installing packages globally usually requires elevated privileges such as `root`, `sudo`, etc. When this is done, the package `setup.py` is executed with superuser privileges. This is a huge security risk if the package contains malware
45
+
46
+Also, the command `pip isntall -U <package>` installs and updates the package and all its dependencies. This may mess with the existing packages or even break your other projects.
47
+
48
+Lastly, installing everything globally pollutes the list of packages, making it hard to keep track of specific project’s dependencies.
49
+
50
+Creating a VE and Install Django
51
+--------------------------------
52
+
53
+Since `Python 3.6`, we can create VE with the command
54
+
55
+```
56
+python3 -m venv <folder-name> [--prompt <ve-name>]
57
+```
58
+
59
+
60
+**NB**: In Ubuntu, we must install `python3-venv` via `apt` . This is because `venv` is excluded from the default Python installation in Ubuntu
61
+
62
+Another alternative would be `virtualenv` which can be installed globally with `pip install virtualenv`
63
+
64
+Let’s now create a virtual environment for our Django project. In a folder called `Django_First` , open a terminal, and run the command
65
+
66
+```
67
+python -m venv .venv --prompt django-first
68
+```
69
+
70
+
71
+After we have created our virtual environment, we need to source or activate it before installing any packages.
72
+
73
+```
74
+source .venv/bin/activate
75
+```
76
+
77
+
78
+Then, install Django. Here I am using `~=` to install the newest version within the same major release, which is, `5`in the case below.
79
+
80
+```
81
+pip install Django ~=5.0.0
82
+```
83
+
84
+
85
+Creating Django Project
86
+-----------------------
87
+
88
+To create a Django project, we use the command `django-admin` and subcommand `startproject` . Then we specify some folders after that.
89
+
90
+```
91
+django-admin startproject <project-name> [<config-folder-name>]
92
+```
93
+
94
+
95
+Say, we want our project name to be `djangofirst` . Many newbies will create it like this
96
+
97
+```
98
+django-admin startproject djangofirst
99
+```
100
+
101
+
102
+This will be the structure of your project if you do it as above;
103
+
104
+```
105
+.
106
+├── Django_First
107
+│ └── djangofirst
108
+│ ├── djangofirst
109
+│ │ ├── __init__.py
110
+│ │ ├── asgi.py
111
+│ │ ├── settings.py
112
+│ │ ├── urls.py
113
+│ │ └── wsgi.py
114
+│ └── manage.py
115
+```
116
+
117
+
118
+We can notice a lot of indented folders and some with the same name. In this example, we created a folder for our project to be `Django_First` where we created our virtual environment. Then running the Django start project command, we ended up with two folders with the same name.
119
+
120
+**NB**: To specify the name of the configuration folder, we need to be sure that the folder exists already in that directory.
121
+
122
+Best Practice
123
+-------------
124
+
125
+Given that we have created the folder that carries the name of our project, to effectively create a Django project without having all the nested folders, we can run the command
126
+
127
+```
128
+django-admin startproject config .
129
+```
130
+
131
+
132
+This will be our new structure
133
+
134
+```
135
+.
136
+├── Django_First
137
+│ ├── config
138
+│ │ ├── __init__.py
139
+│ │ ├── asgi.py
140
+│ │ ├── settings.py
141
+│ │ ├── urls.py
142
+│ │ └── wsgi.py
143
+│ └── manage.py
144
+```
145
+
146
+
147
+From this new structure, the initial name of our project is maintained, and the configuration is now `config` .
148
+
149
+Requirements
150
+------------
151
+
152
+When it’s time to install packages, most developers will search for a package on `pypi` and install the version they want, or they’ll just use `pip install <package-name>` . And the latest version will be installed. Others will have just a single `requirements.txt` file.
153
+
154
+Best Practice
155
+-------------
156
+
157
+If you are careful with backward compatibility and, also aware of the different environments you are running your project in, then you’ll want to make sure everyone running your project is using the right version. Also, unnecessary packages should not be installed in the environments.
158
+
159
+A `requirements.txt` file is important for complex projects so that, anyone can get your project, create their virtual environment, and install all packages with the right version from this file, with the command `pip install -r requirements.txt` .
160
+
161
+Here, we will consider the different environments our project can run in. For example; `development` , `stagging` , `production` , etc. We don’t want a package like `ipython` , which is commonly used during development to be installed in production for example.
162
+
163
+So, we will create the different environments with their specific requirement files and packages.
164
+
165
+* `base.txt` : This will contain packages that are common to all environments
166
+* `dev.txt` : This will contain packages used only in development
167
+
168
+```
169
+.
170
+├── Django_First
171
+│ ├── config
172
+│ │ ├── __init__.py
173
+│ │ ├── asgi.py
174
+│ │ ├── settings.py
175
+│ │ ├── urls.py
176
+│ │ └── wsgi.py
177
+│ ├── manage.py
178
+│ └── requirements
179
+│ ├── base.txt
180
+│ └── dev.txt
181
+```
182
+
183
+
184
+Now that we have our structure, let’s look at the packages we could install for each environment.
185
+
186
+```
187
+# base.txt
188
+Django~=5.0
189
+django-environ==0.11.2
190
+# dev.txt
191
+-r base.txt
192
+ipython==8.18.1
193
+django-extensions==3.2.3
194
+```
195
+
196
+
197
+Here, we have
198
+
199
+* `Django` : which is the main framework,
200
+* `django-environ` : to read environment variables
201
+* `ipython` : for a powerful interactive Python shell
202
+* `django_extensions` : To auto-import necessary packages and modules from Django when we start a shell.
203
+
204
+**NB**: Installing from `dev.txt`indirectly install from `base.txt` thanks to the line `-r base.txt` . Also, many other environments can be created like production, etc. Check [Django Web Application to Railway](https://www.youtube.com/watch?v=dtze68tNVbI) to see how I used the production environment to host a Django project on railway.
205
+
206
+Settings
207
+--------
208
+
209
+By default, Django comes with a file `setting.py`that contains settings for the Django project. This is okay for a very simple Django project.
210
+
211
+Best Practice
212
+-------------
213
+
214
+Just like we did for the requirements above, we want to be sure we can handle many different environments. Plus having configurations for all the environments in one file can’t be scalable.
215
+
216
+Here, we will create a folder called `settings/`then in it, create different Python modules based on the environments.
217
+
218
+```
219
+.
220
+├── Django_First
221
+│ ├── config
222
+│ │ ├── __init__.py
223
+│ │ ├── asgi.py
224
+│ │ ├── settings
225
+│ │ │ ├── base.py
226
+│ │ │ └── dev.py
227
+│ │ ├── urls.py
228
+│ │ └── wsgi.py
229
+│ ├── manage.py
230
+│ └── requirements
231
+│ ├── base.txt
232
+│ └── dev.txt
233
+```
234
+
235
+
236
+* `base.py` : This will contain configurations common to all environments
237
+* `dev.py` : This will contain the development configurations
238
+
239
+**base.py**
240
+
241
+```
242
+from pathlib import Path
243
+BASE_DIR = Path(__file__).resolve().parent.parent.parent
244
+DEFAULT_APP = [
245
+ "django.contrib.admin",
246
+ "django.contrib.auth",
247
+ "django.contrib.contenttypes",
248
+ "django.contrib.sessions",
249
+ "django.contrib.messages",
250
+ "django.contrib.staticfiles",
251
+]
252
+CREATED_APP = [] # custom apps goes here
253
+THIRD_PARTY_APP = [] # third party apps goes here
254
+INSTALLED_APPS = [*DEFAULT_APP, *CREATED_APP, *THIRD_PARTY_APP]
255
+MIDDLEWARE = [
256
+ "django.middleware.security.SecurityMiddleware",
257
+ "django.contrib.sessions.middleware.SessionMiddleware",
258
+ "django.middleware.common.CommonMiddleware",
259
+ "django.middleware.csrf.CsrfViewMiddleware",
260
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
261
+ "django.contrib.messages.middleware.MessageMiddleware",
262
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
263
+]
264
+ROOT_URLCONF = "config.urls"
265
+TEMPLATES = [
266
+ {
267
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
268
+ "DIRS": [],
269
+ "APP_DIRS": True,
270
+ "OPTIONS": {
271
+ "context_processors": [
272
+ "django.template.context_processors.debug",
273
+ "django.template.context_processors.request",
274
+ "django.contrib.auth.context_processors.auth",
275
+ "django.contrib.messages.context_processors.messages",
276
+ ],
277
+ },
278
+ },
279
+]
280
+WSGI_APPLICATION = "config.wsgi.application"
281
+AUTH_PASSWORD_VALIDATORS = [
282
+ {
283
+ "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
284
+ },
285
+ {
286
+ "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
287
+ },
288
+ {
289
+ "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
290
+ },
291
+ {
292
+ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
293
+ },
294
+]
295
+LANGUAGE_CODE = "en-us"
296
+TIME_ZONE = "UTC"
297
+USE_I18N = True
298
+USE_TZ = True
299
+STATIC_URL = "static/"
300
+DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
301
+```
302
+
303
+
304
+**dev.py**
305
+
306
+```
307
+import environ
308
+from .base import *
309
+env = environ.Env(DEBUG=(bool, True))
310
+environ.Env.read_env(str(BASE_DIR / ".env"))
311
+SECRET_KEY = env.str("SECRET_KEY")
312
+DEBUG = env.bool("DEBUG")
313
+ALLOWED_HOSTS = ["*"]
314
+THIRD_PARTY_APP = [
315
+ "django_extensions",
316
+] # third party apps goes here
317
+INSTALLED_APPS = INSTALLED_APPS + THIRD_PARTY_APP
318
+# If you want to use postgresql instead, then uncomment this and comment that of sqlite3 bellow.
319
+# DATABASES = {
320
+# "default": {
321
+# "ENGINE": "django.db.backends.postgresql_psycopg2",
322
+# "NAME": env.str("DB_NAME"),
323
+# "USER": env.str("DB_USER"),
324
+# "PASSWORD": env.str("DB_PWD"),
325
+# "HOST": env.str("DB_HOST"),
326
+# "PORT": env.str("DB_PORT"),
327
+# }
328
+# }
329
+DATABASES = {
330
+ "default": {
331
+ "ENGINE": "django.db.backends.sqlite3",
332
+ "NAME": BASE_DIR / "db.sqlite3",
333
+ }
334
+}
335
+```
336
+
337
+
338
+Django Applications
339
+-------------------
340
+
341
+Many developers create their apps in the working directory by running the command
342
+
343
+```
344
+python3 manage.py startapp <app-name>
345
+```
346
+
347
+
348
+**NB**: This turns to crowd the working directory.
349
+
350
+Best Practice
351
+-------------
352
+
353
+Here we will create our Django applications in the `apps/`folder. This way, all applications are grouped and easy to find. Also, it makes the working directory less crowded.
354
+
355
+To create a new app, we do the following;
356
+
357
+* Create the `apps/`directory
358
+* cd to the `apps/`directory and run the `startapp` management command
359
+
360
+```
361
+python3 ../manage.py startapp <app-name>
362
+```
363
+
364
+
365
+**NB**: After `startapp` , what follows should be the name of the application. It doesn’t support paths. That’s why we have to cd to the `apps/` before running the command, while referring back to the `manage.py` with `../`
366
+
367
+Core App
368
+--------
369
+
370
+The core application will serve the purpose of containing code that is common to two or more applications.
371
+
372
+For example, it is common for models to share the same fields; `created_at` , `modified_at` . Rather than duplicating these in all the models, we can have them in a separate application and import them where needed.
373
+
374
+**apps/core/abstracts/models.py**
375
+
376
+```
377
+# apps/core/abstracts/models.py
378
+from django.db import models
379
+class CreatedModifiedAbstract(models.Model):
380
+ created_at = models.DateTimeField(auto_now_add=True)
381
+ modified_at = models.DateTimeField(auto_now=True)
382
+ class Meta:
383
+ abstract = True
384
+```
385
+
386
+
387
+**NB**: The code above defines an abstract model that should be inherited into a model.
388
+
389
+```
390
+.
391
+├── Django_First
392
+│ ├── apps
393
+│ │ ├── __init__.py
394
+│ │ └── core
395
+│ │ ├── __init__.py
396
+│ │ ├── abstracts
397
+│ │ │ ├── __init__.py
398
+│ │ │ └── models.py
399
+│ │ └── apps.py
400
+│ ├── config
401
+│ │ ├── __init__.py
402
+│ │ ├── asgi.py
403
+│ │ ├── settings
404
+│ │ │ ├── base.py
405
+│ │ │ └── dev.py
406
+│ │ ├── urls.py
407
+│ │ └── wsgi.py
408
+│ ├── manage.py
409
+│ └── requirements
410
+│ ├── base.txt
411
+│ └── dev.txt
412
+```
413
+
414
+
415
+Command shortcuts with Makefile
416
+-------------------------------
417
+
418
+Now that we have a different structure and, we are able to run our Django project in different environments, we will have lengthy commands because we have to specify the environment we are running in.
419
+
420
+To create shortcuts for our various commands, we can use a `Makefile`. Here are common commands we can create shortcuts for when running in the development environment;
421
+
422
+**Makefile**
423
+
424
+```
425
+dev-start:
426
+ python3 manage.py runserver --settings=config.settings.dev
427
+dev-startapp:
428
+ cd apps && python3 ../manage.py startapp $(app) --settings=config.settings.dev
429
+dev-migrate:
430
+ python3 manage.py migrate --settings=config.settings.dev
431
+dev-makemigrations:
432
+ python3 manage.py makemigrations --settings=config.settings.dev
433
+dev-dbshell:
434
+ python3 manage.py dbshell --settings=config.settings.dev
435
+dev-shell:
436
+ python3 manage.py shell --settings=config.settings.dev
437
+dev-shell-plus:
438
+ python3 manage.py shell_plus --settings=config.settings.dev
439
+dev-install:
440
+ pip install -r requirements/dev.txt
441
+dev-test:
442
+ python3 manage.py test --settings=config.settings.dev
443
+```
444
+
445
+
446
+To run a command from the Makefile, we just run `make <command-name>` where `<command-name>` is a shortcut name in the Makefile. For example, to start our Django server, we will run
447
+
448
+```
449
+make dev-start
450
+```
451
+
452
+
453
+Conclusion
454
+----------
455
+
456
+The Django framework is a powerful tool for creating complex web applications. However, many developers struggle with their project’s structure, which slows down productivity.
457
+
458
+This article presented a boilerplate that can be used to create a Django project. It is structured using best practices and makes it possible to scale any Django project.
... ...
\ No newline at end of file