Django项目基本流程

环境相关

1.python包

  • Django3
  • python3
  • pyCharm
  • 虚拟环境

2.虚拟环境

virtualenvwrapper【用来管理virtualenv的扩展包,方便env虚拟环境管理。】

  • 基本操作

    # 安装
    pip install virtualenvwrapper
    # 创建虚拟环境
    mkvirtualenv [env name]
    # 创建指定解释器的虚拟环境
    mkvirtualenv -p [python version] [env name]
    # 启动虚拟环境
    workon [env name]
    # 退出虚拟环境
    deactivate
    # 删除虚拟环境
    rmvirtualenv [env name]
    # 虚拟环境存储路径更改
    https://blog.csdn.net/a200822146085/article/details/89048172
    

3.远程仓库

  • 建立远程仓库

    Github

    Gitee

    码云

  • 基本操作

    # 克隆项目
    git clone  https://github.com/xxxx.git
    
  • 创建Django工程

    django-admin startproject [project name]
    
  • 启动服务

    **python manage.py runserver**
    python manage.py runserver 0.0.0.0:8000     默认本地8000端口
    python manager.py runserver --host 0.0.0.0 --port 9008
    

启动服务

1.基本操作

  • 命令

    # 立项
    django-admin startproject [project name]
    # 添加子应用
    python manage.py startapp [appname]
    # 启动服务
    **python manage.py runserver**
    python manage.py runserver 0.0.0.0:8000     默认本地8000端口
    python manager.py runserver --host 0.0.0.0 --port 9008
    

配置相关

1.settings目录

  • 创建settings包

  • 复制原settings.py 为 base.py,用于不同环境且相同配置的继承

  • 新建dev.py 和pro.py 两个文件至settings包中,分别对应测试和生产环境的配置文件

  • dev.py

    from .base import *	# NOQA
    # NOQA用来指示PEP8规范检测工具,此处无需检测;也可以在无需检测的文件第一行增加 # flake8: NOQA
    DEBUG = True
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': BASE_DIR / 'db.sqlite3',
        }
    }
    
  • manage.py

    # 修改环境配置代码,加载不同的环境变量,以此区分生产和测试环境
    ...
    profile = os.environ.get('TYPEIDEA_PROFILE', 'develop')
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'typeidea.settings.%s' % profile)
    ...
    

2.Jinja2模板

  • 目录utils中新建jinja2_evn.py

  • jinja2_env.py

    from jinja2 import Environment
    from django.contrib.staticfiles.storage import staticfiles_storage
    from django.urls import reverse
    
    def environment(**options):
        env = Environment(**options)
        env.globals.update({
            'static': staticfiles_storage.url,
            'url': reverse,
        })
        return env
    
  • 进入settings/dev中的TEMPLATES列表,添加Jinja2模板环境配置字典,添加在Django自带模板的上方

  • settings/dev.py

    # Jinja2 模板
        {
            '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',
                ],
                # 补充Jinja2模板引擎环境
                'environment': 'utils.jinja2_env.environment',
            },
        },
    

3.Redis

  • settings/dev.py

    # Redis配置
    CACHES = {
        # 默认为0号数据库
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/0",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        },
        # session为1号数据库
        "session": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/1",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        },
    }
    SESSION_ENGINE = "django.contrib.sessions.backends.cache"
    SESSION_CACHE_ALIAS = "session"
    # 配置session存储3种方式
    # 1.存储在数据库中,如下设置可写可不写,是默认存储模式
    # SESSION_ENGINE = "django.contrib.sessions.backends.db"
    # 2.存储在缓存中,存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快
    # SESSION_ENGINE = "django.contrib.sessions.backends.cache"
    # 3.混合存储:优先从本机内存中存取,如果没有则冲数据库中存取
    
    

