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
Check out Django Tutorials page to know more about each file.
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.
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)
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)
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)
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.
from django.urls import path
from .views import AccountView
urlpatterns = [
path('', AccountView.as_view())
]
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.
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 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.
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.
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.
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"),)
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.
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.
Various options for security and authetication layer in between requests and responses are set in this section of settings.py
Settings related to django templates and view variables
Settings related to connections with various typs of databases
Settings related to URLs or directories for static files (css, javascript, image, etc.)
$ python manage.py runserver
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
$ 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.