Overview

Creating a Django Project

The following command:

$ django-admin startproject mysite

creates the following directory:

mysite/
    manage.py
    mysite/
        __init__.py
		views.py
		models.py
        settings.py
        urls.py
        asgi.py
        wsgi.py
		migrations
        	__init__.py
  • manage.py : A collection of command-line utility that lets the user interact with Django project (migrate, shell, collectstatic, etc.)
  • The inner mysite/ directory : actual Python package for the user's project
  • mysite/__init__.py : An empty file that tells Python that this directory should be considered a Python package
  • settings.py : Settings / configuration for the Django project
  • urls.py : The URL (path) declarations for the project
  • views.py : deals with the logic of taking a web request and returning a web response
  • migrations directory : stores data table structures created from makemigrations command at manage.py
  • models.py : defines the structure of stored data, including the field types, maximum size, default values, selection list options, etc.
  • wsgi.py : “Web Server Gateway Interface,” which is used to forward requests from a web server (such as Apache or NGINX) to a backend Python web application or framework
  • asgi.py : "Asynchronous Server Gateway Interface," ASGI is a spiritual successor to WSGI intended to provide a standard interface between async-capable Python web servers, frameworks, and applications. (WSGI only provides a standard for synchronous Python apps)

Check out Django Tutorials page to know more about each file.

Understanding the Mechanism

Publishing on web always requires two key pieces of information: contents and URL. With Django, the contents of the page are produced by a view function, and the URL is specified in a URLconf.

views.py

import json
from django.views import View
from django.http  import JsonResponse

class MainView(View):
    def get(self, request):
		    return JsonResponse({"Hello":"World"}, status=200)
  • JSON (JavaScript Object Notation) is a widely used datatype in internet communication.
  • json is imported to process json data.
  • MainView inherits View class from django.views.
  • JsonResponse is imported to respond to server request in Json.
  • def get() method is a built-in method in its parent class, View. This method only responds to GET method among HTTP request methods.

Each view function takes at least one parameter request by convention. This object contains information about the current Web request that has triggered this view, and it's an instance of the class django.http.HttpRequest.

We can also have a method to respond to POST method, assuming that models.py has already been created:

def post(self, request):
        data = json.loads(request.body)
        Users(
              name     = data['name'],
              email    = data['email'],
			  password = data['password']
        ).save()
        
        return JsonResponse({'message':'SUCCESS'}, status=200)

Class-based views

A view is a callable which takes a request and returns a response.
This can be more than a function, a class, for example.
Django provides base view classes which will suit a wide range of applications; all views inherit from the View class, which handles linking the view into the URLs, HTTP method dispatching and other common features.

RedirectView provides a HTTP redirect, and TemplateView extends the base class to make it also render a template.

from django.urls import path
from django.views.generic import TemplateView

urlpatterns = [
    path('about/', TemplateView.as_view(template_name="about.html")),
]

as_view()
Because TemplateView is a class and not a function, we should point the URL to the as_view() class method instead, which provides a function-like entry to class-based views.
Any arguments passed to as_view() will override attributes set on the class. In the above example, template_name is overriden.

Custom Class Views
In a Class View, each type of http method can only be redefined once.

class AccountView(View):
	def post(self, request):
    	data = json.loads(request.body) // json -> dictionary
        try:
        	Account.obects.create(
        		name = data['name'],
            	phone = data['phone'],
            	email = data['email'],
            	password = data['password']
        	) // OR .save() along with Account constructor
        	return JsonResponse({'message':'SUCCESS'}, status=200)
        except KeyError:
        	return JsonResponse({'message':'INVALID_KEY'}, status=400)

Try Except will prevent a 500 error from occurring if some field values are not provided when a new account object is created. Backend engineers should avoid a 500 error at all cost.

Error handling won't be necessary if we allow our database to store null values when some fields are not provided. To be more specific, we should enable "null True" and have None as a default value if there is no matching key.

name = data.get('name', None)

Moreover, just use HttpResponse if only a status code response is needed. Use JsonResponse if some json information has to be returned.

Let's add a get view to our AccountView.

def get(self, request):
    	account_data = Account.objects.values()
        return JsonResponse({'data':list(account_data)}, status=200)
        // JsonResponse does not understand query sets; query sets should be casted to lists in order to be returned as JsonResponse
        
		// JsonResponse also requires dictionary format. 
		// To return data that is not in dictionary format, use safe=False to force JsonResponse to understand it as a pseudo dictionary.
        account_data = list(Account.objects.values())
        return JsonResponse(account_data, safe=False, status=200) 
        
		// Pass field names as arguments on values() if you wish to retrieve values of only the mentioned fields
        account_data = list(Account.objects.values('name','email','phone'))
        return JsonResponse(account_data, safe=False, status=200)

Testing functionality with httpie

httpie is a program that enables sending HTTP requests.

http -v 'url_address' name='test_name' email='test_email' password='1234'

Then, how are these HTTP methods received? This is when the urls.py kicks in.

urls.py

from django.urls import path
from .views  import AccountView

