Tutorials | Django Tutorial

1. Introduction of Django:

Django is a high-level, open-source web framework for building web applications in Python. It follows the model-view-controller (MVC) architectural pattern, emphasizing the use of reusable components, rapid development, and the “Don’t Repeat Yourself” (DRY) principle. Developed by Django Software Foundation, Django provides a robust set of tools and features that make web development efficient and scalable.

Key Features of Django:

  1. Object-Relational Mapping (ORM):

    • Django includes a powerful ORM that allows developers to interact with databases using Python objects.
    • Supports various database backends such as PostgreSQL, MySQL, SQLite, and Oracle.

  2. Admin Interface:

    • Django comes with a built-in admin interface for managing application data.
    • Developers can easily create, update, and delete records without building a separate admin panel.

  3. URL Routing and Views:

    • Django provides a clean and simple URL routing mechanism, allowing developers to map URLs to views.
    • Views handle the business logic and return HTTP responses.

  4. Template Engine:

    • Django’s template engine allows developers to create dynamic HTML using Python-like syntax.
    • Templates support inheritance, filters, and other features for building reusable components.

  5. Middleware:

    • Middleware components in Django process requests and responses globally.
    • Developers can customize middleware to add functionality at various stages of the request-response cycle.

  6. Authentication and Authorization:

    • Django provides a built-in authentication system with support for user login, logout, and password reset.
    • Authorization is handled through a flexible permissions system.

  7. Forms Handling:

    • Django simplifies form handling, validation, and rendering.
    • Developers can create HTML forms, handle form submissions, and validate user input with ease.

  8. Security:

    • Django includes built-in security features to protect against common web vulnerabilities, such as cross-site scripting (XSS) and cross-site request forgery (CSRF).

  9. Middleware for Caching:

    • Django supports middleware for caching, allowing developers to cache the entire page or parts of it to improve performance.

  10. RESTful API Support:

    • Django Rest Framework (DRF) extends Django to support building RESTful APIs with serializers, authentication, and viewsets.

Getting Started with Django:

  1. Installation:

    • Install Django using pip:
      bash
      pip install django

  2. Create a Project:

    • Create a new Django project using the following command:
      bash
      django-admin startproject projectname

  3. Run the Development Server:

    • Navigate to the project directory and run the development server:
      bash
      python manage.py runserver

  4. Create an App:

    • Inside the project, create a new app using:
      bash
      python manage.py startapp appname

  5. Define Models, Views, and Templates:

    • Define models in the app’s models.py, create views in views.py, and templates in the templates directory.

  6. Run Migrations:

    • Run migrations to apply changes to the database:
      bash
      python manage.py makemigrations python manage.py migrate

  7. Create Admin User:

    • Create a superuser for the admin interface:
      bash
      python manage.py createsuperuser

  8. Access Admin Interface:

    • Start the development server and access the admin interface at http://localhost:8000/admin/ to manage models.

Django’s official documentation (https://docs.djangoproject.com/) is an excellent resource for in-depth learning and reference. It covers all aspects of Django development, including models, views, templates, forms, middleware, and more.

2. Django Models:

Django models are used to define the structure of the database and represent data as Python objects. Each model class corresponds to a database table, and instances of the class represent records in the table. Django’s Object-Relational Mapping (ORM) system allows you to interact with the database using Python code rather than SQL.

Creating a Model:

To create a model in Django, you define a class in your app’s models.py file. Each attribute of the class represents a field in the database table.

Example:

python

# models.py

from django.db import models

class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
publication_date = models.DateField()
is_available = models.BooleanField(default=True)

def __str__(self):
return self.title

In this example, we define a Book model with fields such as title, author, publication_date, and is_available. The __str__ method is used to provide a human-readable representation of the model instance.

Migrations:

After defining the model, you need to create migrations and apply them to the database to create the corresponding table.

bash
python manage.py makemigrations
python manage.py migrate

These commands generate migration files in the migrations directory and apply the changes to the database.

Django Model Fields:

Django provides various field types to represent different types of data in the database. Some commonly used field types include:

  • CharField: For short to medium-length strings.
  • TextField: For longer text.
  • IntegerField, FloatField: For integers and floating-point numbers.
  • DateField, DateTimeField: For dates and date-time values.
  • BooleanField: For true/false values.
  • ForeignKey: For creating relationships between models.
  • ManyToManyField: For creating many-to-many relationships.

Model Methods:

You can define methods within your model to perform specific operations or computations related to the model instance.

Example:

python

# models.py

from django.db import models

class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
publication_date = models.DateField()
is_available = models.BooleanField(default=True)

def __str__(self):
return self.title

def mark_as_unavailable(self):
self.is_available = False
self.save()

In this example, we added a mark_as_unavailable method to the Book model, which sets the is_available field to False and saves the instance.

Querying the Database:

Django provides a high-level API for querying the database. You can perform operations such as creating, updating, and deleting records using the model’s manager.

Example:

python

# views.py

from django.shortcuts import render
from .models import Book

def available_books(request):
books = Book.objects.filter(is_available=True)
return render(request, ‘books/available_books.html’, {‘books’: books})

In this example, we use Book.objects.filter(is_available=True) to retrieve all available books.

3. Class-Based Views (CBVs):

Class-Based Views (CBVs) are an alternative way to define views in Django using classes instead of functions. They provide a more organized and reusable structure for handling HTTP requests and responses. CBVs are built around the concept of inheritance, allowing you to create views by extending and customizing Django’s built-in view classes.

Basic Structure of a Class-Based View:

A CBV is defined as a class that inherits from one of Django’s generic view classes. These classes are found in the django.views module.

Example:

python

from django.views import View
from django.http import HttpResponse

class MyView(View):
def get(self, request):
return HttpResponse(“Hello, this is a Class-Based View!”)

In this example, MyView is a simple CBV that responds to HTTP GET requests with a text response.

Common Generic Class-Based Views:

Django provides a set of generic class-based views that handle common patterns. These views are defined in the django.views.generic module.

  1. DetailView:

    • Displays details of a single object.
    • Example: DetailView.as_view(model=MyModel)
  2. ListView:

    • Displays a list of objects.
    • Example: ListView.as_view(model=MyModel)
  3. CreateView:

    • Handles the creation of new objects.
    • Example: CreateView.as_view(model=MyModel)
  4. UpdateView:

    • Handles updating existing objects.
    • Example: UpdateView.as_view(model=MyModel)
  5. DeleteView:

    • Handles the deletion of objects.
    • Example: DeleteView.as_view(model=MyModel)

Using Class-Based Views:

To use a CBV, you typically create a URL pattern that maps to the view. You can use the as_view method to convert the class-based view into a function-based view that can be used in the urls.py file.

Example:

python

from django.urls import path
from .views import MyView

urlpatterns = [
path(‘my-view/’, MyView.as_view(), name=’my-view’),
]

Class-Based View Mixins:

Mixins are additional classes that provide reusable functionality to your CBVs. Django includes several mixins, such as LoginRequiredMixin, PermissionRequiredMixin, and FormMixin, that you can use to extend the functionality of your views.

Example:

python

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
from django.http import HttpResponse

class MyAuthenticatedView(LoginRequiredMixin, View):
def get(self, request):
return HttpResponse(“This view requires authentication.”)

In this example, MyAuthenticatedView requires the user to be authenticated before accessing the view.

Class-Based Views vs. Function-Based Views:

  • Organization: CBVs can provide better organization for complex views by using class inheritance and mixins.
  • Reusability: CBVs can be more reusable as you can extend and customize existing view classes.
  • Complexity: For simple views, function-based views may be more straightforward and concise.

Both class-based views and function-based views are valid approaches, and the choice between them often depends on the complexity and requirements of your application.

4. Static Files and Media Files:

 
In Django, static files and media files serve different purposes, and they are handled differently.

Static Files:

Static files are files that don’t change often, such as stylesheets, images, and JavaScript files. These files are typically served directly by the web server, and they don’t depend on the user or any dynamic input. In Django, you can organize your static files within the static directory of your apps.

How to Serve Static Files in Django:

  1. Define Static Files in Your App:

    • Create a static directory within your app.
    • Organize static files in subdirectories, e.g., static/css for CSS files.
  2. Configure STATICFILES_DIRS:

    • In your Django settings, specify additional directories for static files using the STATICFILES_DIRS setting.
    python
    # settings.py
    STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'your_app/static'),
    ]

  3. Include Static Files in Templates:

    • Use the {% load static %} template tag to include static files in your templates.
    html
    <!-- example.html -->
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
    <img src="{% static 'images/logo.png' %}" alt="Logo">

  4. Collect Static Files:

    • During deployment, use the collectstatic management command to collect all static files into a single directory.
    bash
    python manage.py collectstatic

  5. Configure Web Server:

    • Ensure that your web server is configured to serve the collected static files.

Media Files:

Media files are user-uploaded files, such as images or documents. Unlike static files, media files can change frequently and are typically associated with user-generated content. In Django, you need to configure specific settings to handle media files.

How to Handle Media Files in Django:

  1. Configure MEDIA_ROOT and MEDIA_URL:

    • Specify a directory on your server where uploaded media files will be stored (MEDIA_ROOT).
    • Define the base URL for serving media files (MEDIA_URL).
    python

    # settings.py

    MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’)
    MEDIA_URL = ‘/media/’

  2. Define Media Fields in Models:

    • In your models, use FileField or ImageField to define fields for storing media files.
    python

    # models.py

    from django.db import models

    class MyModel(models.Model):
    media_file = models.FileField(upload_to=’uploads/’)

  3. Configure urlpatterns for Development:

    • During development, configure urlpatterns in your project’s urls.py to serve media files.
    python

    # urls.py

    from django.conf import settings
    from django.conf.urls.static import static

    urlpatterns = [
    # … your other URL patterns …
    ]

    if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

  4. Collect Media Files:

    • If you are serving media files through your web server in production, ensure that it’s configured to serve the media files from MEDIA_ROOT.

    Alternatively, you can use a third-party storage solution like Amazon S3 or Google Cloud Storage for handling media files in production.

5. Django Signals:

Django signals are a mechanism for allowing decoupled applications to get notified when certain actions occur elsewhere in the application. This enables components to get notified and respond to events without being tightly coupled to the components that trigger those events. Django signals are based on the observer pattern, where a signal sender triggers an event, and multiple signal receivers respond to that event.

Here’s a basic overview of using signals in Django:

Defining a Signal:

  1. Import Necessary Components:

    • Import the django.dispatch module to define signals.
    python
    # signals.py from django.dispatch import Signal

  2. Create Signals:

    • Create signals as instances of the Signal class.
    python
    # signals.py my_signal = Signal()

Connecting Signal Receivers:

  1. Define a Receiver Function:

    • Define a function that will act as the signal receiver. This function will be called when the signal is sent.
    python
    # signals.py
    def my_receiver(sender, **kwargs):
    print("Signal received!")

  2. Connect Receiver to Signal:

    • Connect the receiver function to the signal using the connect method.
    python
    # signals.py
    my_signal.connect(my_receiver)

Sending Signals:

  1. Import Signal and Send Signal:

    • Import the signal and use the send method to trigger the signal.
    python
    # views.py
    from signals import my_signal
    def my_view(request):
    # ... some view logic ...
    # Send the signal
    my_signal.send(sender=my_view)

Disconnecting Signal Receivers (Optional):

If you want to disconnect a receiver from a signal, you can use the disconnect method.

python
# signals.py
my_signal.disconnect(my_receiver)

Using Decorators:

Django provides decorators to simplify connecting and disconnecting signal receivers. You can use @receiver to decorate a function and connect it to a signal.

python
# signals.py
from django.dispatch import
receiver @receiver(my_signal)
def my_receiver(sender, **kwargs):
print("Signal received!")

Built-in Signals:

Django comes with several built-in signals that are emitted during various stages of request processing, model saving, and other events. For example, the post_save signal is sent after a model’s save method is called.

python
# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from myapp.models import MyModel
@receiver(post_save, sender=MyModel)
def my_model_post_save(sender, instance, **kwargs):
print(f"Instance of {sender} saved: {instance}")

Django signals are a powerful tool for decoupling components within your application. They allow different parts of your application to communicate without directly depending on each other, promoting a more modular and maintainable codebase.

6. Django Testing:

Django provides a robust testing framework to help developers write and run tests for their applications. Testing is an essential part of the development process to ensure that your code behaves as expected and to catch potential issues early. In Django, you can write tests for various components, including models, views, forms, and more.

Writing Tests in Django:

Tests in Django are written using Python’s unittest module. You can create test classes that inherit from django.test.TestCase. This special test case class provides additional functionality tailored for testing Django applications.

Here’s a basic example of writing a test for a Django model:

python

# myapp/tests.py

from django.test import TestCase
from myapp.models import MyModel

class MyModelTests(TestCase):

def test_model_creation(self):
# Test the creation of a model instance
my_model = MyModel.objects.create(name=’Test Model’)
self.assertEqual(my_model.name, ‘Test Model’)
self.assertIsNotNone(my_model.created_at)

In this example, a test class MyModelTests is created, and a test method test_model_creation is defined. The test method creates an instance of MyModel and asserts that the attributes are set correctly.

Running Tests:

To run tests in Django, you can use the python manage.py test command. This command will discover and run all the tests in your Django project.

bash
python manage.py test

You can also specify a specific app or test case to run:

bash
python manage.py test myapp.tests.MyModelTests

Django Test Client:

Django provides a test client that allows you to simulate HTTP requests during tests. This client is an instance of django.test.Client and can be used to make requests to your views and test the responses.

Here’s an example of using the test client to test a view:

python

# myapp/tests.py

from django.test import TestCase, Client
from django.urls import reverse

class MyViewTests(TestCase):

def test_view_response(self):
# Test the response of a view
url = reverse(‘my_view’)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, ‘Hello, this is a view!’)

In this example, the test client is used to perform a GET request to a view, and the response is then asserted.

Mocking in Tests:

Django provides a unittest.mock module for mocking objects during tests. Mocking is useful when you want to isolate the code under test from external dependencies.

python

# myapp/tests.py

from django.test import TestCase
from unittest.mock import patch
from myapp.views import external_api_function

class MyViewTests(TestCase):

@patch(‘myapp.views.external_api_function’)
def test_external_api_call(self, mock_api_function):
# Test a view that calls an external API
mock_api_function.return_value = ‘Mocked Response’
url = reverse(‘my_view’)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, ‘Mocked Response’)

In this example, the external_api_function is replaced with a mock, allowing you to control its behavior during the test.

Django’s testing framework provides a powerful and flexible way to write and run tests for your applications. Writing tests is a good practice that helps catch bugs early, ensures that changes don’t introduce regressions, and facilitates code maintenance.

7. Security middleware and settings

Django provides several security features that you can configure using middleware and settings to enhance the security of your web application. These features include protection against common security threats and vulnerabilities.

Security Middleware:

Django includes a set of middleware components that help enforce security measures. Ensure that the following middleware classes are included in your MIDDLEWARE setting:

python
# settings.py
MIDDLEWARE = [
# ...
'django.middleware.security.SecurityMiddleware',
# ...
]

  1. SecurityMiddleware:
    • This middleware provides various security enhancements, such as setting security headers, protecting against clickjacking, and ensuring secure browser settings.
python
# settings.py
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
  • SECURE_BROWSER_XSS_FILTER: Enables XSS (cross-site scripting) protection.
  • SECURE_CONTENT_TYPE_NOSNIFF: Prevents browsers from interpreting files as a different MIME type.
  • X_FRAME_OPTIONS: Prevents your content from being embedded into other websites via <frame>, <iframe>, <embed>, or <object>.
  1. X-Content-Type-Options:
    • The X-Content-Type-Options header prevents browsers from interpreting files as a different MIME type. This is configured using the SECURE_CONTENT_TYPE_NOSNIFF setting.
python
# settings.py
SECURE_CONTENT_TYPE_NOSNIFF = True

  1. X-XSS-Protection:
    • The X-XSS-Protection header enables the browser’s built-in XSS protection.
python
# settings.py
SECURE_BROWSER_XSS_FILTER = True

  1. X-Frame-Options:
    • The X-Frame-Options header controls whether a browser should be allowed to render a page in a <frame>, <iframe>, <embed>, or <object>.
python
# settings.py
X_FRAME_OPTIONS = 'DENY'
 

CSRF Protection:

Django provides built-in protection against Cross-Site Request Forgery (CSRF) attacks. Ensure that the following settings are configured:

python
# settings.py
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'Strict'

  • CSRF_COOKIE_SECURE: Ensures that the CSRF cookie is only sent over HTTPS connections.
  • CSRF_COOKIE_SAMESITE: Controls when the CSRF cookie should be sent with a cross-origin request.

Session Security:

Configure session-related security settings to enhance the security of user sessions:

python
# settings.py
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Strict'

  • SESSION_COOKIE_SECURE: Ensures that the session cookie is only sent over HTTPS connections.
  • SESSION_COOKIE_HTTPONLY: Prevents client-side JavaScript from accessing the session cookie.
  • SESSION_COOKIE_SAMESITE: Controls when the session cookie should be sent with a cross-origin request.
  •  

Content Security Policy (CSP):

Content Security Policy (CSP) is a security standard that helps prevent various types of attacks. You can configure CSP in your Django application using the django-csp package or by setting the Content-Security-Policy header directly.

python
# settings.py
CSP_DEFAULT_SRC = "'self'"
CSP_SCRIPT_SRC = "'self'"
# Add more directives based on your application's requirements

Secure Deployment Practices:

  • Use HTTPS:
    • Always deploy your application over HTTPS to encrypt data in transit.
  • Keep Software Updated:
    • Regularly update Django, dependencies, and the web server to patch security vulnerabilities.
  • Database Security:
    • Implement strong database access controls and regularly audit database security.
  • Security Auditing:
    • Conduct regular security audits and penetration testing.

8. Django Forms:

Django Forms provide a convenient way to handle HTML forms, validate user input, and process form submissions in Django applications. Forms in Django help you create, display, and process HTML forms with minimal boilerplate code.

Creating a Simple Form:

  1. Define a Form Class:

    • Create a form class by inheriting from django.forms.Form.
    python

    # forms.py

    from django import forms

    class MyForm(forms.Form):
    name = forms.CharField(label=’Your Name’, max_length=100)
    email = forms.EmailField(label=’Your Email’)
    message = forms.CharField(label=’Your Message’, widget=forms.Textarea)

    In this example, MyForm is a simple form with fields for name, email, and a message.

  2. Rendering the Form in a View:

    • In your views, instantiate the form and pass it to the template context.
    python

    # views.py

    from django.shortcuts import render
    from .forms import MyForm

    def my_view(request):
    if request.method == ‘POST’:
    form = MyForm(request.POST)
    if form.is_valid():
    # Process the form data
    # …
    else:
    form = MyForm()

    return render(request, ‘my_template.html’, {‘form’: form})

    In this example, the view checks if the form was submitted. If it was, it validates the form data. If the form is valid, you can process the data.

  3. Rendering the Form in a Template:

    • Use the form object in your template to render the HTML form.
    html

    <!– my_template.html –>

    <form method=”post” action=””>
    {% csrf_token %}
    {{ form.as_p }}
    <button type=”submit”>Submit</button>
    </form>

    The {{ form.as_p }} template tag renders the form fields as paragraphs.

Form Validation:

Django forms come with built-in validation methods that you can use to validate user input. For example, the CharField has a max_length parameter that validates the length of the input.

python

# forms.py

from django import forms

class MyForm(forms.Form):
name = forms.CharField(label=’Your Name’, max_length=100)
email = forms.EmailField(label=’Your Email’)
message = forms.CharField(label=’Your Message’, widget=forms.Textarea)

def clean_name(self):
name = self.cleaned_data[‘name’]
if len(name) < 2:
raise forms.ValidationError(“Name must be at least 2 characters long.”)
return name

In this example, a custom clean_name method is added to the form class to perform additional validation on the “name” field.

Handling Form Submissions:

When the form is submitted, you need to handle the POST request in your view and process the form data.

python

# views.py

from django.shortcuts import render, redirect
from .forms import MyForm

def my_view(request):
if request.method == ‘POST’:
form = MyForm(request.POST)
if form.is_valid():
# Process the form data
name = form.cleaned_data[‘name’]
email = form.cleaned_data[’email’]
message = form.cleaned_data[‘message’]
# Perform further actions with the form data
# …

return redirect(‘success_page’)
else:
form = MyForm()

return render(request, ‘my_template.html’, {‘form’: form})

In this example, if the form is valid, you can access the cleaned data using form.cleaned_data. After processing the data, you may want to redirect the user to a success page or perform other actions.

Form Widgets:

Django forms come with a variety of built-in widgets (e.g., TextInput, Textarea, Select) that control the HTML rendering of form fields. You can customize the appearance of your forms by selecting appropriate widgets or creating your own.

9. Deployment and Hosting:

Deploying and hosting a Django web application involves several steps, from preparing your application for production to choosing a hosting provider. Below is a general guide to help you deploy and host your Django application.

Preparing Your Django Application for Production:

  1. Update Settings: Adjust your Django settings for production by setting DEBUG = False, configuring ALLOWED_HOSTS, and securing sensitive information.

  2. Static Files: Collect and serve static files by running python manage.py collectstatic. Configure your web server to serve these files.

  3. Database: Ensure your production database is configured correctly. You might want to use a more robust database like PostgreSQL.

  4. Security Measures: Implement security measures, such as setting up HTTPS, configuring secure headers, and enabling CSRF protection.

Choosing a Hosting Provider:

  1. Platform as a Service (PaaS): Platforms like Heroku, Google App Engine, and AWS Elastic Beanstalk provide a managed environment for deploying web applications. They abstract away server management tasks.

  2. Infrastructure as a Service (IaaS): Services like AWS EC2, DigitalOcean, and Linode allow you to provision virtual machines where you have more control over the server configuration.

  3. Container Orchestration: Platforms like Docker and Kubernetes provide containerization solutions. Deploying your Django app in containers allows for scalability and easy management.

Deploying to Heroku (Example):

  1. Install Heroku CLI: Install the Heroku Command Line Interface (CLI) by following the instructions on the Heroku website.

  2. Login to Heroku:

    Run heroku login in your terminal to authenticate your Heroku account.

  3. Create a Procfile:

    • Create a file named Procfile (no file extension) in your project’s root directory. This file tells Heroku how to run your application.
    plaintext
    web: gunicorn your_project_name.wsgi

    Replace your_project_name with the actual name of your Django project.

  4. Install Gunicorn:

    Install Gunicorn, a production-ready WSGI server, by running pip install gunicorn.

  5. Update requirements.txt:

    • Make sure Gunicorn is included in your requirements.txt file.
    plaintext
    gunicorn==20.1.0

  6. Configure Database: If you’re using a database, configure the production database in your Heroku environment variables.

  7. Deploy to Heroku:

    • Initialize a Git repository if you haven’t already (git init), commit your changes, and push to Heroku.
    bash
    heroku create your-app-name
    git add .
    git commit -m "Initial commit"
    git push heroku master

  8. Migrate Database:

    • Run migrations on the Heroku server.
    bash
    heroku run python manage.py migrate

  9. Open the App:

    • Visit the deployed app using the Heroku-provided URL.
    bash
    heroku open

Other Hosting Providers:

For hosting on other providers like AWS, DigitalOcean, or Google Cloud, the steps may vary. Generally, you’ll need to provision a server, configure the server environment, deploy your code, and handle database migrations.

Considerations:

  • Scalability: Choose a hosting solution that can scale with your application’s growth.

  • Monitoring and Logging: Implement monitoring and logging to track the performance and issues of your application.

  • Backup and Recovery: Set up regular backups and have a recovery plan in case of failures.

  • Domain and SSL: Configure your domain and enable SSL for secure communication.