Skip to content

Instantly share code, notes, and snippets.

@EVanGorkom
Last active March 28, 2024 21:24
Show Gist options
  • Save EVanGorkom/c615e685a9bae34f6535324fc26b54ac to your computer and use it in GitHub Desktop.
Save EVanGorkom/c615e685a9bae34f6535324fc26b54ac to your computer and use it in GitHub Desktop.
Django Starter Notes

Django Application Cheatsheet

Getting Started

In your command line, you'll need to make sure that pip and python are installed.

If this is not your first time making a Django application, see the Quickstart option instead.

Next is setting up a virtual environment. It is a recommended best practice when working on Python projects, including Django. A virtual environment helps isolate project dependencies and avoids conflicts with other projects.

1. Create your project root directory:

If you haven't already created a new project directory, now is the time. Once created, we'll want to cd into it:

mkdir name_of_project

cd name_of_project
2. Install virtualenv:

If you don't have virtualenv installed, you can install it using pip:

pip3 install virtualenv
3. Create a Virtual Environment:

Create a virtual environment. Replace the second "venv" with your preferred name for the virtual environment

python3 -m venv venv
                ^^^^
This venv can be any version of 'venv' you like. I usually use 'env' and will refer to it as 'env' for the rest of the walk through
4. Activate the Virtual Environment:

On macOS and Linux:

source env/bin/activate

After activation, your terminal prompt should change, indicating that the virtual environment is active.

You'll want to do this every time you come back to your project!!

5. Install Django:

While the virtual environment is active, install Django using pip:

pip3 install django

If you're working on an API, then you'll also want to run the following:

pip3 install djangorestframework
6. Continue with Project Setup:

Now that your virtual environment is set up and Django is installed, you can proceed with the steps mentioned earlier to create a new Django project, create a Django app, configure database settings, apply migrations, and run the development server.

7. Deactivate the Virtual Environment:

When you're done working on your project, you can deactivate the virtual environment:

deactivate
Additional Note:

Remember to reactivate the virtual environment every time you work on your project. You can reactivate it using the activation command mentioned in step 3.

By using a virtual environment, you create an isolated environment for your project, making it easier to manage dependencies and ensuring that your project runs with the correct versions of libraries and packages.

Quickstart

  1. Create a project directory mkdir my_django_project

  2. Move into the project directory cd my_django_project

  3. Set up a virtual environment (optional but recommended) python3 -m venv env

  4. Activate the virtual environment source env/bin/activate

  5. Install Django pip install django

  6. Create a Django project django-admin startproject name_of_project

  7. Navigate into the project directory cd name_of_project

  8. Run migrations python manage.py migrate

Packages (similar to Ruby gems)

This list of packages and their uses. It's a good idea to install them ahead of time, but you can install them as needed. For each of these include pip install before the package name:

  • django -- The basic framework for building in django
  • djangorestframework -- Used to create API or strictly back end applications
  • psycopg2 -- Used to switch your database to PostgreSQL (more steps still required)
  • psycopg2-binary -- Used to switch your databases over from SQLite to PostgreSQL
  • whitenoise -- Simplifies serving static files (css, js, images, fonts, icons, etc.) for Django web applications
  • pytest-django -- Used for Testing
  • gunicorn -- Needed for deployment purposes
  • django-heroku -- Needed for deploying on Heroku
  • pillow -- Needed for image uploading features

Tip:

You can chain these packages together into one long command if you know what you want to install ahead of time. Ex: pip install djangorestframework psycopg2 whitenoise pytest-django gunicorn pillow


Making Models:

Now in order to make a new model you will need to create your models.py file and create some tables.

1. Include your connection from django.db import models line in your code or Django won't know to make these model classes!!!

include better steps here in the future...

Screenshot 2023-11-30 at 1 30 46 PM

Before we're able to make these models as migrations, we need to take and extra step. Go to your settings.py file and scroll down to 'INSTALLED_APPS' section. You need to add the name of your project here so that you can call it in your next step and it knows what to make these migrations for.

Screenshot 2023-11-30 at 4 34 16 AM

NOW you need to run the following code in order to create your new migration to your DB: $ python manage.py makemigrations name_of_your_table

This will create your migration, but [remember], migrations need to be expressly migrated. Run the migrate command again from the beginning of the project: $ python manage.py migrate

Setting up the Admin (optional)

While the admin side of a Django app is optional, it can make 'seeding' your database incredibly streamlined. In order to get your admin set up and see your newly created tables on the admin site, follow the directions below:

Django comes preloaded with a handy feature known as a SuperUser. Which is effectively a Super admin of the application that you can interact with like a website. You can set this up by running the following:

$ python manage.py createsuperuser

It will give a response of the following and you can fill in the blanks

Username (leave blank to use 'ethan.vangorkom'): admin
Email address: email@email.com
Password: (this field will be blank as you type)
Password (again):

If you password is basic and easy, it will give you an error. Type 'y' and hit enter to overwrite the warning.

Now that the admin is set up, let's add the new tables to the admin's scope! To do that we'll need to create a new file called admin.py in our project.

  • Within the file we'll start our first import from the admin roles and rules that come with Django.
    • from django.contrib import admin
  • Then we'll need to reference our models, so we'll make the call to our models file and import each model class.
    • from .models import Name_Of_Model
  • Finally we just have to register our new model to be within our admin's scope.
    • admin.site.register(Name_Of_Model)

Screenshot 2023-11-30 at 4 53 20 AM

Additionally, Django is nice in that it will let you import all of your models at once. You can also write the code above like this:

from django.contrib import admin
from .models import Customer, Subscription, Tea

Now that our admin/superuser has access to the tables, we can run our server. In the command line run: $ python manage.py runserver If you have no issues in your code, it will load similar to rails server, and give your a link. http://127.0.0.1:8000/ Paste this into your webbrowser and then add admin/ at the end and log in with the credentials that you entered perviously in the 'createsuperuser' step.

You should come to a home page that looks like this:

Screenshot 2023-11-30 at 1 10 37 PM

From here you can see the various tables that you created and registered previously. Django comes preloaded with an Admin function that allows you to do basic CRUD functionality without needing to write those methods in your views yet (more on that later).

Screenshot 2023-11-30 at 1 15 07 PM

NOTE: In a Django application, when you create multiple models, and link them together with ForeignKey or ManytoManyField You must put the parent model, above the model you call the field attribute in, otherwise it will not be able to find it.


Urls

In the Django framework, urls.py act like the routes files we're used to using in a rails application. Here we'll need to link them to our views.py in order to create our endpoints.

First, well need to declare our connections and scope.

# These first two come preloaded in a Django application
from django.contrib import admin
from django.urls import path

# These are the ones you'll need to add.
from your_project_name import views
from rest_framework.urlpatterns import format_suffix_patterns

This lets your application know that we're going to talk to our views.py and look at some of the methods we have there.

urlpatterns = [
path('teas/', views.tea_list),
path('teas/<int:id>/', views.tea_details),
path('admin/', admin.site.urls),
]

Notice that in our paths we're calling on our file 'views' and then will look at the methods that belong to that view tea_list and tea_details in this case. That means it's going to look for those methods and run them when we enter the corresponding url endpoints. teas/ will hit the tea_list method and so on. Keep this in mind when we move onto creating our views file next.


Views:

The Functionality of Controllers in Rails

In the Django framework, views.py house the CRUD methods (called 'functions' in python) for your class models.

1. Create the views.py file if you haven't already.

2. Import the data we want to manipulate.

In Django, we need to declare our connections. To achieve this we use the following syntax:

from django.http import JsonResponse
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .serializers import *
from .models import *

- Not all of these imports will make sense immediately, but I promise we'll go over it.

3. Start making your views

First it's important to know that our urls.py file will be searching our views for a specific method to run. ALL of the CRUD functionality of all models exists within the same file here (until refactored out anyways), so naming convention is important here.

For simplicity, I've broken up the CRUD functions of each model into two main methods. List and Details.

  • List - will refer to the GET (index) endpoint and the POST endpoint as neither require a specific or valid id value to be utilized.
  • Details - will be used to GET (show), PUT, and DELETE since these all require a specific and valid id value.

Now then, in these examples I follow along to the video tutorial and utilize a conditional to determine which request is being sent and respond accordingly for both the list and details methods

# Let's do the list functionality first
@api_view(['GET', 'POST'])
def tea_list(request):
	if request.method == 'GET':
		teas = Tea.objects.all()
		serializer = TeaSerializer(teas, many=True)
		return Response(serializer.data)
	elif request.method == 'POST':
		serializer = TeaSerializer(data=request.data)
		if serializer.is_valid():
			serializer.save()
			return Response(serializer.data, status=status.HTTP_201_CREATED)

While this may look like a lot at first, if you've been coding in Ruby/Rails for a while, this is actually fairly straightforward. The request.method type is being passed through a conditional to decide how we should handle it. If it's a GET then we'll need to find all of our objects that we're looking for, serializer them and then explicitly return the response. If the request is POST, then we'll need to get the request.data and turn it into an object and serialize it for the response. First we check to make sure that the serializer is valid (which can weed out the bad requests, or invalid inputs, etc.), then we save it!

Now let's take a look at our tea_details function.

@api_view(['GET', 'PUT', 'DELETE'])
def tea_detail(request, id):
	try:
		tea = Tea.objects.get(pk=id)
	except tea.DoesNotExist:
		return Response(status=status.HTTP_404_NOT_FOUND)
		
	if request.method == 'GET':
		serializer = TeaSerializer(tea)
		return Response(serializer.data)
	elif request.method == 'PUT':
		serializer = TeaSerializer(tea, data=request.data)
		if serializer.is_valid():
			serializer.save()
			return Response(serializer.data)
		return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
	elif request.method == 'DELETE':
		tea.delete()
		return Response(status=status.HTTP_204_NO_CONTENT)

Again, this is using a conditional to determine which action we should take. However, here we have an added parameter in the tea_detail function of id. In Django we pass this value in along with our request type parameter to help us locate the specific object we're looking for. In Python, we can use the exception (see python tutorial if you're not sure what that is) try to determine if the object exists or not. If it does, then we continue down our conditional!

4. Your Errors

If you're seeing TeaSerializer have a bunch of squiggles underneath every instance of it, then congratulations! You're ready to move on to making our serializers.py file! If you're not seeing that, then you might want to install the Python extension for VS Code or the Pylance extension (which should come as part of the Python extension).


Serializing


Testing

PDB

pdb is the Python equivelant to a pry session. It will stop your code and let you access variables and values from within the code. You must import pdb before you attempt to use it in a file though.

At the top of your file - import pdb

Whereever you want to stop your code - pdb.set_trace()

TestCases

Django has a TestCase builtin so you don't have to use Pytest if you don't want to. To utilize the Testcase, you'll need to import the funtionality and build out your tests.

from django.test import TestCase

If you're building an API, the rest_framework has it's own TestCase you can utilize as well. Though I would recommend utilizing one or the other.

from rest_framework.test import APITestCase

You can run your entire test suite for the entire application or you can make it more specific.

python3 manage.py test

You can get as specific as calling a specific method!

python3 manage.py test app_name.tests.test_file.TestCase.test_method

Database Manipulation

Basic commands in Rails and their Django equivalents

rails db:drop --> python manage.py flush
# This will ask if you're sure you want to delete everything
# Note: This will also delete your superuser credentials!

rails db:create --> python manage.py migrate
# This command not only creates the database but also applies any pending migrations to update the database schema.

rails db:migrate --> python manage.py migrate
# As above

rails db:seed --> python manage.py loaddata your_fixture_file
# Replace `your_fixture_file` with your own fixture file (ex. `fixtures`). Fixtures in Django are typically JSON or YAML files containing serialized data

Deployment (on Heroku)

There's a lot that is required for deployment on Heroku with Django. While I'm sure there are many ways to do it, we found that this way works for us. Be sure to do your own research to get a bette understanding of the changes and concepts that are employed here.

Python Deployment with Heroku Docs: https://devcenter.heroku.com/articles/getting-started-with-python

Deploying an already created repository: https://devcenter.heroku.com/articles/preparing-a-codebase-for-heroku-deployment

Deployment Made Easy-ish

1. Make sure that you have all of your required packages installed!

# These packages will almost always be used!

django
djangorestframework
psycopg2
psycopg2-binary
whitenoise
gunicorn
django-heroku
django-cors-headers

2. Make sure that you are on your main branch in your terminal and that your Required docs are set up:

  • runtime.txt
python-3.11.6

Within the runtime file should just be your version of python that you used to write the project with.

  • Procfile
web: gunicorn your_project_name.wsgi

Within the Procfile should just be the text above, but substitute your actual project name.

  • requirements.txt Create your requirements file by running the command pip freeze > requirements.txt

This will auto-generate your requirements file with all of the packages you have installed in your application. (Necessary for Heroku to know what framework you're using, remember Django is a python package too)

3. Run Heroku commands from the command line.

Now we're ready to create and connect our application to Heroku.

  • Make sure that Heroku is installed on your local machine and that your are in your project's directory.
  • Run the command heroku login
    • Follow the prompts and login
  • Now that we're logged in, run the command heroku create
    • This will create a new heroku deployment, but it's not completely connected to your application yet. You still need to fix your database.
    • Update your settings.py file following the next steps

First we'll need to change your DATABASES values to the values below instead of the SQLite database that comes preloaded.

settings.py

if "DATABASE_URL" in os.environ:
	DATABASES = {
		'default': {
			'ENGINE': 'your_info_here',
			'USER': 'your_info_here',
			'NAME': 'your_info_here',
			'PASSWORD': 'your_info_here',
			'HOST': 'your_info_here',
			'PORT': "5432",
		}
	}
else:
	DATABASES = {
		'default': {
			'ENGINE': 'django.db.backends.sqlite3',
			'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
		}
	}

Update this information with the data in Heroku!! go to the Heroku Postgres, then view credentials!

You'll find the necessary information by clicking the following highlighted links:

  1. Heroku Postgres

  2. Settings

  3. View Credentials...

Note: The Database value within the credentials section will be your value for NAME within your settings.py file. You will likely not use your URI value that comes within the credentials section. Also your credentials are NOT permanent! You may have to come back and update these credentials within your app periodically.

4. Necessary updates to your settings.py file

Be sure to add this to your settings file too... Again there's a lot of subtle changes to your code base to get Django applications to deploy properly.

from pathlib import Path
import os

# ...

# Heroku settings
# Allow Heroku to set the DJANGO_SETTINGS_MODULE environment variable
SETTINGS_MODE = os.getenv("DJANGO_SETTINGS_MODULE", "your_project_name.settings.production")

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key")

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

# Allow all host headers
ALLOWED_HOSTS = ["*"]

# Databases
import dj_database_url

# Your databases from earlier here

# ... Scroll till the bottom

import django_heroku
STATIC_ROOT = BASE_DIR / "staticfiles"
STATIC_URL = 'static/'
django_heroku.settings(locals())

Finally, git add, git commit, git push, then git push heroku main.

Run the command python3 manage.py migrate

  • This is because we've connected our data to a new database (hosted online), and we need to migrate again to the deployed database.
  • Run git add, commit, push, and push heroku main again

At last everything should be connected and using a PostgreSQL database. Check your deployment to make sure everything is working properly!

... but we're not done yet...

5. CORS integration

A CORS (Cross-Origin Resource Sharing) error occurs when a web application running at one origin (domain) makes a request to a different origin, and the browser blocks the request due to security reasons. This is a common issue when your frontend (client-side) is running on a different domain or port than your backend (server-side). To avoid this, follow the next steps.

  • Install django-cors-headers if you haven't done that already. Remember to rerun pip freeze > requirements.txt to update your requirements file if you're installing it now.
  • Go back to your settings.py and update the following fields.
INSTALLED_APPS = [ 
	# ... 
	'corsheaders', 
	# ... 
] 

MIDDLEWARE = [ 
	# ... 
	'corsheaders.middleware.CorsMiddleware', 
	# ... 
]


# Add this new section to the very bottom
CORS_ALLOWED_ORIGINS = [
	"http://localhost:3000",
	"https://your_production's_frontend.com",
	"https://127.0.0.1:8000/"
	#... whatever other sites you want to allow
]

# Or format it like this

CORS_ALLOW_ALL_ORIGINS = True

Be sure to add, commit, push, and push heroku!

Useful Methods

SerializerMethodField():

  • SerializerMethodField() is a field provided by Django REST Framework that allows you to create a custom field in your serializer that does not directly map to a model field. It's useful when you need to include custom logic or data that is not directly available from your model.
  • class PlayerSerializer(serializers.ModelSerializer):
    campaigns = serializers.SerializerMethodField()
    
    class Meta:
      model = Player
      fields = ['id', 'campaigns', 'first_name', 'last_name', 'date_created', 'updated_at']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment