Skip to content

Instantly share code, notes, and snippets.

@AaradhyaSaxena
Last active March 7, 2024 22:43
Show Gist options
  • Save AaradhyaSaxena/78f5ca37b992fdb9e7ffd905be54daf4 to your computer and use it in GitHub Desktop.
Save AaradhyaSaxena/78f5ca37b992fdb9e7ffd905be54daf4 to your computer and use it in GitHub Desktop.
django

Django Project

Some initial setup ,auto-generate some code that establishes a Django project.

  • A collection of settings for an instance of Django, including database configuration, Django-specific options and application-specific settings.
  • A project may contain multiple apps.
django-admin startproject mysite

This will create a mysite directory in our current directory.

These files are:

  • manage.py: A command-line utility that lets you interact with this Django project in various ways. You can read all the details about manage.py in django-admin and manage.py.
  • The outer mysite/ root directory is a container for your project.
    • The inner mysite/ directory is the actual Python package for your project. Its name is the Python package name you’ll need to use to import anything inside it (e.g. mysite.urls).
    • mysite/settings.py: Settings/configuration for this Django project. Django settings will tell you all about how settings work.
    • mysite/urls.py: The URL declarations for this Django project; a “table of contents” of your Django-powered site.

The development server

python manage.py runserver 8080

By default, the runserver command starts the development server on the internal IP at port 8000. To change the server’s port, pass it as a command-line argument. For instance, this command starts the server on port 8080:

To change the server’s IP, pass it along with the port. For example, to listen on all available public IPs (which is useful if you are running Vagrant or want to show off your work on other computers on the network), use:

python manage.py runserver 0.0.0.0:8000

Django App

Now that our environment – a “project” – is set up, we're set to start doing work.

Each application you write in Django consists of a Python package that follows a certain convention. Django comes with a utility that automatically generates the basic directory structure of an app.

Projects vs. apps

What’s the difference between a project and an app? An app is a web application that does something – e.g., a blog system, a database of public records or a small poll app. A project is a collection of configuration and apps for a particular website. A project can contain multiple apps. An app can be in multiple projects.

The apps can live anywhere on your Python path. We’ll create our poll app in the same directory as your manage.py file so that it can be imported as its own top-level module, rather than a submodule of mysite.

To create your app, make sure you’re in the same directory as manage.py and type this command:

python manage.py startapp polls

That’ll create a directory polls, which is laid out like this:

polls/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py

Django App Example

Write your first view

Open the file polls/views.py and put the following Python code in it:

polls/views.py¶

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

To call the view, we need to map it to a URL - and for this we need a URLconf. To create a URLconf in the polls directory, create a file called urls.py.

In the polls/urls.py file include the following code:

polls/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

The next step is to point the root URLconf at the polls.urls module.

mysite/urls.py

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]

The include() function allows referencing other URLconfs. Whenever Django encounters include(), it chops off whatever part of the URL matched up to that point and sends the remaining string to the included URLconf for further processing.

You have now wired an index view into the URLconf.

Verify: python manage.py runserver

Go to in your browser, and you should see the text “Hello, world. You’re at the polls index.”, which you defined in the index view.

The path() function is passed four arguments, two required: route and view, and two optional: kwargs, and name.

Database Setup

server/settings.py By default, the configuration uses SQLite. SQLite is included in Python, so no need to install anything else to support your database.

To use another database, we need to install the appropriate database bindings and change the "ENGINE" and "NAME" keys in the DATABASES 'default' item to match your database connection settings:

ENGINE – 'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', etc.
NAME – The name of your database. The default value, BASE_DIR / 'db.sqlite3', will store the file in your project directory.

Note, the INSTALLED_APPS setting at the top of the file. That holds the names of all Django applications that are activated in this Django-instance.

django.contrib.admin – The admin site. You’ll use it shortly.
django.contrib.auth – An authentication system.
django.contrib.contenttypes – A framework for content types.
django.contrib.sessions – A session framework.
django.contrib.messages – A messaging framework.
django.contrib.staticfiles – A framework for managing static files.