4.Mysql

  • 依赖pymysql 包

  • 数据库准备

    # 建库
    create database [database name] charset=utf8 ;   (djangomall
    # 新建用户
    create user [user name] identified by [password] ;  (u:xuanRui  p:djangomall
    # 为用户分配数据库权限
    grant all on [database name].* to '[user name]'@'%' ;
    # 刷新权限
    flush privileges ;
    # 清除表中数据
    delete from 表名;
    truncate table 表名;
    不带where参数的delete语句可以删除mysql表中所有内容,使用truncate table也可以清空mysql表中所有内容。
    效率上truncate比delete快,但truncate删除后不记录mysql日志,不可以恢复数据。
    delete的效果有点像将mysql表中所有记录一条一条删除到删完,
    而truncate相当于保留mysql表的结构,重新创建了这个表,所有的状态都相当于新表。
    
  • settings\dev.py

    # MySql 配置
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '127.0.0.1',  # 数据库主机
            'PORT': 3306,  # 数据库端口
            'USER': 'root',  # 数据库用户名
            'PASSWORD': 'xuanRui',  # 数据库用户密码
            'NAME': 'django_demo'  # 数据库名字
        }
    }
    
  • 项目同名目录__init__.py

    import  pymysql
    pymysql.version_info = (1, 4, 13, "final", 0)
    pymysql.install_as_MySQLdb()
    
  • 否则报错

    raise ImproperlyConfigured('mysqlclient 1.4.0 or newer is required; you have %s.' % Database.__version__)
    django.core.exceptions.ImproperlyConfigured: mysqlclient 1.4.0 or newer is required; you have 0.10.0.
    

5.app注册

  • app.py

    """
    注册 User 模型类 替换系统自带的User模块
    # AUTH_USER_MODEL = '子应用的名称.模型类名字'
    """
    AUTH_USER_MODEL = 'users.User'
    
  • srttings/base.py

    # 注册子应用
    ...
    INSTALLED_APPS = [
    	'appname',
        
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    ]
    ...
    
  • 注意,当新建apps作为应用集合时,需要更改每个app中的apps文件

    someapp.apps.py(python manage.py startapp someapp 默认生成的app包中)

    class PollsConfig(AppConfig):
        default_auto_field = 'django.db.models.BigAutoField'
        name = 'polls
       
    # 修改为:
    class PollsConfig(AppConfig):
        default_auto_field = 'django.db.models.BigAutoField'
        name = 'apps.polls'
    

6.static静态文件

  • 新建static文件夹(前端材料)

  • settings/dev.py中配置

    STATIC_URL = '/static/'
    STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')]
    

7.log日志模块

  • 新建logs文件夹

  • settings/dev.py

    # 日志文件配置
    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.utils.log.RequireDebugTrue',
            },
        },
        'handlers': {
            # 终端输出
            'console': {
                'level': 'DEBUG',
                'filters': ['require_debug_true'],
                'class': 'logging.StreamHandler',
                'formatter': 'simple'
            },
            # 文件输出
            'file': {
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',
                'filename': os.path.join(BASE_DIR, "logs/.log"),  # 日志文件的位置
                'maxBytes': 300 * 1024 * 1024,
                'backupCount': 10,
                'formatter': 'verbose'
            },
        },
        # 日志器
        'loggers': {
            'django': {  # 定义了一个名为django的日志器
                'handlers': ['console', 'file'],
                'propagate': True,  # 追加模式
                'level': 'INFO',  # 日志器接收的最低日志级别
            },
        }
    }
    # 实例化 单例模式
    import logging
    LOGGER = logging.getLogger("django")
    
    # 配置完成后,启动服务,logs文件夹中会出现日志文件
    

8.urls路由

  • url path() re_path() include()

    url是Django 1.x中的写法,在Django2.1中,开始舍弃Django1.x中的url写法。在Django2.x中,描写url配置的有两个函数path和re_path,re_path()函数可以看做是django 1.x中得url函数,即可以在路径中使用正则。

  • path()函数内置转化器

    str:匹配任何非空字符串,但不含斜杠/,如果你没有专门指定转换器,那么这个是默认使用的
    int:匹配0和正整数,返回一个int类型
    slug:可理解为注释。该转换器匹配任何ASCII字符以及连接符和下划线,比如'building-your-1st-django-site'
    uuid:匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如'075194d3-6885-417e-a8a8-6c931e272f00'返回一个UUID对象;
    path:匹配任何非空字符串,重点是可以包含路径分隔符'/'。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串。
    
  • inclue() 三种用法

    1.在任何时候,urlpatterns都可以“include”其他URLconf模块。这本质上是一组位于其他url之下的“roots”。

    from django.urls import include, path
    
    urlpatterns = [
        path('community/', include('aggregator.urls')),
        path('contact/', include('contact.urls')),
    ]
    # 每当Django遇到时include(),它都会截断直到该处匹配的URL的任何部分,并将剩余的字符串发送到包含的URLconf中以进行进一步处理
    

    2.另一种可能性是通过使用path()实例列表包括其他URL模块 。例如:

    from django.urls import include, path
    
    from apps.main import views as main_views
    from credit import views as credit_views
    
    extra_patterns = [
        path('reports/', credit_views.report),
        path('reports//', credit_views.report),
        path('charge/', credit_views.charge),
    ]
    
    urlpatterns = [
        path('', main_views.homepage),
        path('help/', include('apps.help.urls')),
        path('credit/', include(extra_patterns)),
    ]
    # /credit/reports/将由credit_views.report()视图处理 
    

    3 .可用于从URLconf中删除重复使用单个模式前缀的冗余。

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('-/history/', views.history),
        path('-/edit/', views.edit),
        path('-/discuss/', views.discuss),
        path('-/permissions/', views.permissions),
    ]
    

    可通过仅说明一次公共路径前缀并对不同的后缀进行分组:

    from django.urls import include, path
    from . import views
    
    urlpatterns = [
        path('-/', include([
            path('history/', views.history),
            path('edit/', views.edit),
            path('discuss/', views.discuss),
            path('permissions/', views.permissions),
        ])),
    ]
    # 详见:https://blog.csdn.net/weixin_44870139/article/details/105565242
    
  • 路由的反向解析

    • Django1.x 和 Django2.x 存在一定区别
    • Django2.x
1. # 主路由urls
    path('', include(('apps.contents.urls','contents'), namespace="contents")),
	                      子路由         子路由名字(一般保持与主路由相同) 主路由名字
# 子路由urls
	path('index/',views.index_view(),name='index')
# 视图函数实现反向解析:
	...
    return redirect(reverse('contents:index'))
						 主路由名    子路由名
    
2. ### 建议使用如下方法:
    - 子路由urls文件中路由列表前添加 app_name = "index"
    - 需要反向解析的子路由入口后添加name参数 
     如下:
app_name = 'index'
urlpatterns = [
    path('', views.IndexView.as_view(),name='index_page'),
]
return redirect(reverse('index:index_page'))  # 即可实现反向解析

9.语言和时区

LANGUAGE_CODE = 'zh-Hans'

TIME_ZONE = 'Asia/Shanghai'

模型层

1.model类

  • 模型类迁移

    python manage.py makemigrations
    python manage.py migrate
    

2.自定义用户模型类

  • 实现django之外的自定义字段

  • apps.users.model.py

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    class User(AbstractUser):
        # django自带 username password (加解密)
        # 添加字段 手机号 mobile
        mobile = models.CharField(max_length=11, unique=True, verbose_name="手机号")
        class Meta:
            # 表名称
            db_table = 'tb_uers'
            verbose_name = "用户"
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.username
    
  • settings\base.py

    """
    注册 User 模型类 替换系统自带的User模块
    # AUTH_USER_MODEL = '子应用的名称.模型类名字'
    """
    AUTH_USER_MODEL = 'users.User'
    

3.自定义用户认证类

  • 实现多账号用户登录

    • users.utils.py

      """
      自定义后端多账号认证
      重写类方法
      """
      import re
      from django.contrib.auth.backends import ModelBackend
      from .models import User
      def User_get_by_account(account):
          try:
              # 判断用户名类型 手机号
              if re.match('^1[345789]\d{9}$', account):
                  user = User.objects.get(mobile=account)
              else:
                  user = User.objects.get(username=account)
          except:
              return None
          else:
              return user
      class UsernameMobileAuthBackend(ModelBackend):
          def authenticate(self, request, username=None, password=None, **kwargs):
              user = User_get_by_account(username)
              # 校验密码
              if user and user.check_password(password):
                  return user
      

4.自定义模型基类

  • 实现为多个模型类添加通用字段

  • utils.model.py

    from django.db import models
    
    class BaseModel(models.Model):
        """ 为模型类补充字段 """
    
        create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
        update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
    
        class Meta:
            """ 表明是抽象模型类,用于继承使用,数据迁移时不会创建BaseModel表 """
            abstract = True
    

5.ORM的N+1问题

  • N+1

    一般情况下由外键查询产生的N+1问题比较多,即一条查询请求返回N条数据,当操作数据时,又会产生额外的请求,这就是N+1问题,所有的ORM都存在这样的问题。

  • Django的解决方案

    select_related

    # 文章表
    posts = Post.objects.all()
    for post in posts: # 产生数据库查询
        print(post.owner) # 产生额外的数据库查询
        
    # 作者表(关联表)
    posts = Post.objects.all().select_related('category')
    for post in posts: # 产生数据库查询,category数据也会一次性查询出来
        print(post.category)
        
    # 当然 `select_related` 这个接口只能用来解决一对多的关联关系,对于多对多的关联关系,还需要使用 `prefetch_related` 接口
    
    posts = Post.objects.all().prefetch_related('tag')
    for post in posts:  # 产生两条查询语句,分别查询post和tag
        print(post.tag.all())
    

DRF

1.序列化

  • 基本操作

    # serializers.Serializer类
    class UsersSerializer(serializers.Serializer):
        """序列化器"""
        name = serializers.CharField()
        age = serializers.IntegerField()
        height = serializers.DecimalField(max_digits=5)
        sex = serializers.IntegerField()
        # 自定义序列化的字段,序列化的属性值有方法字段来提供
        gender = serializers.SerializerMethodField()
        def get_gender(self, obj):
            return obj.get_sex_display()
    
    class UsersDeserializer(serializers.Serializer):
        """反序列化器"""
        # 序列化属性名不是必须与model属性名对应,但是与之对应会方便序列化将校验通过的数据与数据库进行交互
        name = serializers.CharField(min_length=3, max_length=64, error_messages={
            'required': '姓名必填',
            'min_length': '太短',
        })
        age = serializers.IntegerField(min_value=0, max_value=150, required=False)
        # 自定义反序列化字段:一定参与校验,且要在校验过程中,将其从入库的数据中取出,剩余与model对应的数据才会入库
        verify_name = serializers.CharField(min_length=3, max_length=64)
        height = serializers.DecimalField(max_digits=5, decimal_places=2, default=0)
        sex = serializers.IntegerField(default=0)
    
        # 自定义校验规则:局部钩子、全局钩子
        def validat_name(self, value):
            """局部钩子"""
            if value == "xuanRui":
                raise serializers.ValidationError("该用户已经存在")
            return value
    
        def validate(self, attrs):
            """全局钩子"""
            name = attrs.get("name")
            verify_name = attrs.pop("verify_name")
            if name != verify_name:
                raise serializers.ValidationError({'verify_name': "验证名字错误"})
            return attrs
    
        def create(self, validated_data):
            return Users.objects.create(**validated_data)
    

部署相关

1.UWSGI

  • manage.py 同级下创建uwsgi.ini文件

  • 配置如下

    [uwsgi]
    # 使用Nginx连接时使用,Django程序所在服务器地址
    #socket=172.16.21.25:8001
    # 直接做web服务器使用,Django程序所在服务器地址
    http=192.168.229.148:8001
    # 项目目录  目录是到base_dir
    chdir=/home/python/Desktop/27/meiduo_admin_27/meiduo_mall
    # 项目中wsgi.py文件的目录,相对于项目目录
    wsgi-file=meiduo_mall/wsgi.py
    # 进程数
    processes=4
    # 线程数
    threads=2
    # uwsgi服务器的角色
    master=True
    # 存放进程编号的文件
    pidfile=uwsgi.pid
    # 日志文件
    daemonize=uwsgi.log
    # 指定依赖的虚拟环境 ,到虚拟环境名即可
    virtualenv=/home/python/.virtualenvs/py3_django_44
    
  • uwsgi命令

    # 启动
    uwsgi --ini uwsgi.ini
    # 停止
    uwsgi --stop uwsgi.pid
    

2.Nginx

  • nginx命令

    # 开启和关闭nginx服务1
    systemctl start nginx 
    systemctl stop nginx
    # 开启和关闭nginx服务2
    /etc/init.d/nginx start|stop|reload
    # 开启和关闭nginx服务3
    /usr/sbin/nginx
    # 配置参数
    nginx -V
    # 版本信息
    nginx -v
    # 停止nginx
    nginx -s stop
    # 检查默认配置文件
    nginx -t
    # 检查指定配置文件
    nginx -t -c file.conf
    

你可能感兴趣的:(Django,django,python,pycharm)