[Django] development: supplementary knowledge

Posted by TGLMan on Fri, 18 Feb 2022 18:35:30 +0100

There are things that need to be checked and learned online.

paging

  • Pagination means that there is a large amount of data to be displayed on the web page. In order to facilitate reading, only part of the data is displayed in each page.
  • Benefits:
    1. Easy to read
    2. Reduce the amount of data extraction and reduce the pressure on the server.
  • Django provides Paginator class, which can easily realize paging function
  • The Paginator class is located in Django core. Paginator module.

Paginator object

  • Responsible for the overall management of paging data
  • Construction method of object
    • paginator = Paginator(object_list, per_page)
    • parameter
      • object_list list of objects that need classified data
      • per_page number of data per page
    • Return value:
      • Paginator object
  • Paginator property
    • count: the total number of objects requiring classification data
    • num_pages: total number of pages after paging
    • page_range: a range object starting from 1, which is used to record the current number of face codes
    • per_page number of data per page
  • Paginator method
    • page(number)
      • The parameter number is the page number information (starting from 1)
      • Returns the page information corresponding to the current number page
      • If the provided page number does not exist, an InvalidPage exception is thrown
  • Paginator exception
    • InvalidPage: the total exception base class, including the following two abnormal subclasses
      • PageNotAnInteger: thrown when a value other than an integer is passed to page()
      • EmptyPage: thrown when a valid value is provided to page() but there is no object on that page

Page object

  • Be responsible for the data management of a specific page
  • create object

The Page () method of the Paginator object returns the Page object

page = paginator.page(Page number)
  • Page object properties

object_list: a list of all data objects on the current page

Number: the sequence number of the current page, starting from 1

Paginator: Paginator object related to the current page object

  • Page object method

has_next (): returns True if there is a next page

has_previous(): returns True if there is a previous page

has_other_pages (): returns True if there is a previous or next page

next_page_number(): returns the page number of the next page. If the next page does not exist, an InvalidPage exception is thrown

previous_page_number(): returns the page number of the previous page. If the previous page does not exist, an InvalidPage exception is thrown

len(): returns the number of objects on the current page

  • explain:

The Page object is an iterative object. You can use the for statement to access each object in the current Page

  • Reference documents https://docs.djangoproject.com/en/2.2/topics/pagination/
  • Paging example:

View function

from django.core.paginator import Paginato
def book(request):  
    bks = Book.objects.all()
    paginator = Paginator(bks, 10)
    cur_page = request.GET.get('page', 1)  # Get the default current page
    page = paginator.page(cur_page)
    return render(request, 'bookstore/book.html', locals())

Template design

    <html>
    <head>
        <title>Pagination display</title>
    </head>
    <body>
    {% for b in page %}
        <div>{{ b.title }}</div>
    {% endfor %}
    
    {% if page.has_previous %}
    <a href="{% url 'book' %}?page={{ page.previous_page_number }}">previous page</a>
    {% else %}
    previous page
{% endif %}
    
    {% for p in paginator.page_range %}
        {% if p == page.number %}
            {{ p }}
        {% else %}
            <a href="{% url 'book' %}?page={{ p }}">{{ p }}</a>
        {% endif %}
{% endfor %}
    
    {% if page.has_next %}
    <a href="{% url 'book' %}?page={{ page.next_page_number }}">next page</a>
    {% else %}
    next page
    {% endif %}
    </body>
</html>

File download

Django can directly generate csv files in the view function and respond to the browser

import csv
from django.http import HttpResponse
from .models import Book

def make_csv_view(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="mybook.csv"'
	all_book = Book.objects.all()
    writer = csv.writer(response)
    writer.writerow(['id', 'title'])
    for b in all_book:    
    	writer.writerow([b.id, b.title])

    return response
  • The response gets a special MIME type text / csv. This tells the browser that the document is a CSV file, not an HTML file
  • The response gets an additional content disposition header that contains the name of the CSV file. It will be used by the browser for the save as... Dialog box
  • For each line in the CSV file, call writer Writerow, which passes an iteratable object, such as a list or tuple.

File upload

  • File upload must be submitted by POST
  • The file content data in the form < form > can only be included when the file is uploaded with enctype = "multipart / form data".
  • Upload files in the form with < input type = "file" name = "XXX" > tag

The name XXX corresponds to request Memory buffered file stream object corresponding to files ['xxx ']. You can pass the request The object returned by files ['xxx '] obtains the uploaded file data

file=request.FILES['xxx'] file is bound to the file stream object, and the file data can be obtained through the following information of the file stream object

file.name file name

file. Byte stream data of file

  • Form writing method of uploaded file
<!-- file: index/templates/index/upload.html -->
<html>
<head>
    <meta charset="utf-8">
    <title>File upload</title>
</head>
<body>
    <h3>Upload file</h3>
    <form method="post" action="/test_upload" enctype="multipart/form-data">
        <input type="file" name="myfile"/><br>
        <input type="submit" value="upload">
    </form>
</body>
</html>
  • In setting Py to set media related configuration; Django refers to the files uploaded by users as media resources
# file : settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • Create a media folder under the current project folder
$ mkdir media
  • View processing function scheme 1 of uploaded files: traditional writing
# file views.py
from django.http import HttpResponse
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
import os

@csrf_exempt
def upload_view(request):
    if request.method == 'GET':
        return render(request, 'test_upload.html')
    elif request.method == "POST":
        a_file = request.FILES['myfile']
        print("Upload file name is:", a_file.name)
        filename =os.path.join(settings.MEDIA_ROOT, a_file.name)
        with open(filename, 'wb') as f:
            data = a_file.file.read()
            f.write(data)  
        return HttpResponse("receive files:" + a_file.name + "success")
  • View processing function scheme 2 of uploaded files with the help of orm
#test_upload/models.py
from django.db import models
  
# Create your models here.
class Content(models.Model):

    desc = models.CharField(max_length=100)
    myfile = models.FileField(upload_to='myfiles')

#test_upload/views.py
from test_upload.models import *
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def upload_view_dj(request):
    if request.method == 'GET':
        return render(request, 'test_upload.html')
    elif request.method == 'POST':
        title = request.POST['title']
        a_file = request.FILES['myfile']
        Content.objects.create(desc=title,myfile=a_file)
        return HttpResponse('----upload is ok-----')
  • To access the uploaded resources in the browser, you need to add the binding of media route under the main route of the project in the runserver environment
  from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Browser accessible http://127.0.0.1:8000/media/xxxx

User authentication in Django (using Django authentication system)

  • Django comes with a user authentication system. It handles user accounts, groups, permissions, and cookie based user sessions.
  • effect:
    1. Add ordinary users and super users
    2. Change Password
  • Documentation see
  • User model class
    • Location: from Django contrib. auth. models import User
    • The basic attributes of the default user are:

auth basic model operation:

  • Create user

Create ordinary user create_use

from django.contrib.auth.models import Use
user = User.objects.create_user(username='user name', password='password', email='mailbox',...)
  • Create superusercreate_ superuse
from django.contrib.auth.models import Use
user = User.objects.create_superuser(username='user name', password='password', email='mailbox',...)
  • delete user
from django.contrib.auth.models import Use
try:
    user = User.objects.get(username='user name')
    user.is_active = False  # The current user is invalid
    user.save()
    print("Delete ordinary user succeeded!")
except:
    print("Failed to delete ordinary user")
  • Modify password set_password
from django.contrib.auth.models import Use
try:
    user = User.objects.get(username='xiaonao')
    user.set_password('654321')
    user.save()
    return HttpResponse("Password modification succeeded!")
except:
    return HttpResponse("Failed to modify password!")
  • Check whether the password is correct_ password
  from django.contrib.auth.models import Use
try:
    user = User.objects.get(username='xiaonao')
    if user.check_password('654321'):  # Returns True for success and False for failure
        return HttpResponse("The password is correct")
    else:
        return HttpResponse("Password error")
except: 
    return HttpResponse("There is no such user!")

auth extension field

If you need to expand new fields on the default auth table, such as phone

  1. Add new app
  2. Defining model class inherits AbstractUser
  3. settings. Auth is indicated in py_ USER_ Model = 'application name Class name '
#models.py case
from django.db import models
from django.contrib.auth.models import AbstractUser

# Create your models here.
class UserInfo(AbstractUser):

    phone = models.CharField(max_length=11, default='')
    
#settings.py add configuration
AUTH_USER_MODEL = 'user.UserInfo'

#Add user
from user.models import UserInfo
UserInfo.objects.create_user(username='guoxiao', password='123456', phone='13488871101')

Email sending

  • Use QQ email to send email
  • django. core. The mail sub package encapsulates the automatic sending SMTP protocol of e-mail
  • Preparation before:
    1. Application QQ number
    2. Log in QQ email with QQ number and modify the settings
      • Log in to with the QQ number and password you applied for https://mail.qq.com/
      • Modify QQ mailbox - > Settings - > account - > POP3/IMAP... Service
    3. Set the of Django server and send e-mail with simple mail transfer protocol (SMTP)
  • settings.py settings
# Send mail settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # Fixed writing
EMAIL_HOST = 'smtp.qq.com' # Tencent QQ email SMTP server address
EMAIL_PORT = 25  # Port number of SMTP service
EMAIL_HOST_USER = 'xxxx@qq.com'  # QQ mailbox for sending mail
EMAIL_HOST_PASSWORD = '******'  # The authorization code obtained in QQ email - > Settings - > account - > POP3/IMAP... Service to log in to QQ email from a third party
EMAIL_USE_TLS = True  # Whether to start TLS link (secure link) when communicating with SMTP server. The default is false

In view function

from django.core import mail
mail.send_mail(
            subject,  #subject
            message,  # Message content
            from_email,  # Sender [currently configured mailbox]
            recipient_list=['xxx@qq.com'],  # Recipient mailing list
            )

Project deployment

  • Project deployment refers to the actual installation of the development board software running on the development machine on the server for long-term operation after the software development is completed
  • Deployment should be carried out in the following steps

Install and configure the same version of the environment on the installation machine

  1. django project migration
  2. $sudo scp current project source code remote host address and folder
sudo scp /home/tarena/django/mysite1 root@88.77.66.55:/home/root/xxx
 Please enter root password:

3. Replace python3 manage with uwsgi The PY runserver method starts the server

4. Configure nginx reverse proxy server

5. Configure the static file path with nginx to solve the problem of static path

uWSGI gateway interface configuration (ubuntu 18.04 configuration)

  • WSGI (Web Server Gateway Interface) Web server gateway interface is an interface between Python application or framework and web server, which is widely used
  • Use Python manage Py runserver is usually used only in development and test environments.
  • When the development is finished, the perfect project code needs to run in an efficient and stable environment. At this time, WSGI can be used
  • uwsgi is a kind of WSGI. It implements http protocol, WSGI protocol and uwsgi protocol
  • Install uWSGI

The terminal inputs the following commands

sudo pip3 install uwsgi==2.0.18 -i https://pypi.tuna.tsinghua.edu.cn/simple/

Check whether the installation is successful

sudo pip3 freeze|grep -i 'uwsgi'
#If the installation is successful, the output is displayed
uWSGI==2.0.18
  • Configure uWSGI
  • Add a folder with the same name as the profile item / uwsgi ini

For example: mysite1 / mysite1 / uwsgi ini

    [uwsgi]
    # IP address in socket mode: port number
    # socket=127.0.0.1:8000
    # IP address of Http communication mode: port number
    http=127.0.0.1:8000
    # Current working directory of the project
    chdir=/home/tarena/.../my_project Here, you need to change to the absolute path of the project folder
    # WSGI The directory of the PY file, relative to the current working directory
    wsgi-file=my_project/wsgi.py
    # Number of processes
    process=4
    # Number of threads per process
    threads=2
    # pid record file of the service
    pidfile=uwsgi.pid
    # Destination file location of the service
    daemonize=uwsgi.log
    # Turn on main process management mode
master=true
  • Modify settings Py change DEBUG=True to DEBUG=False
  • Modify settings Py will allow_ Hosts = [] changed to ALLOWED_HOSTS = ['website domain name'] or ['service listening ip address']
  • Operation management of uWSGI

Start uwsgi

$ Enter the folder with the same name of the project [i.e settings.py Directory]
$ sudo uwsgi --ini uwsgi.ini

Stop uwsgi

$ Enter the folder with the same name of the project [i.e settings.py Directory]
$ sudo uwsgi --stop uwsgi.pid

explain:

  • When uwsgi is started, the program of the current django project has become a background daemon, and this process will not stop when the current terminal is closed.
  • If the stop operation fails, you need to execute the following operations to kill the process
ps aux|grep 'uwsgi'  -> see uwsgi process

tarena   103408  0.0  0.9 137172 39984 ?        S    10:02   0:01 uwsgi --ini uwsgi.ini
tarena   103410  0.0  0.9 436200 38552 ?        Sl   10:02   0:00 uwsgi --ini uwsgi.ini

ps -ef | grep 'uwsgi' | grep -v grep | awk '{print $2}' | xargs sudo kill -9

Test:

  • Input at browser side http://127.0.0.1:8000 Test
  • Note that the port number is 8000

nginx and reverse proxy configuration

  • Nginx is a lightweight high-performance Web server, which provides a series of important features such as HTTP proxy and reverse proxy, load balancing, caching and so on.
  • High efficiency in writing and executing C language
  • nginx action
  • Load balancing, multiple servers process requests in turn
  • Reverse proxy
  • Principle:
  • The client requests nginx, and then nginx forwards the request to django run by uWSGI
  • nginx installation under ubuntu $ sudo apt install nginx
vim /etc/apt/sources.list
 Change domestic source
sudo apt-get update
  • nginx configuration
  • Modify the configuration file of nginx / etc / nginx / sites enabled / default
# Add a new location item under the server node, pointing to the ip and port of uwsgi.
server {
    ...
    location / {
        uwsgi_pass 127.0.0.1:8000;  # Redirect to port 8000 of 127.0.0.1
        include /etc/nginx/uwsgi_params; # Turn all parameters to uwsgi
    }
    ...
}
nginx service control

SHELL
1
2
3
$ sudo /etc/init.d/nginx start|stop|restart|status
# or
$ sudo service nginx start|stop|restart|status

Start, stop, restart and status can be used to start, stop, restart and operate nginx services

  • Modify uWSGI configuration

Modify the folder with the same name as the project / uwsgi The Http communication mode under ini is changed to socket communication mode

[uwsgi]
# Remove the following
# http=127.0.0.1:8000
# Change to
socket=127.0.0.1:8000
  • Restart uWSGI service
Enter the folder with the same name as the project
$ sudo uwsgi --stop uwsgi.pid
$ sudo uwsgi --ini uwsgi.ini

Test:

Input at browser side http://127.0.0.1 Test

be careful:

1. At this time, the port number is 80 (nginx default)

2. Any modification in Django needs to restart uwsgi, otherwise the modification will not take effect

nginx configuration static file path

  • Create a new path - it mainly stores all static files of Django, such as: / home/tarena / project name_ static/
  • At Django settings Add new configuration to PY
STATIC_ROOT = '/home/tarena/Project name_static/static  
#Note that this configuration path is used to store all static files required in the formal environment
  • Enter the project and execute Python 3 manage py collectstatic . After executing this command, Django copies all static files of the project to static_ [including the built-in background file of Django]
  • Add new configuration to Nginx configuration
# file : /etc/nginx/sites-enabled/default
# Add a new location /static routing configuration and redirect to the specified path created in the first step
server {
    ...
    location /static {
        # The first step of root is to create the absolute path of the folder, such as:
         root /home/tarena/Project name_static;          
    }
    ...      
}

404 / 500 interface

  • Add 404 in the template folder HTML template, which will be displayed when the view triggers Http404 exception
  • 404.html works only in the release version (i.e. when DEBUG=False in setting.py)
  • When the Http404 exception is triggered to the corresponding processing function, it will jump to the 404 interface
from django.http import Http404
def xxx_view( ):
    raise Http404  # Direct return 404

Mail alarm

Some error tracking will be displayed in the error message, and sensitive information such as password will appear in these error tracking. Django has modified the filtering of sensitive information in the configuration file to multiple asterisks, but the user-defined view function requires the user to manually filter sensitive information

1. Local variables in view function

from django.views.decorators.debug import sensitive_variables

@sensitive_variables('user', 'pw', 'cc')
def process_info(user):
    pw = user.pass_word
    cc = user.credit_card_number
    name = user.name
    ...
#be careful:
#1 if the error message involves the values of local variables such as user,pw,cc, etc., it will be replaced with * * * * *, and the name variable also displays its real value
#2 when there are more than one decorator, it needs to be placed on the top
#3 if no parameter is passed, the values of all local variables will be filtered

2. Data in POST submission

from django.views.decorators.debug import sensitive_post_parameters

@sensitive_post_parameters('password', 'username')
def index(request):
    s = request.POST['username'] + request.POST['abcd']
	#'abcd' does not exist. An error is raised at this time
#The values of username and password in POST will be replaced with******