Some of these applications make use of at least one database table, though, so we need to create the tables in the database before we can use them. To do that, run the following command:

$ python manage.py migrate

The migrate command looks at the INSTALLED_APPS setting and creates any necessary database tables according to the database settings in your mysite/settings.py file and the database migrations shipped with the app.

Creating models

Models – essentially, your database layout, with additional metadata.

Philosophy

A model is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re storing. Django follows the DRY Principle. The goal is to define your data model in one place and automatically derive things from it.

This includes the migrations - unlike in Ruby On Rails, for example, migrations are entirely derived from your models file, and are essentially a history that Django can roll through to update your database schema to match your current models.

In our poll app, we’ll create two models: Question and Choice. A Question has a question and a publication date. A Choice has two fields: the text of the choice and a vote tally. Each Choice is associated with a Question.

These concepts are represented by Python classes. personalization/models.py

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
  • Each model is represented by a class that subclasses django.db.models.Model. Each model has a number of class variables, each of which represents a database field in the model.
  • Each field is represented by an instance of a Field class – e.g., CharField for character fields and DateTimeField for datetimes. This tells Django what type of data each field holds.
  • Some Field classes have required arguments. CharField, for example, requires that you give it a max_length. That’s used not only in the database schema, but in validation.
  • A Field can also have various optional arguments; in this case, we’ve set the default value of votes to 0.

Finally, note a relationship is defined, using ForeignKey. That tells Django each Choice is related to a single Question. Django supports all the common database relationships: many-to-one, many-to-many, and one-to-one.

Activating models

That small bit of model code gives Django a lot of information. With it, Django is able to:

  • Create a database schema (CREATE TABLE statements) for this app.
  • Create a Python database-access API for accessing Question and Choice objects. But first we need to tell our project that the polls app is installed.

Philosophy

Django apps are “pluggable”: You can use an app in multiple projects, and you can distribute apps, because they don’t have to be tied to a given Django installation.

server/settings.py

INSTALLED_APPS = [
    'personalization.apps.PersonalizationConfig',
    'django.contrib.admin',
    ...
]

Now Django knows to include the polls app. Let’s run another command:

$ python manage.py makemigrations polls

By running makemigrations, you’re telling Django that you’ve made some changes to your models (in this case, you’ve made new ones) and that you’d like the changes to be stored as a migration.

Migrations are how Django stores changes to your models (and thus your database schema) - they’re files on disk. You can read the migration for your new model if you like; it’s the file polls/migrations/0001_initial.py. Don’t worry, you’re not expected to read them every time Django makes one, but they’re designed to be human-editable in case you want to manually tweak how Django changes things.

The migrate command takes all the migrations that haven’t been applied (Django tracks which ones are applied using a special table in your database called django_migrations) and runs them against your database - essentially, synchronizing the changes you made to your models with the schema in the database.

Migrations are very powerful and let you change your models over time, as you develop your project, without the need to delete your database or tables and make new ones - it specializes in upgrading your database live, without losing data.

  • Change your models (in models.py).
  • Run pythonmanage.pymakemigrations to create migrations for those changes
  • Run pythonmanage.pymigrate to apply those changes to the database.

The reason that there are separate commands to make and apply migrations is because you’ll commit migrations to your version control system and ship them with your app; they not only make your development easier, they’re also usable by other developers and in production.

Django Admin

Django entirely automates creation of admin interfaces for models, to add, change, and delete content.

Django was written in a newsroom environment, with a very clear separation between content publishers and the public site. Site managers use the system to add news stories, events, sports scores, etc., and that content is displayed on the public site. Django solves the problem of creating a unified interface for site administrators to edit content.

Creating an admin user

First we’ll need to create a user who can login to the admin site. Run the following command:

$ python manage.py createsuperuser

Start the development server

The Django admin site is activated by default. Let’s start the development server and explore it.

