项目背景

这几天一直琢磨着如何才能培养自己好的代码习惯和读源码能力 或者说是我大学以来一直担心的

但是感觉这件事如果不自己写一个’工程项目’是永远都不会懂的。

遂找了个以前毕业必会的项目练练手项目视频教程

项目配置

环境配置

先在windows下创建虚拟环境

# 没装包pip install 一下
mkvirtualenv django_project
workon django_project

主要是django+redis+mysql+jinja2的框架进行前后端不分离开发,环境如下

Django 3.2
django-redis 4.12.1
Jinja2 2.11.3
pip 20.1.1
PyMySQL 1.0.2
redis 3.5.3

github配置

这个很简单,github建库,clone一下就完事了

目前为止的tree结构

├─.idea
└─meiduomall

├─readme
└─.gitignore

因为是django项目在meiduomall里面执行 就有了基本框架

# 如果没有必要包先安装必要包
# pip install django
# pip install redis
# pip install pymysql
# pip install jinja2
# 安装请忽略
django-admin startproject meiduomall

开发环境配置

这里涉及许多就不一一细说了,都是在setting文件里修改。

不过首先为了测试等方便,将原本的setting.py删除创建新的setting包,再在里面创建dev.py作为测试setting

#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
    # 注意这里根据自己的文件夹名字不同设置不同
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "meiduomall.settings.dev")
    try:
        from django.core.management import execute_from_command_line
    except ImportError:
        # The above import may fail for some other reason. Ensure that the
        # issue is really that Django is missing to avoid masking other
        # exceptions on Python 2.
        try:
            import django
        except ImportError:
            raise ImportError(
                "Couldn't import Django. Are you sure it's installed and "
                "available on your PYTHONPATH environment variable? Did you "
                "forget to activate a virtual environment?"
            )
        raise
    execute_from_command_line(sys.argv)

注意使用jinja2的框架要设置环境(在template那),新建utils包,在里面创建jinja2_env.py

# -*- coding:utf-8 -*-
# @Author : Dummerfu
# @Contact : https://github.com/dummerchen 
# @Time : 2021/4/20 18:16

from jinja2 import Environment
from django.urls import reverse
from django.contrib.staticfiles.storage import staticfiles_storage

def jinja2_env(**options):
    env=Environment(**options)
    env.globals.update({
        'static':staticfiles_storage.url, # 获取静态文件前缀
        'url':reverse, # 反向解析路由
    })
    return env

之后就是在setting中修改了,我主要改了 databasestemplates,增加了redisloggingstatic_url

别忘记建个mysql数据库

"""
Django settings for meiduomall project.

Generated by 'django-admin startproject' using Django 1.11.11.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'r3jt$+_uq90)99sr-p#@u%=$y43cwh18%2sdac2(uh!w^oj@qg'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'meiduomall.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [os.path.join(BASE_DIR,'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'environment':'meiduomall.utils.jinja2_env.jinja2_env',
        },
    },
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    }
]
# TEMPLATES = [
#     {
#         'BACKEND': 'django.template.backends.django.DjangoTemplates',
#         'DIRS': [],
#         'APP_DIRS': True,
#         'OPTIONS': {
#             'context_processors': [
#                 'django.template.context_processors.debug',
#                 'django.template.context_processors.request',
#                 'django.contrib.auth.context_processors.auth',
#                 'django.contrib.messages.context_processors.messages',
#             ],
#         },
#     },
# ]

WSGI_APPLICATION = 'meiduomall.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST':'127.0.0.1',
        'PORT':3306,
        'USER':'mall_admin',
        'PASSWORD':'20010720',
        'NAME':'mall',
    }
}

# 配置redis
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    "session": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "session"

# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]
# loggers 配置

LOGGING={
    'version': 1,
    'disable_existing_loggers': False,
    'formatters':{
        'verbose':{
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s',
        },
        'simple':{
            'format':'%(levelname)s %(module)s %(lineno)d %(message)s'
        },

    },
    'filters':{
        'require_debug_true':{# django 在debug模式下输出日志
            '()': 'django.utils.log.RequireDebugTrue',
        }
    },
    'handlers':{ # 日志处理方法
        'console':{
            'level':'INFO',
            'filters':['require_debug_true'],
            'class':'logging.StreamHandler',
            'formatter':'simple',
        },
        'file':{
            'level':'INFO',
            'class':'logging.handlers.RotatingFileHandler',
            'filename':os.path.join(BASE_DIR,'logs/mall.log'),
            'maxBytes': 300*1024*1024,
            'backupCount': 10,
            'formatter':'verbose',
        },
    },
    'loggers':{
        'django':{ # 定义日志器名字
            'handlers':['console','file'],
            'propagate':True, # 是否继续传递日志信息
            'level':'INFO',
        }
    },
}

加上templates,static,logs文件夹的tree如下(当然可以把logs文件夹放在和meiduomall同级目录)

├─.idea
└─meiduomall
└─meiduomall
├─logs
├─settings
├─static
├─templates
├─utils

用户注册

okk,以上我们已经完成了项目配置,现在可以开始构建app了

apps

为了方便管理,我们先在meiduomall目录下创建一个python package apps,然后再在里面创建项目子应用。

python ../../manage.py startapp users

然后在setting中注册子应用每次创建都有注册,后续就不再赘述了

# 用户子应用注册,如果不知道路径可以通过sys.path查看
# 当然也可以sys.path.insert()插入新的导包路径
# 这样就可以使用'users',
# 不加插入,直接用meiduomall.apps.users会说找不到'users'就很奇怪
sys.path.insert(0,os.path.join(BASE_DIR,'apps'))
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'users',

]

注册页面

因为我主要还是从事后端所以所有html都默认已经写好了,直接用(现在templates里面已经有register.html)

在view.py中创建register视图类

视图类可以理解为在当前页面发送请求所触发的相应的响应。后面在详细修改

from django.shortcuts import render
from django.views import View
# Create your views here.
class Register_View(View):
    '''
        用户注册
    '''
    def get(self,request):
        return render(request,'register.html')

设置路由

这里要闲话一下

urlpattern的匹配顺序

  • 在根路由中顺序匹配,匹配成功则进入子应用路由匹配(可以多个匹配成功

  • 子路由中匹配剩余的string

  • 如果根路由正则为r’^register/‘

    子路由中正则为r’^register/$’

    输入路径为 xxxx:xxx/register/ ,会nofound

    提示也有先匹配register/ 匹配成功,然后剩余字符串为’’,匹配子路由失败

    这样是xxx:xx/register/register/才会匹配成功

"""meiduomall URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""

# 总路由
from django.conf.urls import url,include
from django.contrib import admin

# 根路由中不能有严格的结束$,这样子路由就没有意义了
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^',include('users.urls')),
]
# 在users下创建urls.py文件
# 里面内容如下

from django.conf.urls import url,include
from django.contrib import admin
from . import views
urlpatterns = [
    url(r'^$',views.Register_View.as_view()),
]

现在打开即可看到注册页面

image-20210421130438609

实现数据存储数据库

创建user模型类完成注册登录并存储账号等信息到数据库,因为django已经有内置的用户类所以直接继承即可。

from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):

    mobile=models.CharField(max_length=11,unique=True,verbose_name='phone number')

    class Meta:
        # 表名规范 db_name
        db_table='db_user'
        verbose_name='user_name'
        verbose_name_plural=verbose_name

一般get是向服务器发送请求,post是向数据库发送请求

验证信息

信息填写后还需要验证这一步骤,包括前端验证和后端验证,步骤是先前端初步判断信息十分符合,再使用axios发送get|post请求,然后后端向数据库得到数据检验信息是否符合规范。

可以建立一个verification子应用然后再里面修改。(添加子应用和前面步骤都一样

具体检验信息包括:

  • 用户名检验(前端检验是否合法,后端检验是否重复
  • 密码检验(前端后端检验两次密码是否相同
  • 手机号检验(前端检验是否合法,后端检验是否重复
  • 验证码检验(后端检验是否与用户输入相同
  • 短信检验(后端检验是否符合
  • 同意条款检验(前后端检验是否勾选

有条件最后都前后端都检验一下,以防恶意分子。

# View 代码
# 检验用户名
class Username_View(View):
    def get(self,request,username):

        count=User.objects.filter(username=username).count()

        return http.JsonResponse({'count':count,'errormsg':'ok','code':0})
# 检验手机号
class Mobile_View(View):
    def get(self,request,mobile):

        count=User.objects.filter(mobile=mobile).count()

        return http.JsonResponse({'count':count,'errormsg':'ok','code':0})
// 检验手机号为例
check_phone: function (){
            var re = /^1[345789]\d{9}$/;
            if(re.test(this.mobile)) {
                this.error_phone = false;
            } else {
                console.log("???")
                this.error_phone_message = '您输入的手机号格式不正确';
                this.error_phone = true;
            }
            if (this.error_phone == false) {
                axios.get('http://127.0.0.1:8000/mobiles/'+ this.mobile + '/count/', {
                        responseType: 'json'
                    })
                    .then(response => {
                        if (response.data.count > 0) {
                            this.error_phone_message = '手机号已存在,请尝试登录';
                            this.error_phone = true;
                        } else {
                            this.error_phone = false;
                        }
                    })
                    .catch(error => {
                        console.log(error.response.data.code);
                    })
            }
        },

注意验证码是存放在redis中的,项目开始不要忘记开启redis和mysql

也可以再增加一个verify_code redis库,如果你的redis有密码还要配置密码

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://:20010720@127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD":"XXXX",
        }
    },
    "session": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD":'XXXX',
        }
    },
    "verify_code": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/3",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD":'XXXX',
        }
    }
}

下面讲一讲验证码检验部分思路,其实很简单 短信验证用不起

  • 定义view
  • 定义url路由
  • 前端访问对应路由
  • view接受get|post请求
  • view类里查询redis,check用户与后端数据库存储是否相同

后记

你看到后记可能会问,唉?这就后记了?就这?没错

因为写到这里用户注册部分就已经差不多了,之后只是对注册页面的各种优化(虽然不知为什么我的axios的ajax请求会刷新页面)但算是把一个板块写完了吧,所以先告一段落。

由于5.1校赛选择了烟盒的目标识别,发现自己好多网络结构都不懂,trick也是现场yy,开始说好的k折也没实现菜鸡全靠大佬带飞

当务之急是恶补ML基础所以接下来可能按这方面走了:

Mobilenet学习和代码复现

Effient Net学习和代码复现

To be contiune…