Daily fresh Django project

Keywords: Django Redis Database MySQL

1. E-commerce mode

B2B: Business to Business (Alibaba)

C2C: customer to consumer

B2C: Business to Customer

C2B: Customer to Business individual to enterprise (commodity house matching) (individual demand, enterprise production)

O2O: Online to Offline (meituan, hungry)

F2C: Factory to Customer

B2B2C: Enterprise (Supplier) - enterprise (e-commerce enterprise) - individual (consumer) (Jingdong, tmall)

 

2. Page description

index.html Home page (with commodity classification menu, display of "register | login" and "user information" switch, carousel chart)

list.html Product list page

detail.html Product details page

cart.html Shopping cart page

place_order.html Submit order page

login.html Landing page

register.html Registration page

user_center_info.html User center - user information page

user_center_order.html User center - user order page

user_center_site.html User center - receiving address page

 

3. SPU and SKU

SPU=Standard Product Unit, which is the smallest unit of product information aggregation. For example, iPhone 7 is an SPU. No matter in black, gold, 64G or 128G, it has a product profile, product promotion video and other information. It is an abstract concept and cannot correspond to a specific product;

SKU=Stock Keeping Unit, which is the smallest inventory unit that cannot be separated physically. After the classification of types and specifications, for example, iPhone 7 rose gold 128G is SKU, which can truly correspond to a specific product;

 

4. Database design

Note: for a one to many relationship, multiple entries cannot be placed in the same table, and multiple entries should be extracted to create a single table;

User table:

ID, user name, password, mailbox, activation or not, permission ID (ordinary user / administrator)

Address table:

ID, user ID, recipient, address, zip code, contact information, default or not

Product SKU table:

ID, SKU name, introduction, price, unit, inventory, sales volume, picture (one), status (on and off shelves), category ID, SPU ID

(goods and pictures are one-to-many relationships, so pictures should be separated from goods as a new table, but in order to display the goods list faster, we should put a picture for the list into the goods list, because if we don't put it, there will be 50 associated queries to display 50 goods in the list, and so many associated queries affect efficiency, so we exchange space for space Take time)

Product SPU table:

ID, SPU name, details

(in fact, the category ID also belongs to the SPU table, because the gold and black of iPhone 7 belong to the same category, but for the previous reason, if you put it in the SPU table, you need to use a lot of associated queries on the product list page, which is inefficient.)

Product picture table:

ID, picture, SKU commodity ID

List of commodity categories:

ID, category name, logo, picture

Home page rotation table:

ID, SKU ID, picture, index (sequence)

Homepage promotion activity table:

ID, picture, active page url, index

Display table of classified commodities on the home page:

ID, SKU ID, category ID, index, display ID (picture / text)

Order information form:

ID, receiving address ID, user ID, payment method, total amount, freight, payment status, creation time

Order line item table:

ID, SKU ID, order ID, product quantity, price when placing order, comment

redis realizes the functions of shopping cart and browsing record;

 

5. Build Django framework

Premise: install django, pymysql, mysql and other packages;

Find a suitable path to generate Django project and application:

Open with PyCharm, create a new python package called apps in the project directory, and include all applications:

 

6. Registered application (scheme 2 is not easy to use after testing, only scheme 1 can be used)

1) Scheme 1: add one layer of apps to the path/

Because apps / are built by yourself, you can't find where to write userDjango directly. To write apps/user:

# settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'apps.cart',  #Shopping Cart module
    'apps.goods',  # Commodity module
    'apps.order',  # Order module
    'apps.user',  # User module
]
# urls.py

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

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

2) Scheme 2: add apps / to BASE_DIR

Because Django looks for registered applications in the path, just like the environment variable of the computer, BASE_DIR in path (BASE_DIR is the absolute path of the project), so he will look for base_ The child directory of dir will not be found (i.e. apps can be found, apps/user can not be found). Therefore, as long as we add apps to the path, we can find the user under apps:

# settings.py Add

# Add apps / to path
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
# settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'cart',  #Shopping Cart module
    'goods',  # Commodity module
    'order',  # Order module
    'user',  # User module
]
# urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('user/', include('user.urls', namespace='user')),  # User module; namespace is used for reverse parsing to generate url
    path('order/', include('order.urls', namespace='order')),  # Order module
    path('cart/', include('cart.urls', namespace='cart')),  # Shopping Cart module
    path('', include('goods.urls', namespace='goods')),  # Product module, as the default home page, matches unconditionally, so it is placed at the bottom

]

The code reports yellow because PyCharm can't find the path, because the code hasn't been run, apps / hasn't been added to the path, and there will be no problem after running:

sys.path and os.path The former is the environment variable, the latter is the object to operate the path, which can be used for path join and other operations;

 

7. Template path

Create the templates folder in the root directory of the project and add this path:

 

8. Configuration database

# settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'dailyfresh',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': 'localhost',  # Write localhost directly with mysql of this machine
        'PORT': 3306,
    }
}
# __init__.py

import pymysql

# Cheat Django to avoid version checking and error reporting
pymysql.version_info = (1, 3, 13, 'final', 0)

pymysql.install_as_MySQLdb()

To create a database manually:

desc tablename;  // View table structure

Note: if the mysql bound by the project is on another host of the same subnet, the mysql of the other host cannot be bound to 127.0.0.1. To bind the IP that other hosts can access, but mysql is bound to 127.0.0.1 by default. Modify:

sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
//Modify bind address= xx.xx.xx . XX (IP found by ifconfig)
sudo service mysql restart

In addition, the host of mysql also authorizes the host of the project to enter the mysql client and execute the following commands:

// Authorize all tables of the dailyfresh database to the root user of the IP to log in with the password root
grant all privileges on dailyfresh.* to 'root'@'Host of the project IP' identified by 'root' with grant option;
// Make the authorization effective
flush privileges;

 

9. Configure language time zone

 

10. Static file directory

Create the static folder in the root directory of the project settings.py Add:

# settings.py Add

# Static file directory
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

 

11. Application url

One for each application urls.py , temporarily:

from django.urls import path

urlpatterns = [
   
]

Django's url system is actually routing;

 

12. Design model class

The new python package in the project directory is called db, including a python file called base_model.py :

The contents are as follows:

from django.db import models

class BaseModel(models.Model):
    '''Model abstract base class'''
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='Creation time')
    update_time = models.DateTimeField(auto_now=True, verbose_name='Update time')
    is_delete = models.BooleanField(default=False, verbose_name='Delete tag')

    class Meta:
        # Description is an abstract model class
        abstract = True

# Meta is a nested class. The purpose is to add some functions to the parent class or specify some standards
# Because the model classes to be written next have some common properties, write these properties to a class, and let those model classes inherit this class

Next in the application models.py Write in model class:

user/models.py:

from django.db import models
from django.contrib.auth.models import AbstractUser
from db.base_model import BaseModel


class User(AbstractUser, BaseModel):
    '''User model class'''

    class Meta:
        db_table = 'df_user'
        verbose_name = 'user'
        verbose_name_plural = verbose_name


class Address(BaseModel):
    '''Address model class'''
    user = models.ForeignKey('User', verbose_name='Account', on_delete=models.CASCADE)
    receiver = models.CharField(max_length=20, verbose_name='addressee')
    addr = models.CharField(max_length=256, verbose_name='Receiving address')
    zip_code = models.CharField(max_length=6, null=True, verbose_name='Postal Code')
    phone = models.CharField(max_length=11, verbose_name='contact number')
    is_default = models.BooleanField(default=False, verbose_name='Default or not')

    class Meta:
        db_table = 'df_address'
        verbose_name = 'address'
        verbose_name_plural = verbose_name

# The User model class does not write too many fields, but inherits the abstract class AbstractUser built in Django
# AbstractUser class is used for authentication by Django's built-in user authentication system,
# Instead of writing user authentication code, we use Django's built-in authentication system,
# AbstractUser class contains user name, password, mailbox, activation no, administrator no and other fields,
# After we inherit it, we don't have to write these fields

goods/models.py:

from django.db import models
from db.base_model import BaseModel
from tinymce.models import HTMLField

class GoodsType(BaseModel):
    '''Commodity type model class'''
    name = models.CharField(max_length=20, verbose_name='Category name')
    logo = models.CharField(max_length=20, verbose_name='identification')
    image = models.ImageField(upload_to='type', verbose_name='Product type picture')

    class Meta:
        db_table = 'df_goods_type'
        verbose_name = 'Commodity category'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class GoodsSKU(BaseModel):
    '''commodity SKU Model class'''
    status_choices = (
        (0, 'Offline'),
        (1, 'go online'),
    )
    type = models.ForeignKey('GoodsType', verbose_name='Commodity category', on_delete=models.CASCADE)
    goods = models.ForeignKey('Goods', verbose_name='commodity SPU', on_delete=models.CASCADE)
    name = models.CharField(max_length=20, verbose_name='Commodity name')
    desc = models.CharField(max_length=256, verbose_name='Product introduction')
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='commodity price')
    unite = models.CharField(max_length=20, verbose_name='Commodity unit')
    image = models.ImageField(upload_to='goods', verbose_name='Product picture')
    stock = models.IntegerField(default=1, verbose_name='Merchandise inventory')
    sales = models.IntegerField(default=0, verbose_name='Sales volume of goods')
    status = models.SmallIntegerField(default=1, choices=status_choices, verbose_name='Commodity status')

    class Meta:
        db_table = 'df_goods_sku'
        verbose_name = 'commodity'
        verbose_name_plural = verbose_name


class Goods(BaseModel):
    '''commodity SPU Model class'''
    name = models.CharField(max_length=20, verbose_name='commodity SPU name')
    # Rich text type: text with formatting
    detail = HTMLField(blank=True, verbose_name='Product details')

    class Meta:
        db_table = 'df_goods'
        verbose_name = 'commodity SPU'
        verbose_name_plural = verbose_name


class GoodsImage(BaseModel):
    '''Product image model class'''
    sku = models.ForeignKey('GoodsSKU', verbose_name='commodity', on_delete=models.CASCADE)
    image = models.ImageField(upload_to='goods', verbose_name='Picture path')

    class Meta:
        db_table = 'df_goods_image'
        verbose_name = 'Product picture'
        verbose_name_plural = verbose_name


class IndexGoodsBanner(BaseModel):
    '''Home page carousel commodity display model class'''
    sku = models.ForeignKey('GoodsSKU', verbose_name='commodity', on_delete=models.CASCADE)
    image = models.ImageField(upload_to='banner', verbose_name='picture')
    index = models.SmallIntegerField(default=0, verbose_name='Order of presentation')

    class Meta:
        db_table = 'df_index_banner'
        verbose_name = 'Home page carousel'
        verbose_name_plural = verbose_name


class IndexTypeGoodsBanner(BaseModel):
    '''Home category commodity display model'''
    DISPLAY_TYPE_CHOICES = (
        (0, "title"),
        (1, "picture")
    )

    type = models.ForeignKey('GoodsType', verbose_name='Commodity type', on_delete=models.CASCADE)
    sku = models.ForeignKey('GoodsSKU', verbose_name='commodity SKU', on_delete=models.CASCADE)
    display_type = models.SmallIntegerField(default=1, choices=DISPLAY_TYPE_CHOICES, verbose_name='Display type')
    index = models.SmallIntegerField(default=0, verbose_name='Order of presentation')

    class Meta:
        db_table = 'df_index_type_goods'
        verbose_name = "Home page category display goods"
        verbose_name_plural = verbose_name


class IndexPromotionBanner(BaseModel):
    '''Homepage promotion model class'''
    name = models.CharField(max_length=20, verbose_name='Activity name')
    url = models.URLField(verbose_name='Activity links')
    image = models.ImageField(upload_to='banner', verbose_name='Activity picture')
    index = models.SmallIntegerField(default=0, verbose_name='Order of presentation')

    class Meta:
        db_table = 'df_index_promotion'
        verbose_name = "Home promotion"
        verbose_name_plural = verbose_name

# HTMLField is not a built-in type of Django, but a rich text type introduced by itself,
# The difference between rich text and ordinary text is that rich text has format,
# Product details are often not plain text but formatted
# The logo field is not a picture type but a CharField,
# Because the logo of the front end is not a directly specified image, but a sprite image,
# Use class to specify different logo s, and this CharField is the class name
# The choices field is used to limit the value range

To install and configure tinymce for the HTML field used here:

tinymce is just a kind of rich text editor. After logging in to the admin interface, the field is edited with rich text editor. When it is saved in the database, it is an html string, and its sql type is longtext;

# settings.py Add

# Rich text editor configuration
TINYMCE_DEFAULT_CONFIG = {
    'theme': 'advanced',
    'width': 600,
    'height': 400,
}
# urls.py Add url

urlpatterns = [
    path('admin/', admin.site.urls),
    path('tinymce/',include('tinymce.urls')),  # Rich Text Editor 
    path('user/', include('user.urls', namespace='user')),
    path('order/', include('order.urls', namespace='order')),
    path('cart/', include('cart.urls', namespace='cart')),
    path('', include('goods.urls', namespace='goods')),
]

The ImageField used should also be installed:

order/models.py:

from django.db import models
from db.base_model import BaseModel


class OrderInfo(BaseModel):
    '''Order model class'''
    PAY_METHOD_CHOICES = (
        (1, 'Cash on Delivery'),
        (2, 'Wechat payment'),
        (3, 'Alipay'),
        (4, 'UnionPay payment')
    )

    ORDER_STATUS_CHOICES = (
        (1, 'To be paid'),
        (2, 'To be delivered'),
        (3, 'To be received'),
        (4, 'To be evaluated'),
        (5, 'Completed')
    )

    order_id = models.CharField(max_length=128, primary_key=True, verbose_name='order id')
    user = models.ForeignKey('user.User', verbose_name='user')
    addr = models.ForeignKey('user.Address', verbose_name='address')
    pay_method = models.SmallIntegerField(choices=PAY_METHOD_CHOICES, default=3, verbose_name='Payment method')
    total_count = models.IntegerField(default=1, verbose_name='Quantity of goods')
    total_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Total price of goods')
    transit_price = models.DecimalField(max_digits=10, decimal_places=2,verbose_name='Order freight')
    order_status = models.SmallIntegerField(choices=ORDER_STATUS_CHOICES, default=1, verbose_name='Order status')
    trade_no = models.CharField(max_length=128, verbose_name='Payment No')

    class Meta:
        db_table = 'df_order_info'
        verbose_name = 'order'
        verbose_name_plural = verbose_name


class OrderGoods(BaseModel):
    '''Order item model class'''
    order = models.ForeignKey('OrderInfo', verbose_name='order')
    sku = models.ForeignKey('goods.GoodsSKU', verbose_name='commodity SKU')
    count = models.IntegerField(default=1, verbose_name='Number of goods')
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='commodity price')
    comment = models.CharField(max_length=256, verbose_name='comment')

    class Meta:
        db_table = 'df_order_goods'
        verbose_name = 'Order line item'
        verbose_name_plural = verbose_name

# Payment number is the number generated after Alipay payment.

 

13. Specify the model class used by Django authentication system

# settings.py Add

# Model classes used by Django authentication system
AUTH_USER_MODEL = 'user.User'

If not specified, Django has its own model class. After migration, those fields (user name, password, mailbox activation, administrator or not) will be created in an auth_user's table, not our own user.User , and the reason our user model class does not define any fields is that Django automatically creates fields in order to specify user as the authentication model here.

If not specified, when Python is executed manage.py When createsuperuser is created, the generated administrator user will be added to auth_ In the user table, when specified, it is added to the specified table.

Field is_ super_ If user = 1, it means administrator user, and field is_ If staff = 1, it means that it is an ordinary user;

 

14. Migration

These two steps are not smooth sailing. The first step is to report an error:

reason:

One to many relationship (foreign key ForeignKey) to specify on_delete attribute, which specifies how to handle the foreign key if it is deleted. Refer to: https://blog.csdn.net/KreaWu/article/details/89400647

(the above code has been changed)

Step 2 error reporting:

Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.

reason:

App not set_ Name, modify:

# apps/cart/urls.py


from django.urls import path

app_name = 'cart'

urlpatterns = [

]
# apps/goods/urls.py


from django.urls import path

app_name = 'goods'

urlpatterns = [

]
# apps/order/urls.py


from django.urls import path

app_name = 'order'

urlpatterns = [

]
# apps/user/urls.py


from django.urls import path

app_name = 'user'

urlpatterns = [

]

For each application urls.py Add app before urlpatterns = []_ name = 'xxxx' ;

After migration, tables are created in the database:

 

15. Start server

Copy the static page to the static Directory:

So far, the framework has been completed, and there are two small points:

1)verbose_name and verbose_name_plural

Both model classes and fields can specify verbose_name is the name displayed in the admin interface;

The model class can also specify verbose_name_plural, specifying verbose_ Plural form of name;

If verbose in class Meta of model class_ Name = product, then the table name displayed in the admin interface is product s. if you add verbose_name_plural = verbose_name indicates that the table name is commodity, that is, the plural number is the same as the singular number;
Project directory/ admin.py To register model classes in:

2) The ImageField type field in the model class has an attribute upload_to='goods'this is for Django's built-in file storage system. When uploading pictures on the admin interface, they will be saved to the goods folder. However, we do not use Django's built-in file storage system, but use a distributed file storage system, so this field property does not matter;

 

16. User registration

hold register.html Copy to the templates directory;

Write view function:

# user/views.py


from django.shortcuts import render

# /user/register
def register(request):
    '''Show registration page'''
    return render(request, 'register.html')

Configure routes:

# user/urls.py

from django.urls import path
from apps.user import views

app_name = 'user'

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

Browser Test:

To modify a style path:

 
{% load static%} {import static file path {} 
{% static 'css/reset.css '%} {all static files are written in this form {}

To modify form properties:

{# register.html #}

<form method="post" action="user/register_handle">
    {% csrf_token %}  {# use Django Of csrf protect #}

Define view functions:

#user/views.py Add

# /user/register_handle
def register_handle(request):
    '''Registration processing'''
    # Basic steps of trying function: receiving data, data verification, business processing, returning response
    username = request.POST.get('user_name')
    password = request.POST.get('pwd')
    email = request.POST.get('email')
    allow = request.POST.get('allow')

    if not all([username, password, email]):  # all tests are not empty
        return render(request, 'register.html', {'errmsg': 'Incomplete data'})
    if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
        return render(request, 'register.html', {'errmsg': 'Incorrect mailbox format'})
    if allow != 'on':
        return render(request, 'register.html', {'errmsg': 'Please agree to the agreement'})

    user = User.objects.create_user(username, email, password)

    return redirect(reverse('goods:index'))  # namespace:name

# redirect redirection, reverse parsing
# Reverse parsing is to dynamically generate paths based on namespace and name, without having to write the paths dead
# Pay attention to reverse parsing namespace:name Do not have space after colon!!!!!!!!!

Configure routes:

# user/urls.py Add url

from django.urls import path
from apps.user import views

app_name = 'user'

urlpatterns = [
    path('register/', views.register, name='register'),  # register
    path('register_handle', views.register_handle, name='register_handle'),  # Registration processing
]

The redirected index page should also be configured:

Put static/index.html Copy to the templates directory;

# goods/views.py

from django.shortcuts import render

# 127.0.0.1:8000
def index(requst):
    '''home page'''
    return render(requst, 'index.html')
# goods/urls.py

from django.urls import path
from apps.goods import views

app_name = 'goods'

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

Browser Test: (unified user password 11111111)

Click Register to jump to:

View database, user created successfully: (\ G let the results be displayed in rows)

The password is encrypted (industry standard); is_active=1 no, we need to make the newly registered account inactive, and then activate it through email, so:

# user/ views.py  register_ Handle modification

user = User.objects.create_user(username, email, password)
user.is_active = 0
user.save()

Two paths are used for registration page and registration processing, and one path can be used instead:

# user/views.py

import re
from django.shortcuts import render, redirect
from django.urls import reverse

from apps.user.models import User

# /user/register
def register(request):
    if request.method == 'GET':
        # Show registration page
        return render(request, 'register.html')
    elif request.method == 'POST':
        # Registration processing
        # Basic steps of trying function: receiving data, data verification, business processing, returning response
        username = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        if not all([username, password, email]):  # all tests are not empty
            return render(request, 'register.html', {'errmsg': 'Incomplete data'})
        if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return render(request, 'register.html', {'errmsg': 'Incorrect mailbox format'})
        if allow != 'on':
            return render(request, 'register.html', {'errmsg': 'Please agree to the agreement'})

        user = User.objects.create_user(username, email, password)
        user.is_active = 0
        user.save()

        return redirect(reverse('goods:index'))  # namespace:name

        # redirect redirection, reverse parsing
        # Reverse parsing is to dynamically generate paths based on namespace and name, without having to write the paths dead
        # Pay attention to reverse parsing namespace:name Do not have space after colon!!!!!!!!!
# user/urls.py

from django.urls import path
from apps.user import views

app_name = 'user'

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

# As the last section of the path, path('register ') cannot have /, otherwise Page Not Found will be reported
# templates/register.html Change the form submission path

<form method="post" action="/user/register">

So far, method view is used. Different request methods need to define different methods. Now use class view instead, define a view class, corresponding to a path, define get() and post() member methods, corresponding to different requests:

# user/views.py

import re
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View

from apps.user.models import User

# /user/register
class RegisterView(View):
    '''register'''
    def get(self, request):
        # Show registration page
        return render(request, 'register.html')

    def post(self, request):
        # Registration processing
        # Basic steps of trying function: receiving data, data verification, business processing, returning response
        username = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        if not all([username, password, email]):  # all tests are not empty
            return render(request, 'register.html', {'errmsg': 'Incomplete data'})
        if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return render(request, 'register.html', {'errmsg': 'Incorrect mailbox format'})
        if allow != 'on':
            return render(request, 'register.html', {'errmsg': 'Please agree to the agreement'})

        user = User.objects.create_user(username, email, password)
        user.is_active = 0
        user.save()

        return redirect(reverse('goods:index'))  # namespace:name

        # redirect redirection, reverse parsing
        # Reverse parsing is to dynamically generate paths based on namespace and name, without having to write the paths dead
        # Pay attention to reverse parsing namespace:name Do not have space after colon!!!!!!!!!
# user/urls.py modify

path('register', views.RegisterView.as_view(), name='register'),  # register

Class view principle:

dispatch() method, which is distributed to different methods according to different request methods. In fact, the method string is obtained and converted to lowercase as the method name to call the method;

 

17. User activation

After the user registers, he sends a link to the mailbox. After the user clicks the link, the server activates the user according to the identity information in the link. The user's identity information can use the user's id in the data table, but it can't be clear text, because once the user sees that it is an id, it will be attacked, so it uses its dangerous encryption;

Install itsdangerous: (if you want to install timeout later, you can use PyCharm's Terminal to install it.)

Test its dangerous:

Serializer (key, expiration time) creates objects, dumps (Json) encrypts, and loads (byte string) decrypts;

As shown in the figure, as long as the key is the same, it can be decrypted. After the key expires, the decryption reports an error:

163 mailbox opens smtp service:

Will send the verification code to the mobile phone, set the authorization code after the verification is successful, save the authorization code, and use it when sending emails;

Send mail:

# settings.py Add

# Send mail configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = 'django_core_mail@163.com'  # Email address
EMAIL_HOST_PASSWORD = 'django2019'  # Authorization code
EMAIL_FROM = 'DailyFresh<django_core_mail@163.com>'  # Sender nickname < email_ HOST_ USER>
# user/views.py Modify registration view

# /user/register
class RegisterView(View):
    '''register'''
    def get(self, request):
        # Show registration page
        return render(request, 'register.html')

    def post(self, request):
        # Registration processing
        # Basic steps of trying function: receiving data, data verification, business processing, returning response
        username = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        if not all([username, password, email]):  # all tests are not empty
            return render(request, 'register.html', {'errmsg': 'Incomplete data'})
        if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return render(request, 'register.html', {'errmsg': 'Incorrect mailbox format'})
        if allow != 'on':
            return render(request, 'register.html', {'errmsg': 'Please agree to the agreement'})

        user = User.objects.create_user(username, email, password)
        user.is_active = 0
        user.save()

        seri = Serializer(settings.SECRET_KEY, 600)
        info = {'confirm': user.id}
        token = seri.dumps(info).decode('utf8')  # bytes after encryption, turn to utf-8

        subject = 'Daily fresh user activation'
        message = 'Stay indoors, fresh every day'
        sender = settings.EMAIL_FROM
        receiver_list = [email]
        html_message = '<h1>%s,Welcome to daily fresh</h1>Please click on the link to activate the user:<br/>' \
                       '<a href="http://127.0.0.1:8000/user/active/%s">' \
                       'http://127.0.0.1:8000/user/active/%s</a>' % (username, token, token)
        send_mail(subject, message, sender, receiver_list, html_message=html_message)

        return redirect(reverse('goods:index'))  # namespace:name

        # redirect redirection, reverse parsing
        # Reverse parsing is to dynamically generate paths based on namespace and name, without having to write the paths dead
        # Pay attention to reverse parsing namespace:name Do not have space after colon!!!!!!!!!
        # message is plain text, html_message can be html, note html_message is a key parameter
        # With html_message, message will not be displayed
# user/views.py Add

# user/active
class ActiveView(View):
    '''User activation'''
    def get(self, request, token):
        seri = Serializer(settings.SECRET_KEY, 600)
        try:
            info = seri.loads(token)
            id = info['confirm']

            user = User.objects.get(id=id)
            user.is_active = 1
            user.save()

            return redirect(reverse('user:login'))
        except SignatureExpired as e:
            # Activation link expired
            return HttpResponse('Activation link expired')

# user/login
class LoginView(View):
    '''Sign in'''
    def get(self, request):
        '''Show login page'''
        return render(request, 'login.html')
# user/urls.py Add url

path('active/<token>', views.ActiveView.as_view(), name='active'),  # activation
path('login', views.LoginView.as_view(), name='login'),  # Sign in

# Capture parameters with < > in the path

Click Register to go to the home page:

  

View database, is_active=0:

Check the email (may be in the dustbin), click the link to activate and jump to the login page:

  

View database, is_active=1:

Activation complete.

PS: difference between two packages:

View: django.views.View  or  django.views.generic.base.View?

settings: dailyfresh.settings  or  django.conf.settings?

 

18. Send mail asynchronously

Django's built-in send_email() is blocked and should be asynchronous for user experience; any time-consuming operation should be asynchronous;

Cell ry task queue mechanism:

To install cellery:

# Because redis cluster needs redis2.10.6, and the latest version of corery's dependency package kombu is not compatible with redis2.10.6
# Therefore, the lower version of kombu must be used. Because of the dependency, the lower version of cellery is also required

(env1fordjango) pip3 install kombu==4.2.0 
(env1fordjango) pip3 install celery==4.2.0

New python package, python file: cellery_ tasks.tasks

Define task functions

# celery_tasks/tasks.py

from celery import Celery
from django.core.mail import send_mail
from dailyfresh import settings

# Initialize Django environment variable, which is used by task executor
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dailyfresh.settings')
django.setup()

# Create a cell ry instance object, write parameter 1 casually, parameter 2 is the intermediary, local redis, database 8
app = Celery('celery_tasks.tasks', broker='redis://127.0.0.1:6379/8')

# Define task function, a task is a function
@app.task
def send_active_email(to_email, username, token):
    subject = 'Daily fresh user activation'
    message = 'Stay indoors, fresh every day'
    sender = settings.EMAIL_FROM
    receiver_list = [to_email]
    html_message = '<h1>%s,Welcome to daily fresh</h1>Please click on the link to activate the user:<br/>' \
                   '<a href="http://127.0.0.1:8000/user/active/%s">' \
                   'http://127.0.0.1:8000/user/active/%s</a>' % (username, token, token)
    send_mail(subject, message, sender, receiver_list, html_message=html_message)

Call the task function and issue the task:

# user/views.py Modify the registration view to change the email code to the sending task

# /user/register
class RegisterView(View):
    '''register'''
    def get(self, request):
        # Show registration page
        return render(request, 'register.html')

    def post(self, request):
        # Registration processing
        # Basic steps of trying function: receiving data, data verification, business processing, returning response
        username = request.POST.get('user_name')
        password = request.POST.get('pwd')
        email = request.POST.get('email')
        allow = request.POST.get('allow')

        if not all([username, password, email]):  # all tests are not empty
            return render(request, 'register.html', {'errmsg': 'Incomplete data'})
        if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return render(request, 'register.html', {'errmsg': 'Incorrect mailbox format'})
        if allow != 'on':
            return render(request, 'register.html', {'errmsg': 'Please agree to the agreement'})

        user = User.objects.create_user(username, email, password)
        user.is_active = 0
        user.save()

        seri = Serializer(settings.SECRET_KEY, 600)
        info = {'confirm': user.id}
        token = seri.dumps(info).decode('utf8')  # bytes after encryption, turn to utf-8

        # Issue task
        send_active_email.delay(email, username, token)

        return redirect(reverse('goods:index'))  # namespace:name

        # redirect redirection, reverse parsing
        # Reverse parsing is to dynamically generate paths based on namespace and name, without having to write the paths dead
        # Pay attention to reverse parsing namespace:name Do not have space after colon!!!!!!!!!
        # message is plain text, html_message can be html, note html_message is a key parameter
        # With html_message, message will not be displayed

Start the redis service because the man in the middle specified in the task function is 127.0.0.1 redis.conf bind 127.0.0.1 is also required to be consistent in, otherwise the connection cannot be reached;

Start task executor, cellery_ tasks.tasks Is the py file where the specified task function is located, - l info is the print log information:

The yellow warning is not for cellery, but for Django, which means that opening Debug will lead to memory leakage. Do not open Debug in the production environment settings.py In this way, there is no warning, but there is no need, because we are not the production environment:

Registration:

Message received successfully:

The performer also prints information:

be careful:

1) Cellery doesn't work independently. It's just a tool of Django. It needs Django environment to work;

2) How to deploy the daily fresh project (task sender) and executor on different machines: copy the complete project (dailyfresh directory) to the executor's machine, start the executor, the sender does not need to initialize the Django environment variable, and the initiator must initialize it.

 

19. User login

login.html Change static file path (same as register.html ):

Test:

Modify form:

If action is not specified, it is submitted to the address of the current address column, and we just want it to be submitted to this address. Like register, it uses the same address for display and processing, so action is not specified;

Define view:

# user/views.py Log in view add post method

# user/login
class LoginView(View):
    '''Sign in'''
    def get(self, request):
        '''Show login page'''
        return render(request, 'login.html')

    def post(self, request):
        username = request.POST.get('username')
        password = request.POST.get('pwd')
        if not all([username, password]):
            return render(request, 'login.html', {'errmsg': 'Incomplete data'})

        # Django's built-in authentication system verifies the user name and password (automatically encrypts the password and then queries it)
        user = authenticate(username=username, password=password)
        print(user)
        if user is not None:
            if user.is_active:
                login(request, user)  # Django's built-in authentication system records the user's login status to the session
                return redirect(reverse('goods:index'))
            else:
                return render(request, 'login.html', {'errmsg': 'User not activated'})
        else:
            return render(request, 'login.html', {'errmsg': 'Wrong user name and password'})

Using authenticate() will automatically detect is_active field, even if found through username and password, if is_active=0 will also return None, and cancel detection:

# settings.py Add

# Is will not be detected when using Django built-in authentication system_ Active field
AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']

Find a suitable location to output errmsg:

 

19. Set to use cache to store session, and set to use redis as cache

Installation:

pip3 install django-redis  # Installing Django redis will automatically install redis 3.5.0
pip3 install redis==2.10.6  # redis2.10.6 is needed to build the cluster and then install it back

to configure:

# settings.py Middle append

# Django cache setting. The default memory of this machine is cache, which can be set to redis
CACHES = {
    'default':{
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379 / 9 ', address of redis service routine
        'OPTION': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient'
        }
    }
}

# Django session setting, database by default, can be set as cache or redis
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'

Start the redis service:

Enter the client, now there is no key:

Log in and look again:

 

20. Remember user name

# user/views.py Modify get and post methods of login view

# user/login
class LoginView(View):
    '''Sign in'''
    def get(self, request):
        '''Show login page'''
        # Judge whether the user name is remembered
        if 'username' in request.COOKIES:  
            username = request.COOKIES.get('username')
            checked = 'checked'
        else:
            username = ''
            checked = ''
        return render(request, 'login.html', {'username': username, 'checked': checked})

    def post(self, request):
        username = request.POST.get('username')
        password = request.POST.get('pwd')
        if not all([username, password]):
            return render(request, 'login.html', {'errmsg': 'Incomplete data'})

        user = authenticate(username=username, password=password)
        print(user)
        if user is not None:
            if user.is_active:
                login(request, user)  # Django's built-in authentication system records the user's login status to the session
                response = redirect(reverse('goods:index'))

                # If checked, the user name will be saved to the cookie. If unchecked, the user name in the cookie will be cleared
                remember = request.POST.get('remember')
                if remember == 'on':
                    response.set_cookie('username', username, max_age=7*24*3600)  # cookie expires one week
                else:
                    response.delete_cookie('username')
                return response
            else:
                return render(request, 'login.html', {'errmsg': 'User not activated'})
        else:
            return render(request, 'login.html', {'errmsg': 'Wrong user name and password'})

Parameters used in template:

effect:

 

 

 

15. Extract parent template:
hold index.html Copy to templates, rename to base.html ;
Each page has some parts, left;
Some pages have, some do not have the part, writes in the block when the default value, then does not have the direct deletion;
Each page has different parts, delete them and leave a block;
{homepage, registration, login}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
{% load staticfiles %}
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
Page title block
    <title>{% block title %}{% endblock title %}</title>
{css {used for every page}
    <link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}">
    <link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}">
{insert file block at the top of web page}
    {% block topfiles %}{% endblock topfiles %}
</head>
<body>
{ා welcome information block at the top of the web page, some pages have, some have not, so there is a default value. If not, directly clear ා}
{% block header_con %}
    <div class="header_con">...</div>
{% endblock header_con %}

{search box at the top of web page {}
{% block search_bar %}
    <div class="search_bar clearfix">
        <a href="index.html" class="logo fl"><img src="images/logo.png"></a>
        <div class="search_con fl">
            <input type="text" class="input_ Text FL "name =" "placeholder =" search product ">
            <input type="button" class="input_ BTN fr "name =" "value =" search ">
        </div>
        <div class="guest_cart fr">
            <a href="#" class="cart_ Name FL "> my shopping cart</a>
            <div class="goods_count fl" id="show_count">1</div>
        </div>
    </div>
{% endblock search_bar %}

{web page main content block {}
{% block body %}{% endblock body %}

<div class="footer">....</div>

{html element block at the bottom of web page {}
{% block bottom %}{% endblock bottom %}
{insert file block at the bottom of web page}
{% block bottomfiles %}{% endblock bottomfiles %}
</body>
</html>

Posted by sparshdharam on Thu, 11 Jun 2020 22:41:21 -0700