$ python manage.py runserver

Now, go to “/admin/”, http://127.0.0.1:8000/admin/, we should see the admin’s login screen.

Django admin index page: You should see a few types of editable content: groups and users. They are provided by django.contrib.auth, the authentication framework shipped by Django.

But where’s our poll app? It’s not displayed on the admin index page.

Only one more thing to do: we need to tell the admin that Question objects have an admin interface. To do this, open the personalization/admin.py file, and edit it to look like this:

personalization/admin.py

from django.contrib import admin
from .models import Question

admin.site.register(Question)

URL Mappings

(https://www.educative.io/courses/django-python-web-development/gkokGpXV5PZ)

A direct mapping is available that imported the views.py file into the urls.py file of our projects. So, we were connecting views of our application to the urls.py file of the project itself. However, this solution is not modular.

Consider that we have a large scale project that might include various applications. Moreover, the project will also likely have several URL paths, where most paths will be application-specific. So, for a modular solution, we would prefer that the paths specific to an application be declared inside the application folder. This is where the include function will come in and help us out.

The include() function

The include() function allows us to look for a match with the path defined in the urls.py file of our project, and link it back to our application’s own urls.py file. So basically, whenever Django encounters include(), it takes whatever part of the URL matched up to that point and sends the remaining string to the urls.py file of the specified application.

For example, we have an application in our project called profiles. Then, we might have URL paths like:

/profile/admin/<int:id>/
/profile/user/<int:id>/
/home/

Here, the first two are associated with the profiles application, so we want to send them to the urls.py defined inside profiles application folder. Notice that these URLs have the profile at the beginning, which lets us know that they belong to the profiles application. So, we will match the profile part in the project’s url.py and use include() to send the rest of it to the application’s url.py. Consider that before, the path was declared as follows in the project’s url.py:

path('/profile/admin/<int:id>/', profiles.views.admin, name="admin")

Now, it will become:

path('/profile/', include('profiles.urls')

Notice that the include() function takes in the module name as a parameter, where we are sending the rest of the URL to match.

Django for API

Library API

The Library website currently consists of a single page that displays all books in the database. To transform it into a web API we will install Django REST Framework and create a new URL that acts an API endpoint outputting all available books. A web API does not output a traditional webpage with HTML, CSS, and JavaScript. Instead, it is just pure data (often in the JSON format) and accompanying HTTP verbs that specify what user actions are allowed.

We have to formally notify Django of the new installation in our django_project/settings.py file. Scroll down to the INSTALLED_APPS section and add rest_framework.

Ultimately, our web API will expose a single endpoint that lists out all books in JSON. To do this, we will need a new URL route, a new view, and a new serializer file (more on this shortly). There are multiple ways to organize these files. Many professional Django developers will just include API logic in the related app while putting URLs under an /api/ prefix. For now though, to keep the API logic clear from the traditional Django logic, we will create a dedicated apis app for our project.

The apis app will not have its own database models so there is no need to create a migration file and run migrate to update the database. In fact, the database models are the one area we don’t need to touch at all since this new web API is designed to expose existing data not create new data.

URL's: Then create a new file called apis/urls.py with your text editor. This file will import a future view called BookAPIView and set it to the URL route of "" so it will appear at api/. As always, we’ll add a name to it as well, book_list, which helps in the future when we want to refer to this specific route.

VIEW's: In traditional Django views are used to customize what data to send to the templates. Django REST Framework views are similar except the end result is serialized data in JSON format, not the content for a web page! Django REST Framework views rely on a model, a URL, and a new file called a serializer.

To avoid confusion, some developers will call an API views file apiviews.py or api.py.

Then we create a view class called BookAPIView that uses ListAPIView to create a read-only endpoint for all book instances.

The only two steps required in our view are to specify the queryset, which is all available books, and then the serializer_class which will be BookSerializer.

Serializers

We’re on the final step now! So far we have created a urls.py file and a views.py file for our API. The last–but most important–action is to create our serializer.

A serializer33 translates complex data like querysets and model instances into a format that is easy to consume over the internet, typically JSON. It is also possible to “deserialize” data, literally the same process in reverse, whereby JSON data is first validated and then transformed into a dictionary. The real beauty of Django REST Framework lies in its serializers which abstracts away most of the complexity for us.

By creating a new URL route, a new view, and a serializer class we have created an API endpoint for our Library website that will display all existing books in list format.

python manage.py runserver

Our API endpoint is: http://127.0.0.1:8000/api/

Todo API

We will build and deploy Todo API back-end that contains both a list API endpoint for all todos and dedicated endpoints for each individual todo.

CORS

Cross-Origin Resource Sharing (CORS)59 refers to the fact that whenever a client interacts with an API hosted on a different domain (mysite.com vs yoursite.com) or port (localhost:3000 vs localhost:8000) there are potential security issues.

Specifically, CORS requires the web server to include specific HTTP headers that allow for the client to determine if and when cross-domain requests should be allowed. Because we are using a SPA architecture the front-end will be on a different local port during development and a completely different domain once deployed!

The easiest way to handle this issue–-and the one recommended by Django REST Framework is to use middleware that will automatically include the appropriate HTTP headers based on our settings. The third-party package django-cors-headers is the default choice within the Django community and can easily added to our existing project.

python -m pip install django-cors-headers~=3.10.0
  • add corsheaders to the INSTALLED_APPS
  • add CorsMiddleware above CommonMiddleWare in MIDDLEWARE
  • create a CORS_ALLOWED_ORIGINS config at the bottom of the file

It’s very important that corsheaders.middleware.CorsMiddleware appears in the proper location since Django middlewares are loaded top-to-bottom.

CSRF

Just as CORS is an issue when dealing with a SPA architecture, so too are forms. Django comes with robust CSRF protection62 that should be added to forms in any Django template, but with a dedicated React front-end setup this protection isn’t inherently available. Fortunately, we can allow specific cross-domain requests from our frontend by setting CSRF_TRUSTED_ORIGINS. At the bottom of the settings.py file, next to CORS_ORIGIN_WHITELIST, add this additional line for React’s default local port of 3000.

django_project/settings.py

CSRF_TRUSTED_ORIGINS = ["localhost:3000"]

Django MVC

Controller: A controller is the heart of the system, it steers everything. For a web framework, this means handling requests and responses, setting up database connections and loading add-ons. For this, Django reads a settings file so that it knows what to load and set up. And Django reads a URL config file that tells it what to do with the incoming requests from browsers.

Model: The model layer in Django means the database plus the Python code that directly uses it. It models reality. You capture whatever your website needs in database tables. Django helps you write Python classes (called models) that tie 1:1 to the database tables.

View: The view layer is the user interface. Django splits this up in the actual HTML pages and the Python code (called views) that renders them. And it also has an automatic web admin interface for editing the models.

Redis

Redis is an in-memory data structure store that can be used as a caching engine. Since it keeps data in RAM, Redis can deliver it very quickly. Redis is not the only product that we can use for caching. Memcached is another popular in-memory caching system, but many people agree that Redis is superior to Memcached in most circumstances. Personally, i like how easy it is to set up and use Redis for other purposes such as Redis Queue.

Django Database Migrations

Migration is a way of applying changes that we have made to a model, into the database schema. Django creates a migration file inside the migration folder for each model to create the table schema, and each table is mapped to the model of which migration is created.

Django provides the various commands that are used to perform migration related tasks. After creating a model, we can use these commands.

  • makemigrations : It is used to create a migration file that contains code for the tabled schema of a model.
  • migrate : It creates table according to the schema defined in the migration file.
  • sqlmigrate : It is used to show a raw SQL query of the applied migration.
  • showmigrations : It lists out all the migrations and their status.

It allows us to set up a schema, names of the tables, fields, data-types, how one table might relate to another table via code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment