Django - herougo/SoftwareEngineerKnowledgeRepository GitHub Wiki
Sources:
- https://www.w3schools.com/django/index.php
- https://github.com/techwithtim/Music-Controller-Web-App-Tutorial/tree/main
- https://docs.djangoproject.com/en/5.1/
Notes:
- App: An app is a web application that has a specific meaning in your project, like a home page, a contact form, or a members database.
- Views: Django views are Python functions that takes http requests and returns http response, like HTML documents. Views are usually put in a file called views.py located on your app's folder.
Commands
django-admin startproject my_tennis_club # create project
python manage.py migrate # apply model migrations
py manage.py makemigrations members # create migrations based on python code changes
python manage.py runserver # run the project
python manage.py startapp members # create members app
python manage.py test
File Structure (React App)
my_tennis_club
manage.py
members/
migrations/
__init__.py
__init__.py
admin.py
apps.py
models.py
tests.py
urls.py # created by user
views.py
frontend
src/ # created by user, contains javascript
templates/ # created by user, contains index.html
static # created by user, contains css
...
my_tennis_club/
__init__.py
asgi.py
settings.py
urls.py
wsgi.py
File Explanations
manage.py: A command-line utility that lets you interact with this Django project in various ways.
my_tennis_club/
- urls.py: The URL declarations for this Django project; a “table of contents” of your Django-powered site
- mysite/asgi.py: An entry-point for ASGI-compatible web servers to serve your project. See How to deploy with ASGI for more details.
- mysite/wsgi.py: An entry-point for WSGI-compatible web servers to serve your project. See How to deploy with WSGI for more details.
- settings.py: settings file (BIG)
members/
- apps.py: contains the config for the application; name corresponds to the Full Python path to the application (e.g. 'music_project.frontend')
- admin.py: register models so you can use admin dashboard to modify the data fields
Add an App and View
my_tennis_club/members/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('members/', views.members, name='members'),
]
my_tennis_club/my_tennis_club/urls.py:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('', include('members.urls')),
path('admin/', admin.site.urls),
]
my_tennis_club/members/templates/myfirst.html:
...
my_tennis_club/members/views.py:
from django.http import HttpResponse
from django.template import loader
def members(request):
template = loader.get_template('myfirst.html')
return HttpResponse(template.render())
my_tennis_club/my_tennis_club/settings.py:
INSTALLED_APPS = [
...
'members'
]
Add API Get Endpoint
my_tennis_club/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls'))
]
api/urls.py
from django.urls import path
from .views import UserInRoom
urlpatterns = [
path('user-in-room', UserInRoom.as_view())
]
api/views.py
from rest_framework.views import APIView
from rest_framework import status
from django.http import JsonResponse
class UserInRoom(APIView):
def get(self, request, format=None):
if not self.request.session.exists(self.request.session.session_key):
self.request.session.create()
data = {
'code': self.request.session.get('room_code')
}
return JsonResponse(data, status=status.HTTP_200_OK)
Add Tests
Use django.test.TestCase, a subclass of unittest.TestCase. Tests go in the tests folder in the appropriate application folder (i.e. my_tennis_club/members/tests).
from django.test import TestCase
class AnimalTestCase(TestCase):
def setUp(self):
...
def test_animals_can_speak(self):
...
Article: Django Best Practices from DoorDash
source: https://medium.com/@DoorDash/tips-for-building-high-quality-django-apps-at-scale-a5a25917b2b5
- Don't be afraid of having one big app: If you don’t really understand the point of apps, ignore them and stick with a single app for your backend. You can still organize a growing codebase without using separate apps.
- If you do want to create separate applications, you will want to be very intentional about how you define them: Be very explicit about and minimize any dependencies between different apps. (If you are planning to migrate to microservices down the line, I can imagine that “apps” might be a useful construct to define precursors to a future microservice).
- Organize your apps inside a package: For example, twitter/app1, twitter/app2, etc. This helps with namespace conflicts.
- Explicitly name your DB tables
class Foo(Model):
class Meta:
db_table = 'foo'
- Explicitly create your many-to-many tables: That way, you can access them easily.
- Avoid GenericForeignKey: ???
- Keep migrations safe: For example, make sure you have a stable deploy which doesn't reference a column before deploying a migration which deletes the column.
- Squash your migrations: Many migrations can be time-consuming when running tests, etc.
- Make sure you handle conflicting migration files: i.e. 0001_a, 0002_b, 0003_c, 0003_d
- Avoid fat models: Avoid putting the bulk of your business logic inside model methods.
- Be careful with (or even avoid) signals: Putting too much logic in signals can make program flow difficult to trace and read. Passing custom arguments or information through a signal is not really possible.
- Avoid using the ORM as the main interface to your data: Suppose you want to create a ModelB every time ModelA is created. You can use signals or overload the model.save. A better solution for this is to establish a pattern in which you route all important database operations (create/update/delete) through some kind of simple interface that wraps the ORM layer. This gives you clean entry points to add additional logic before or after database events.
- Don't cache Django models: If you migrate your schema (add/change/delete fields from your model), Django actually does not handle this very gracefully when dealing with cached instances. If Django tries to read a model instance that was written to the cache from an earlier version of the schema, it will pretty much die. Under the hood, it’s deserializing a pickled object from the cache backend, but that object will be incompatible with the latest code. This is more of an unfortunate Django implementation detail than anything else.