urlpatterns = [
    path('', AccountView.as_view())
]
  • django.urls.path module is provided by Django to process URL paths
  • Django always stores and processes paths in a list like urlpatterns above
  • When the first argument is passed to path, as_view() method decides whether the HTTP request is GET, POST, UPDATE, or DELETE, and executes corresponding function.

django.urls functions (for use in URLconfs)

A URLconf is a mapping between URLs and the view functions that should be called for the corresponding URLS.

The user needs to tell Django explicitly that he or she is activating a view at a particular URL. Use URLconf to hook a view function to a particular URL.

Example URLconf:

from django.urls import include, path

urlpatterns = [
	path('...', include('...')),
	path('/<int:product_id>', AccountView.as_view())
    ...
]

The variable urlpatterns defines the mapping between URLs and the code that handles those URLs.

The include() function allows referencing other URLconfs. Whenever Django encounters include() function, "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." This function is useful in that in complex websites, urls for different apps are better managed separately in each app's urls.py.

path() function is passed four arguments:
1. route (required): a string that contains a URL pattern.
2. view (required): When Django finds a matching pattern, it calls the specified view function with an HttpRequest object as the first argument.
3. kwargs (optional): arbitrary keyword argument passed in a dictionary to the target view.
4. name (optional): provide a name to a URL so that it can be referred from elsewhere in Django, especially from within templates. Global changes to the URL patterns can be made easy with this feature.

Backslash in front of the endpoint name

By RESTful API convention, we keep backslash at the front of the current address when using it as a path() argument.
(By default, if the address argument has a trailing slash, django will redirect routes without a trailing slash to that route that has one. No one will get confused in this way)

models.py

Models are a link between the user's database and app. Each model class corresponds to a table in the user's database and each field of the model class is a column in that table.

ORM (Object-relational mapper)

Django ORM enables transmitting data between a relational database and objects of object oriented programming. In essence, the relationship between objects and their field values are used to automatically produce SQL.

Example model

from django.db import models

class Account(models.Model): # All class names are singular by convention
	name = models.CharField(max_length=50) 
	phone = models.CharField(max_length=15)
	email = models.EmailField(max_length=100)
	password = models.CharField(max_length=300)
	updated_at = models.DateTimeField(auto_now = True) 
	created_at = models.DateTimeField(auto_now_add = True)

	class Meta:
    	db_table = 'accounts'

Recording time is a feature that is complicated to implement, but Django provides this feature as a built-in method.

"auto_now_add" records time only when the field is first updated. "auto_now" records every time there is an update. "use_tz=true" will record time based on timezone mentioned in the settings file. Otherwise, the time will be recorded based on the user's computer time.

Primary Key

A primary key is an immutable, expendable field in a model class. By default, Django adds an id field to each model, which is used as the primary key for that model. However, the user can create his own primary key field by adding the keyword arg:

primary_key = True

to a field. If custom primary key field is added like above, the automatic one will not be added.

Another way of customizing primary key is using a meta class property, unique_together

class Hop(models.Model):
    migration = models.ForeignKey('Migration')
    host = models.ForeignKey(User, related_name='host_set')

    class Meta:
        unique_together = (("migration", "host"),)

Model Field Reference Guide

  • models.CharField is used to store a String field
    - class CharField(max_length=None, **options)
    - For large amounts of text, TextField() is preferred

  • models.DateField is used to store a date, represented in Python by a datetime.date instance
    - class DateField(auto_now=False, auto_now_add=False, **options)

  • models.DateTimeField(auto_now=False, auto_now_add=False, **options)
    - A date and time, represented in Python by a datetime.datetime instance

  • models.EmailField is a CharField that checks that the value is a valid email address using EmailValidator

  • models.ImageField inherits all attributes and methods from FileField, but also validates that the uploaded object is a valid image
    - class ImageField(upload_to=None, height_field=None, width_field=None, max_length=100, **options)

For more information on model fields, check Django reference.

settings.py

INSTALLED_APPS

INSTALLED_APPS holds the names of all Django applications that are activated in current Django instance.

When we start creating apps for our project, this is where we add a reference to the app's configuration class. The PollsConfig class will be in polls/apps.py, so its dotted path is polls.apps.PollsConfig.

mysite/settings.py¶
INSTALLED_APPS = [
  >>'polls.apps.PollsConfig', 
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Assuming there is a frontend developer working together on the project, there is no reason to use django.contrib.admin and django.contrib.auth, which create an admin site and an authentication system for us.

MIDDLEWARE_CLASSES

Various options for security and authetication layer in between requests and responses are set in this section of settings.py

TEMPLATES

Settings related to django templates and view variables

DATABASES

Settings related to connections with various typs of databases

STATIC_URL

Settings related to URLs or directories for static files (css, javascript, image, etc.)

The development server

$ python manage.py runserver

Changing the port

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

$ python manage.py runserver 8080

Creating the Polls app

$ python manage.py startapp polls

Django automatically generates the basic directory structure of an app.
After creating an app, be sure to add the app name to settings.py list.

0개의 댓글