• 项目相关 --知识点


    前后端不分离

    ***在图片上写代码,发送到前台,进行验证码 校验
    from
    PIL import Image,ImageDraw,ImageFont """ Image 生成图片的 ImageDraw 在图片上写东西的 ImageFont 控制字体样式的 """ from io import BytesIO,StringIO """ io是一个内存管理器模块 BytesIO 能够帮你存储数据 二级制格式 StringIO 能够帮你存储数据 字符串格式 """ import random def get_random(): return random.randint(0,255),random.randint(0,255),random.randint(0,255) def get_code(value): # 在产生的图片上 写验证码 img_obj = Image.new('RGB', (360,35), get_random()) img_draw = ImageDraw.Draw(img_obj) # 生成一个可以在图片上写字的画笔对象 img_font = ImageFont.truetype('static/font/222.ttf',30) # 字体样式 及 大小 io_obj = BytesIO() # 图片验证码 一般都是有 数字 大写字母 小写字母 # 五位 每一位都可以 数字或大写字母或小写字母 code = '' for i in range(5): upper_str = chr(random.randint(65,90)) low_str = chr(random.randint(97,122)) random_int = str(random.randint(0,9)) # 随机取一个 temp_code = random.choice([upper_str,low_str,random_int]) # 朝图片上写 img_draw.text((70+i*45,0),temp_code,get_random(),font=img_font) code += temp_code print(code) # 将产生的随机验证码 存入session中 以便后续其他视图函数获取 校验验证码 request.session['code'] = code img_obj.save(io_obj,'png') return HttpResponse(io_obj.getvalue())
    models.py
    models.py
    
    from django.db import models
    from django.contrib.auth.models import AbstractUser
    # Create your models here.
    class UserInfo(AbstractUser):
        phone = models.BigIntegerField(null=True,blank=True)  # blank是用来告诉admin后台 该字段可以不填
        # avatar存的是用户头像文件路径 用户上传的头像会自动保存到avatar文件夹下
        avatar = models.FileField(upload_to='avatar/',default='avatar/default.jpg')
        create_time = models.DateField(auto_now_add=True)
    
        blog = models.OneToOneField(to='Blog',null=True)
    
        class Meta:
            verbose_name_plural = '用户表'
            # verbose_name =  '用户表'
    
        def __str__(self):
            return self.username
    
    
    class Blog(models.Model):
        site_title = models.CharField(max_length=32)
        site_name = models.CharField(max_length=32)
        site_theme = models.CharField(max_length=255)
    
    
        def __str__(self):
            return self.site_name
    
    class Category(models.Model):
        name = models.CharField(max_length=32)
        blog = models.ForeignKey(to='Blog')
        def __str__(self):
            return self.name
    
    class Tag(models.Model):
        name = models.CharField(max_length=32)
        blog = models.ForeignKey(to='Blog')
        def __str__(self):
            return self.name
    
    
    class Article(models.Model):
        title = models.CharField(max_length=255)
        desc = models.CharField(max_length=255)
        content = models.TextField()  # 存大段文本
        create_time = models.DateField(auto_now_add=True)
    
        # 数据库优化字段
        comment_num = models.IntegerField(default=0)
        up_num = models.IntegerField(default=0)
        down_num = models.IntegerField(default=0)
    
        # 外键字段
        blog = models.ForeignKey(to='Blog',null=True)
        category = models.ForeignKey(to='Category',null=True)
        tag = models.ManyToManyField(to='Tag',through='Article2Tag',through_fields=('article','tag'))
    
        def __str__(self):
            return self.title
    
    class Article2Tag(models.Model):
        article = models.ForeignKey(to='Article')
        tag = models.ForeignKey(to='Tag')
    
    
    
    class UpAndDown(models.Model):
        user = models.ForeignKey(to='UserInfo')
        article = models.ForeignKey(to='Article')
        is_up = models.BooleanField()  # 传布尔值  存0/1
    
    
    class Comment(models.Model):
        user = models.ForeignKey(to='UserInfo')
        article = models.ForeignKey(to='Article')
        content = models.CharField(max_length=255)
        create_time = models.DateField(auto_now_add=True)
        parent = models.ForeignKey(to='self',null=True)

    from django import forms
    from django.forms import widgets
    from app01 import models
    
    class MyRegForm(forms.Form):
        username = forms.CharField(max_length=8, min_length=3, label='用户名',
                                   error_messages={
                                       'max_length': '用户名最大八位',
                                       'min_length': '用户名最小三位',
                                       'required': '用户名不能为空'
                                   }, widget=widgets.TextInput(attrs={'class': 'form-control'})
                                   )
        password = forms.CharField(max_length=8, min_length=3, label='密码',
                                   error_messages={
                                       'max_length': '密码最大八位',
                                       'min_length': '密码最小三位',
                                       'required': '密码不能为空'
                                   }, widget=widgets.PasswordInput(attrs={'class': 'form-control'})
                                   )
        confirm_password = forms.CharField(max_length=8, min_length=3, label='确认密码',
                                           error_messages={
                                               'max_length': '确认密码最大八位',
                                               'min_length': '确认密码最小三位',
                                               'required': '确认密码不能为空'
                                           }, widget=widgets.PasswordInput(attrs={'class': 'form-control'})
                                           )
        email = forms.EmailField(label='邮箱', error_messages={
            'required': "邮箱不能为空",
            'invalid': "邮箱格式错误"
        }, widget=widgets.EmailInput(attrs={'class': 'form-control'}))
    
        # 局部钩子 校验用户名是否已存在
        def clean_username(self):
            username = self.cleaned_data.get('username')
            is_user = models.UserInfo.objects.filter(username=username)
            if is_user:
                self.add_error('username', '用户名已存在')
            return username
    
        # 全局钩子 校验密码是否一致
        def clean(self):
            password = self.cleaned_data.get('password')
            confirm_password = self.cleaned_data.get('confirm_password')
            if not password == confirm_password:
                self.add_error('confirm_password', '两次密码不一致')
            return self.cleaned_data
    自定义forms 组件
    from django.contrib import admin
    from app01 import models
    admin.site.register(models.UserInfo)
    admin.site.register(models.Blog)
    admin.site.register(models.Tag)
    admin.site.register(models.Category)
    admin.site.register(models.Article2Tag)
    admin.site.register(models.Article)
    admin.site.register(models.UpAndDown)
    admin.site.register(models.Comment)
    admin注册
    """BBS 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
    from django.contrib import admin
    from app01 import views
    from django.views.static import serve
    from BBS import settings
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
     
        url(r'^register/', views.register),
        url(r'^login/', views.login),
    
        # 图片验证码相关路由
        url(r'^get_code/',views.get_code),
    
        # 主页
        url(r'^home/',views.home),
        # 注销
        url(r'^logout/',views.logout),
        # 修改密码
        url(r'^set_password/',views.set_password),
    
        # 手动暴露后端文件夹资源
        url(r'^media/(?P<path>.*)',serve,{"document_root":settings.MEDIA_ROOT}),
        # 手动暴露后端文件资源的时候 一定要慎重
        # url(r'^app01/(?P<path>.*)',serve,{"document_root":settings.MEDIA_ROOT1})
        # 文章点赞点踩功能
        url(r'^up_or_down/',views.up_or_down),
        # 文章评论功能
        url(r'^comment/',views.comment),
        # 后台管理
        url(r'^backend/',views.backend),
        # 后台添加文章
        url(r'^add_article/',views.add_article),
        # 文本编辑器上传的图片功能
        url(r'^upload_img/',views.upload_img),
        # 修改用户头像
        url(r'^edit_avatar/',views.edit_avatar),
    
        # 个人站点
        url(r'^(?P<username>w+)/$',views.site),
        # 侧边栏筛选功能
        # url(r'^(?P<username>w+)/category/(?P<param>d+)/',views.site),
        # url(r'^(?P<username>w+)/tag/(?P<param>d+)/',views.site),
        # url(r'^(?P<username>w+)/archive/(?P<param>.*)/',views.site),  # 2018-2
        # 合成一条的url
        url(r'^(?P<username>w+)/(?P<condition>category|tag|archive)/(?P<param>.*)/', views.site),
        # 文章详情页
        url(r'^(?P<username>w+)/article/(?P<article_id>d+)/',views.article_detail)
    
    ]
    路由
    LOGIN_URL = '/login/'
    # 规定 用户上传的所有的静态文件 全部放到media文件夹下
    MEDIA_ROOT = os.path.join(BASE_DIR,'media')
    # 暴露任意文件夹资源
    # MEDIA_ROOT1 = os.path.join(BASE_DIR,'app01')
    settings
    视图相关函数

    from
    django.shortcuts import render,HttpResponse,redirect from django.http import JsonResponse from app01 import myforms from app01 import models from django.contrib import auth from django.contrib.auth.decorators import login_required from django.db.models import Count,F from django.db.models.functions import TruncMonth from django.utils.safestring import mark_safe def register(request): form_obj = myforms.MyRegForm() if request.method == 'POST': back_dic = {'code':100,'msg':''} # 校验用户信息是否合法 form_obj = myforms.MyRegForm(request.POST) if form_obj.is_valid(): clean_data = form_obj.cleaned_data # clean_data = {'username':'','password':'','confirm_password':'','email':''} clean_data.pop('confirm_password') # clean_data = {'username':'','password':'','email':''} # 手动获取用户头像 user_file = request.FILES.get('myfile') if user_file: # 一定要判断用户是否传头像了 如果传了才往字典里面添加 没传不用添加 因为有默认 clean_data['avatar'] = user_file # 创建数据 models.UserInfo.objects.create_user(**clean_data) back_dic['msg'] = '注册成功' back_dic['url'] = '/login/' else: back_dic['code'] = 101 back_dic['msg'] = form_obj.errors return JsonResponse(back_dic) return render(request,'register.html',locals()) def login(request): if request.method == 'POST': back_dic = {'code':100,'msg':''} username = request.POST.get('username') password = request.POST.get('password') code = request.POST.get('code') # 1.先校验用户输入的验证码是否正确 忽略大小写校验 内部统一转大写或者小写比较 if request.session.get('code').upper() == code.upper(): # 2.数据库校验用户名和密码是否正确 user_obj = auth.authenticate(username=username,password=password) if user_obj: # 3.保存用户登录状态 auth.login(request,user_obj) back_dic['msg'] = '登录成功' back_dic['url'] = '/home/' else: back_dic['code'] = 101 back_dic['msg'] = '用户名或密码错误' else: back_dic['code'] = 102 back_dic['msg'] = '验证码错误' return JsonResponse(back_dic) return render(request,'login.html') from PIL import Image,ImageDraw,ImageFont """ Image 生成图片的 ImageDraw 在图片上写东西的 ImageFont 控制字体样式的 """ import random from io import BytesIO,StringIO """ io是一个内存管理器模块 BytesIO 能够帮你存储数据 二级制格式 StringIO 能够帮你存储数据 字符串格式 """ def get_random(): return random.randint(0,255),random.randint(0,255),random.randint(0,255) # 验证码相关 def get_code(request): # 推导步骤1:直接将本地已经存图片的以二进制方式读取发送 # with open(r'D:python脱产10期视频BBSavatar222.jpg','rb') as f: # data = f.read() # return HttpResponse(data) # 推导步骤2:能够产生任何多张的图片的方法 # img_obj = Image.new('RGB',(35,360),'green') # img_obj = Image.new('RGB',(35,360),get_random()) # # 先以文件的形式保存下来 # with open('xxx','wb') as f: # img_obj.save(f,'png') # # 然后再打开这个文件发送 # with open('xxx','rb') as f: # data = f.read() # return HttpResponse(data) # 推导步骤3:需要找一个能够临时存储文件的地方 避免频繁文件读写操作 # img_obj = Image.new('RGB', (35, 360), get_random()) # io_obj = BytesIO() # 实例化产生一个内存管理对象 你可以把它当成文件句柄 # img_obj.save(io_obj,'png') # return HttpResponse(io_obj.getvalue()) # 从内存对象中获取二级制的图片数据 # 推导步骤4:在产生的图片上 写验证码 img_obj = Image.new('RGB', (360,35), get_random()) img_draw = ImageDraw.Draw(img_obj) # 生成一个可以在图片上写字的画笔对象 img_font = ImageFont.truetype('static/font/222.ttf',30) # 字体样式 及 大小 io_obj = BytesIO() # 图片验证码 一般都是有 数字 大写字母 小写字母 # 五位 每一位都可以 数字或大写字母或小写字母 code = '' for i in range(5): upper_str = chr(random.randint(65,90)) low_str = chr(random.randint(97,122)) random_int = str(random.randint(0,9)) # 随机取一个 temp_code = random.choice([upper_str,low_str,random_int]) # 朝图片上写 img_draw.text((70+i*45,0),temp_code,get_random(),font=img_font) code += temp_code print(code) # 将产生的随机验证码 存入session中 以便后续其他视图函数获取 校验验证码 request.session['code'] = code img_obj.save(io_obj,'png') return HttpResponse(io_obj.getvalue()) def home(request): # 获取网站所有的文章 展会到前端 article_list = models.Article.objects.all() # 如果文章数目比较多 你应该做分页处理 return render(request,'home.html',locals()) @login_required def logout(request): auth.logout(request) return redirect('/login/') @login_required def set_password(request): if request.is_ajax(): back_dic = {'code':100,'msg':''} old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') confirm_password = request.POST.get('confirm_password') is_right = request.user.check_password(old_password) if is_right: if new_password == confirm_password: request.user.set_password(new_password) request.user.save() back_dic['msg'] = '修改成功' back_dic['url'] = '/login/' else: back_dic['code'] = 102 back_dic['msg'] = '两次密码不一致' else: back_dic['code'] = 101 back_dic['msg'] = '原密码错误' return JsonResponse(back_dic) def site(request,username,**kwargs): # 先获取用户用户名 查看是否存在 user_obj = models.UserInfo.objects.filter(username=username).first() if not user_obj: # 如果用户不存在 应该返回一个404页面 return render(request,'errors.html') # 获取该用户的个人站点 blog = user_obj.blog # 查询当前这个人的所有的文章 article_list = models.Article.objects.filter(blog=blog) """侧边栏筛选功能:就是对当前用户所有的文章 再进行一次筛选""" if kwargs: condition = kwargs.get('condition') # tag/category/archive param = kwargs.get('param') # 1/1/2019-1 if condition == 'category': article_list = article_list.filter(category_id=param) elif condition == 'tag': article_list = article_list.filter(tag__id=param) else: year,month = param.split('-') article_list = article_list.filter(create_time__year=year,create_time__month=month) # 查询当前用户每一个分类下的文章数 # category_menu = models.Category.objects.filter(blog=blog).annotate(c=Count('article')).values_list('name', 'c','pk') # # print(category_menu) # # 查询每一个分类下的文章数 # # res = models.Category.objects.annotate(c=Count('article')).values('name','c') # # print(res) # # 查询当前用户每一个标签下的文章数 # tag_menu = models.Tag.objects.filter(blog=blog).annotate(c=Count('article')).values_list('name','c','pk') # # print(tag_menu) # date_menu = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(c=Count('pk')).values_list('month','c') # # print(date_menu) return render(request,'site.html',locals()) def article_detail(request,username,article_id): # 先获取用户用户名 查看是否存在 user_obj = models.UserInfo.objects.filter(username=username).first() if not user_obj: # 如果用户不存在 应该返回一个404页面 return render(request, 'errors.html') blog = user_obj.blog # 根据文章id 查询出对应的文章 展示到前端 即可 article_obj = models.Article.objects.filter(pk=article_id,blog=blog).first() comment_list = models.Comment.objects.filter(article=article_obj) return render(request,'article_detail.html',locals()) import json # 仅仅只是处理ajax请求点赞点踩逻辑 def up_or_down(request): if request.is_ajax(): back_dic = {'code':100,'msg':''} # 1 先校验用户是否登录 if request.user.is_authenticated(): # 获取必要的数据 article_id = request.POST.get("article_id") is_up = request.POST.get('is_up') # 将字符串形式的js布尔值转换成后端python布尔值 is_up = json.loads(is_up) # 2 校验当前文章是否是当前用户自己写的 article_obj = models.Article.objects.filter(pk=article_id).first() if not article_obj.blog.userinfo == request.user: # 3 校验当前用户是否已经给当前文章点过赞或踩 is_click = models.UpAndDown.objects.filter(article=article_obj,user=request.user) if not is_click: # 4 操作数据库 记录数据 在记录的时候 需要做到文章表里的普通字段跟点赞点踩数据同步 并且区分是点赞还是点踩 if is_up: # 如果是赞 先把文章表里面的普通点赞字段加1 models.Article.objects.filter(pk=article_id).update(up_num = F('up_num') + 1) back_dic['msg'] = '点赞成功' else: # 如果是踩 先把文章表里面的普通点踩字段加1 models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1) back_dic['msg'] = '点踩成功' # 操作点赞点踩表 存实际数据 models.UpAndDown.objects.create(user=request.user,article=article_obj,is_up=is_up) else: back_dic['code'] = 101 back_dic['msg'] = '你已经点过了' else: back_dic['code'] = 102 back_dic['msg'] = '你个臭不要脸的,不能点自己的!' else: back_dic['code'] = 103 back_dic['msg'] = mark_safe('请先<a href="/login/">登录</a>') return JsonResponse(back_dic) """ 事务 """ from django.db import transaction def comment(request): if request.is_ajax(): back_dic = {'code':100,'msg':''} # 前端虽然更具用户是否登录展示评论框 但是后端最好再校验一次用户是否登录 if request.user.is_authenticated(): content = request.POST.get('content') article_id = request.POST.get('article_id') parentId = request.POST.get('parentId') # 评论表 文章评论数普通字段 同步 with transaction.atomic(): models.Comment.objects.create(user=request.user,article_id=article_id,content=content,parent_id=parentId) models.Article.objects.filter(pk=article_id).update(comment_num = F('comment_num') + 1) back_dic['msg'] = '评论成功' else: back_dic['code'] = 101 back_dic['msg'] = mark_safe('请先<a href="/login/">登录</a>') return JsonResponse(back_dic) from utils.mypage import Pagination @login_required def backend(request): article_list = models.Article.objects.filter(blog=request.user.blog) page_obj = Pagination(current_page=request.GET.get('page',1),all_count=article_list.count(),per_page_num=10) page_queryset = article_list[page_obj.start:page_obj.end] return render(request,'backend/backend.html',locals()) from bs4 import BeautifulSoup @login_required def add_article(request): if request.method == 'POST': title = request.POST.get('title') content = request.POST.get('content') tags = request.POST.getlist('tag') category_id = request.POST.get('category') # 麻瓜式做法 直接对content窃取150 # 1 先生成一个BeautifulSoup对象 soup = BeautifulSoup(content,'html.parser') for tag in soup.find_all(): # 针对script标签 应该直接删除 # print(tag.name) # 获取当前html页面所有的标签 if tag.name == 'script': tag.decompose() # 将符合条件的标签删除 # 文章简介应该是150个文本内容 desc = soup.text[0:150] # desc = content[0:150] article_obj = models.Article.objects.create(title=title,desc=desc,content=str(soup),category_id=category_id,blog=request.user.blog) # 一个个的添加 b_list = [] for tag_id in tags: b_list.append(models.Article2Tag(article=article_obj,tag_id=tag_id)) models.Article2Tag.objects.bulk_create(b_list) return redirect('/backend/') tag_list = models.Tag.objects.filter(blog=request.user.blog) category_list = models.Category.objects.filter(blog=request.user.blog) return render(request,'backend/add_article.html',locals()) import os from BBS import settings def upload_img(request): # 接收用户写文章传的所有的图片资源 if request.method == 'POST': file_obj = request.FILES.get('imgFile') # 要将文件存入media文件下一个专门用来存储文章图片的文件夹(article_img) # 1 手动先拼接出图片所在的文件件路径 base_path = os.path.join(settings.BASE_DIR,'media','article_img') if not os.path.exists(base_path): os.mkdir(base_path) # 2 手动拼接文件的具体路径 file_path = os.path.join(base_path,file_obj.name) # 3 文件操作 with open(file_path,'wb') as f: for line in file_obj: f.write(line) """ //成功时 { "error" : 0, "url" : "http://www.example.com/path/to/file.ext" } //失败时 { "error" : 1, "message" : "错误信息" } """ back_dic = { 'error':0, 'url':'/media/article_img/%s'%file_obj.name } return JsonResponse(back_dic) def edit_avatar(request): if request.method == 'POST': file_obj = request.FILES.get('myfile') if file_obj: # models.UserInfo.objects.filter(pk=request.user.pk).update(avatar=file_obj) # queryset更新方法修改头像的时候 不会自动加avatar前缀 # 你可以手动通过文件操作 写入文件 然后给avatar传一个手动拼接好的路径 request.user.avatar = file_obj request.user.save() return redirect('/%s/'%request.user.username) return render(request,'edit_avatar1.html')
    from django import template
    from app01 import models
    from django.db.models import Count
    from django.db.models.functions import TruncMonth
    register = template.Library()
    
    
    @register.inclusion_tag('left_menu.html')
    def left_menu(username):
        # 先获取用户用户名 查看是否存在
        user_obj = models.UserInfo.objects.filter(username=username).first()
        # 获取该用户的个人站点
        blog = user_obj.blog
        # 查询当前用户每一个分类下的文章数
        category_menu = models.Category.objects.filter(blog=blog).annotate(c=Count('article')).values_list('name', 'c',
                                                                                                           'pk')
        # print(category_menu)
        # 查询每一个分类下的文章数
        # res = models.Category.objects.annotate(c=Count('article')).values('name','c')
        # print(res)
        # 查询当前用户每一个标签下的文章数
        tag_menu = models.Tag.objects.filter(blog=blog).annotate(c=Count('article')).values_list('name', 'c', 'pk')
        # print(tag_menu)
        date_menu = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values(
            'month').annotate(c=Count('pk')).values_list('month', 'c')
        # print(date_menu)
        return locals()
    templatetags -mytags
    前后端分离
    pip安装源
        
        1、采用国内源,加速下载模块的速度
        2、常用pip源:
            -- 豆瓣:https://pypi.douban.com/simple
            -- 阿里:https://mirrors.aliyun.com/pypi/simple
        3、加速安装的命令:
            -- >: pip install -i https://pypi.douban.com/simple 模块名
    
    永久配置安装源
        1、文件管理器文件路径地址栏敲:%APPDATA% 回车,快速进入 C:Users电脑用户AppDataRoaming 文件夹中
        2、新建 pip 文件夹并在文件夹中新建 pip.ini 配置文件
        3、新增 pip.ini 配置文件内容
        
    MacOS、Linux
        1、在用户根目录下 ~ 下创建 .pip 隐藏文件夹,如果已经有了可以跳过
            -- mkdir ~/.pip
        2、进入 .pip 隐藏文件夹并创建 pip.conf 配置文件
            -- cd ~/.pip && touch pip.conf
        3、启动 Finder(访达) 按 cmd+shift+g 来的进入,输入 ~/.pip 回车进入
        4、新增 pip.conf 配置文件内容
        
        配置文件内容
            [global]
            index-url = http://pypi.douban.com/simple
            [install]
            use-mirrors =true
            mirrors =http://pypi.douban.com/simple/
            trusted-host =pypi.douban.com              
            
    虚拟环境的搭建
        优点:
            1、使不同应用开发环境相互独立
            2、环境升级不影响其他应用,也不会影响全局的python环境
            3、防止出现包管理混乱及包版本冲突
    
        1.windows安装    :
            # 建议使用pip3安装到python3环境下
            pip3 install virtualenv
            pip3 install virtualenvwrapper-win
        
    配置环境变量:
    控制面板 => 系统和安全 => 系统 => 高级系统设置 => 环境变量 => 系统变量 => 点击新建 => 填入变量名与值
    变量名:WORKON_HOME  变量值:自定义存放虚拟环境的绝对路径
        eg: WORKON_HOME: D:Virtualenvs
    
    同步配置信息:
    去向Python3的安装目录 => Scripts文件夹 => virtualenvwrapper.bat => 双击
    配置虚拟环境管理器工作目录  

          2.在终端工作的命令

        1、创建虚拟环境到配置的WORKON_HOME路径下
          选取默认Python环境创建虚拟环境:
          -- mkvirtualenv 虚拟环境名称
           基于某Python环境创建虚拟环境:
          -- mkvirtualenv -p python2.7 虚拟环境名称
          -- mkvirtualenv -p python3.6 虚拟环境名称

        2、查看已有的虚拟环境
          -- workon

        3、使用某个虚拟环境
          -- workon 虚拟环境名称

        4、进入|退出 该虚拟环境的Python环境
          -- python | exit()

        5、为虚拟环境安装模块
          -- pip或pip3 install 模块名

        6、退出当前虚拟环境
          -- deactivate

        7、删除虚拟环境(删除当前虚拟环境要先退出)
          -- rmvirtualenv 虚拟环境名称

    MacOS、Linux  安装

      # 建议使用pip3安装到python3环境下
      pip3 install -i https://pypi.douban.com/simple virtualenv
      pip3 install -i https://pypi.douban.com/simple virtualenvwrapper

    # 先找到virtualenvwrapper的工作文件 virtualenvwrapper.sh,该文件可以刷新自定义配置,但需要找到它
    # MacOS可能存在的位置 /Library/Frameworks/Python.framework/Versions/版本号文件夹/bin
    # Linux可能所在的位置 /usr/local/bin  |  ~/.local/bin  |  /usr/bin
    # 建议不管virtualenvwrapper.sh在哪个目录,保证在 /usr/local/bin 目录下有一份
    # 如果不在 /usr/local/bin 目录,如在 ~/.local/bin 目录,则复制一份到 /usr/local/bin 目录
        -- sudo cp -rf ~/.local/bin/virtualenvwrapper.sh /usr/local/bin
    工作文件
    # 在 ~/.bash_profile 完成配置,virtualenvwrapper的默认默认存放虚拟环境路径是 ~/.virtualenvs
    # WORKON_HOME=自定义存放虚拟环境的绝对路径,需要自定义就解注
    VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3
    source /usr/local/bin/virtualenvwrapper.sh
    
    # 在终端让配置生效:
        -- source ~/.bash_profile
    配置

    xadmin后台管理的使用

    安装 :
        >: pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2
    
    注册app:
        INSTALLED_APPS = [
            # ...
            # xamin主体模块
            'xadmin',
            # 渲染表格模块
            'crispy_forms',
            # 为模型通过版本控制,可以回滚数据
            'reversion',
        ]
    
    xadmin:需要自己的数据库模型类,完成数据库迁移
        python manage.py makemigrations
        python manage.py migrate
    
    设置主路由替换掉admin:主urls.py
    
        # xadmin的依赖
        import xadmin
        xadmin.autodiscover()
        # xversion模块自动注册需要版本控制的 Model
        from xadmin.plugins import xversion
        xversion.register_models()
    
        urlpatterns = [
        
            path(r'xadmin/', xadmin.site.urls),
        ]    
    
    
    完成xadmin全局配置:新建xxx/adminx.py
    
        import xadmin
        from xadmin import views
    
        class GlobalSettings(object):
            """xadmin的全局配置"""
            site_title = "xxxx"  # 设置站点标题
            site_footer = "xxxx"  # 设置站点的页脚
            menu_style = "accordion"  # 设置菜单折叠
    
        xadmin.site.register(views.CommAdminView, GlobalSettings)

    支付相关 

    # 1、在沙箱环境下实名认证:https://openhome.alipay.com/platform/appDaily.htm?tab=info
    
    # 2、电脑网站支付API:https://docs.open.alipay.com/270/105898/
    
    # 3、完成RSA密钥生成:https://docs.open.alipay.com/291/105971
    
    # 4、在开发中心的沙箱应用下设置应用公钥:填入生成的公钥文件中的内容
    
    # 5、Python支付宝开源框架:https://github.com/fzlee/alipay
    # >: pip install python-alipay-sdk --upgrade
    
    # 7、公钥私钥设置
    """
    # alipay_public_key.pem
    -----BEGIN PUBLIC KEY-----
    支付宝公钥
    -----END PUBLIC KEY-----
    
    # app_private_key.pem
    -----BEGIN RSA PRIVATE KEY-----
    用户私钥
    -----END RSA PRIVATE KEY-----
    """
    
    # 8、支付宝链接
    """
    开发:https://openapi.alipay.com/gateway.do
    沙箱:https://openapi.alipaydev.com/gateway.do
    """
    支付宝支付
    依赖:
        pip install python-alipay-sdk 
    
    结构:
       libs
        ├── iPay                              # aliapy二次封装包
        │   ├── __init__.py                 # 包文件
        │   ├── keys                        # 密钥文件夹
        │   │   ├── alipay_public_key.pem      # 支付宝公钥
        │   │   └── app_private_key.pem      # 应用私钥
        └── └── settings.py                  # 应用配置  
    
    setting.py:
    import os
    # 支付宝应用id
    APP_ID = '2016093000631831'
    # 默认异步回调的地址,通常设置None就行
    APP_NOTIFY_URL = None
    # 应用私钥文件路径
    APP_PRIVATE_KEY_PATH = os.path.join(os.path.dirname(__file__), 'keys', 'app_private_key.pem')
    # 支付宝公钥文件路径
    ALIPAY_PUBLIC_KEY_PATH = os.path.join(os.path.dirname(__file__), 'keys', 'alipay_public_key.pem')
    # 签名方式
    SIGN_TYPE = 'RSA2'
    # 是否是测试环境 - 是否是支付宝沙箱
    DEBUG = True
    # 支付连接
    DEV_PAY_URL = 'https://openapi.alipaydev.com/gateway.do?'
    PROD_PAY_URL = 'https://openapi.alipay.com/gateway.do?'
    
    __init__.py
    
    from alipay import AliPay
    from .settings import *
    # 对外提供支付对象
    alipay = AliPay(
        appid=APP_ID,
        app_notify_url=APP_NOTIFY_URL,
        app_private_key_path=APP_PRIVATE_KEY_PATH,
        alipay_public_key_path=ALIPAY_PUBLIC_KEY_PATH,
        sign_type=SIGN_TYPE,
        debug=DEBUG
    )
    
    # 对外提供的支付链接前缀
    pay_url = DEV_PAY_URL if DEBUG else PROD_PAY_URL
    
    
    订单序列模块:
    from rest_framework import serializers
    from . import models
    class OrderModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Order
            fields = ('subject', 'total_amount', 'out_trade_no', 'pay_type', 'user')
            extra_kwargs = {
                'pay_type': {
                    'required': True
                },
                'total_amount': {
                    'required': True
                },
            }
    
    支付接口生成支付连接:
    
    import time
    from rest_framework.views import APIView
    from utils.response import APIResponse
    from libs.iPay import alipay
    from . import authentications, serializers
    from rest_framework.permissions import IsAuthenticated
    from django.conf import settings
    # 获取前台 商品名、价格,产生 订单、支付链接
    class PayAPIView(APIView):
        authentication_classes = [authentications.JWTAuthentication]
        permission_classes = [IsAuthenticated]
        def post(self, request, *args, **kwargs):
            # 前台提供:商品名、总价、支付方式
            request_data = request.data
            # 后台产生:订单号、用户
            out_trade_no = '%d' % time.time() * 2
            request_data['out_trade_no'] = out_trade_no
            request_data['user'] = request.user.id
    
            # 反序列化数据,用于订单生成前的校验
            order_ser = serializers.OrderModelSerializer(data=request_data)
            if order_ser.is_valid():
                # 生成订单,订单默认状态为:未支付
                order = order_ser.save()
                # 支付链接的参数
                order_string = alipay.api_alipay_trade_page_pay(
                    subject=order.subject,
                    out_trade_no=order.out_trade_no,
                    total_amount='%.2f' % order.total_amount,
                    return_url=settings.RETURN_URL,
                    notify_url=settings.NOTIFY_URL
                )
                # 形成支付链接:alipay._gateway根据字符环境DEBUG配置信息,决定是沙箱还是真实支付环境
                pay_url = '%s?%s' % (alipay._gateway, order_string)
                return APIResponse(0, 'ok', pay_url=pay_url)
    
    
            return APIResponse(1, 'no ok', results=order_ser.errors)
    
    支付完成订单校验的接口:
    from . import models
    from utils.logging import logger
    from rest_framework.response import Response
    class SuccessAPIView(APIView):
        # 不能认证,别人支付宝异步回调就进不来了
        # authentication_classes = [authentications.JWTAuthentication]
        # permission_classes = [IsAuthenticated]
        def patch(self, request, *args, **kwargs):
            # 默认是QueryDict类型,不能使用pop方法
            request_data = request.query_params.dict()
            # 必须将 sign、sign_type(内部有安全处理) 从数据中取出,拿sign与剩下的数据进行校验
            sign = request_data.pop('sign')
            result = alipay.verify(request_data, sign)
            if result:  # 同步回调:修改订单状态
                try:
                    out_trade_no = request_data.get('out_trade_no')
                    order = models.Order.objects.get(out_trade_no=out_trade_no)
                    if order.order_status != 1:
                        order.order_status = 1
                        order.save()
                except:
                    pass
                return APIResponse(0, '支付成功')
            return APIResponse(1, '支付失败')
    
        # 支付宝异步回调
        def post(self, request, *args, **kwargs):
            # 默认是QueryDict类型,不能使用pop方法
            request_data = request.data.dict()
            # 必须将 sign、sign_type(内部有安全处理) 从数据中取出,拿sign与剩下的数据进行校验
            sign = request_data.pop('sign')
            result = alipay.verify(request_data, sign)
            # 异步回调:修改订单状态
            if result and request_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED" ):
                out_trade_no = request_data.get('out_trade_no')
                logger.critical('%s支付成功' % out_trade_no)
                try:
                    order = models.Order.objects.get(out_trade_no=out_trade_no)
                    if order.order_status != 1:
                        order.order_status = 1
                        order.save()
                except:
                    pass
                # 支付宝八次异步通知,订单成功一定要返回 success
                return Response('success')
            return Response('failed')
    ### 服务器命令
    
    ##### 管理员权限
    
    ```
    1)以下所有的服务器命令均可以在管理员权限下执行
    >: sudo 命令
    ```
    
    ##### 配置终端
    
    ```
    1)编辑配置文件
    >: vim ~/.bash_profile
    
    2)将原来内容全部删除掉
    >: ggdG
    
    3)进入编辑状态:填入下方两行
    >: i
    
    export PATH=$PATH:$HOME/bin
    PS1='Path:w
    >:'
    
    4)退出编辑状态
    >: esc
    
    5)保存修改并退出
    >: :wq
    
    6)生效配置
    >: source ~/.bash_profile
    ```
    
    
    
    ## 重要
    
    ##### 更新系统软件包
    
    ```
    >: yum update -y
    ```
    
    ##### 安装软件管理包和可能使用的依赖
    
    ```
    >: yum -y groupinstall "Development tools"
    >: yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel psmisc libffi-devel
    ```
    
    
    
    
    
    ### 安装Mysql
    
    ```
    1)前往用户根目录
    >: cd ~
    
    2)下载mysql57
    >: wget http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
    
    也可以本地上传,这条命令要在本地终端上执行
    >: scp -r C:UsersdellDesktoppkgmysql57-community-release-el7-10.noarch.rpm root@39.98.144.221:~
    
    3)安装mysql57
    >: yum -y install mysql57-community-release-el7-10.noarch.rpm
    >: yum -y install mysql-community-server
    
    4)启动mysql57并查看启动状态
    >: systemctl start mysqld.service
    >: systemctl status mysqld.service
    
    5)查看默认密码并登录
    >: grep "password" /var/log/mysqld.log
    >: mysql -uroot -p
    
    6)修改密码
    >: ALTER USER 'root'@'localhost' IDENTIFIED BY 'new password';
    >: ALTER USER 'root'@'localhost' IDENTIFIED BY 'Owen1234?';
    ```
    
    ALTER USER 'root'@'localhost' IDENTIFIED BY '678666';
    
    
    
    ### 安装Redis
    
    ```
    1)前往用户根目录
    >: cd ~
    
    2)下载redis-5.0.5
    >: wget http://download.redis.io/releases/redis-5.0.5.tar.gz
    >: scp -r C:UsersdellDesktoppkg
    edis-5.0.5.tar.gz root@39.98.144.221:~
    
    3)解压安装包
    >: tar -xf redis-5.0.5.tar.gz
    
    4)进入目标文件
    >: cd redis-5.0.5
    
    5)编译环境
    >: make
    
    6)复制环境到指定路径完成安装
    >: cp -r ~/redis-5.0.5 /usr/local/redis
    
    7)配置redis可以后台启动:修改下方内容
    >: vim /usr/local/redis/redis.conf
    
    daemonize yes
    
    8)完成配置修改
    >: esc
    >: :wq
    
    9)建立软连接
    >: ln -s /usr/local/redis/src/redis-server /usr/bin/redis-server
    >: ln -s /usr/local/redis/src/redis-cli /usr/bin/redis-cli
    
    10)后台运行redis
    >: redis-server &
    ctrl + c
    
    11)测试redis环境
    >: redis-cli
    ctrl + c
    
    12)关闭redis服务
    >: pkill -f redis -9
    ```
    
    
    
    ### 安装Python3.6
    
    ```
    1)前往用户根目录
    >: cd ~
    
    2)下载 或 上传 Python3.6.7
    >: wget https://www.python.org/ftp/python/3.6.7/Python-3.6.7.tar.xz
    >: scp -r 本地Python-3.6.7.tar.xz ssh root@39.98.144.221:服务器路径
    >: scp -r C:UsersdellDesktoppkgPython-3.6.7.tar.xz ssh root@39.98.144.221:~
    
    3)解压安装包
    >: tar -xf Python-3.6.7.tar.xz
    
    4)进入目标文件
    >: cd Python-3.6.7
    
    5)配置安装路径:/usr/local/python3
    >: ./configure --prefix=/usr/local/python3
    
    6)编译并安装
    >: make && sudo make install
    
    7)建立软连接:终端命令 python3,pip3
    >: ln -s /usr/local/python3/bin/python3.6 /usr/bin/python3
    >: ln -s /usr/local/python3/bin/pip3.6 /usr/bin/pip3
    
    8)删除安装包与文件:
    >: rm -rf Python-3.6.7
    >: rm -rf Python-3.6.7.tar.xz
    ```
    
    
    
    
    
    ### 配置pip源:阿里云不用配置,默认配置阿里源
    
    ```
    1)创建pip配置路径
    >: mkdir ~/.pip
    
    2)进入目录编辑配置文件:填入下方内容
    cd ~/.pip && vim pip.conf
    
    [global]
    index-url = http://pypi.douban.com/simple
    [install]
    use-mirrors =true
    mirrors =http://pypi.douban.com/simple/
    trusted-host =pypi.douban.com
    ```
    
    
    
    ### 安装uwsgi
    
    ```
    1)在真实环境下安装
    pip3 install uwsgi
    
    2)建立软连接
    ln -s /usr/local/python3/bin/uwsgi /usr/bin/uwsgi
    ```
    
    
    
    
    
    ### 安装虚拟环境
    
    ```
    1)安装依赖
    >: pip3 install virtualenv
    >: pip3 install virtualenvwrapper
    
    2)建立虚拟环境软连接
    >: ln -s /usr/local/python3/bin/virtualenv /usr/bin/virtualenv
    
    3)配置虚拟环境:填入下方内容
    >: vim ~/.bash_profile
    
    VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
    source /usr/local/python3/bin/virtualenvwrapper.sh
    
    4)退出编辑状态
    >: esc
    
    5)保存修改并退出
    >: :wq
    
    6)更新配置文件内容
    >: source ~/.bash_profile
    
    7)虚拟环境默认根目录:~/.virtualenvs
    ```
    
    
    
    
    
    ### 了解:服务器运行测试Django项目
    
    ```
    1)创建虚拟环境
    >: mkvirtualenv test_venv
    
    2)安装依赖
    >: pip install django
    
    3)前往目标目录,创建项目工作目录,再进入
    >: cd /home
    >: mkdir project
    >: cd project
    
    4)创建Django项目,并进入
    >: django-admin startproject test_site
    >: cd test_site
    
    5)完成项目配置:修改下方几行内容
    >: vim /home/project/test_site/test_site/settings.py
    
    ALLOWED_HOSTS = ['*']
    #DATABASES = {
    #    'default': {
    #        'ENGINE': 'django.db.backends.sqlite3',
    #        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    #    }
    #}
    
    6)跑原生服务
    >: python3 manage.py runserver 0.0.0.0:80
    ```
    
    
    
    
    
    ### 安装Nginx
    
    ```
    1)前往用户根目录
    >: cd ~
    
    2)下载nginx1.13.7
    >: wget http://nginx.org/download/nginx-1.13.7.tar.gz
    
    3)解压安装包
    >: tar -xf nginx-1.13.7.tar.gz
    
    4)进入目标文件
    >: cd nginx-1.13.7
    
    5)配置安装路径:/usr/local/nginx
    >: ./configure --prefix=/usr/local/nginx
    
    6)编译并安装
    >: make && sudo make install
    
    7)建立软连接:终端命令 nginx
    >: ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx
    
    8)删除安装包与文件:
    >: rm -rf nginx-1.13.7
    >: rm -rf nginx-1.13.7.tar.xz
    
    9)测试Nginx环境,服务器运行nginx,本地访问服务器ip
    >: nginx
    >: 服务器绑定的域名 或 ip:80
    ```
    
    
    
    ### Nginx命令
    
    ```
    1)启动
    >: nginx
    
    2)关闭nginx
    >: nginx -s stop
    
    3)重启nginx
    >: nginx -s reload
    
    4)查看端口,强行关闭
    >: ps -aux|grep nginx
    >: kill <pid:进程编号>
    ```
    
    
    
    
    
    ### 了解:Nginx & uwsgi 运行Django
    
    ```
    1)在项目的虚拟环境安装uwsgi
    >: workon test_venv
    >: pip install uwsgi
    
    2)项目根目录配置uwsgi:填入下方内容
    >: vim /home/project/test_site/test_site.xml
    
    <uwsgi>    
       <socket>127.0.0.1:8808</socket> <!-- 内部端口,自定义 --> 
       <chdir>/home/project/test_site/</chdir> <!-- 项目路径 -->            
       <module>test_site.wsgi</module>  <!-- test_site为wsgi.py所在目录名--> 
       <processes>4</processes> <!-- 进程数 -->     
       <daemonize>uwsgi.log</daemonize> <!-- 日志文件 -->
    </uwsgi>
    
    3)完成项目配置:修改下方几行内容
    >: vim /home/project/test_site/test_site/settings.py
    
    DEBUG = False
    ALLOWED_HOSTS = ['*']
    
    4)去向Nginx配置目录,备份配置,完全更新配置:填入下方内容
    >: cd /usr/local/nginx/conf
    >: cp nginx.conf nginx.conf.bak
    >: vim nginx.conf
    >: ggdG
    >: i
    
    events {
        worker_connections  1024;
    }
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        server {
            listen 8000;
            server_name  127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
            charset utf-8;
            location / {
               include uwsgi_params;
               uwsgi_pass 127.0.0.1:8808;  # 端口要和uwsgi里配置的一样
               uwsgi_param UWSGI_SCRIPT test_site.wsgi;  #wsgi.py所在的目录名+.wsgi
               uwsgi_param UWSGI_CHDIR /home/project/test_site/; # 项目路径
            }
        }
    }
    
    5)启动uwsgi
    >: uwsgi -x /home/project/test_site/test_site.xml
    
    6)启动nginx
    >: nginx
    
    7)浏览器测试:http://39.98.144.221/admin
    
    8)关闭uwsgi所有进程
    >: pkill -f uwsgi -9
    ```
    
    
    
    
    
    
    
    
    
    ## 路飞项目部署:Nginx + uwsgi + django + vue
    
    
    
    ### 配置前台项目
    
    #### 上线前配置
    
    ##### assets/settings.js
    
    ```js
    base_url: 'http://39.98.144.221:8000',  // 设置公网ip
    ```
    
    #### 上线
    
    ```
    1)本地项目打包,前往luffycity项目目录下
    >: cnpm run build
    
    2)上传
    >: scp -r dist root@39.98.144.221:~
    
    3)移动并重命名
    mv ~/dist /home/html
    
    4)去向Nginx配置目录,备份配置,完全更新配置:填入下方内容
    >: cd /usr/local/nginx/conf
    >: cp nginx.conf nginx.conf.bak
    >: vim nginx.conf
    >: ggdG
    >: i
    
    events {
        worker_connections  1024;
    }
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        server {
            listen 80;
            server_name  127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
            charset utf-8;
            location / {
                root /home/html; # html访问路径
                index index.html; # html文件名称
                try_files $uri $uri/ /index.html; # 解决单页面应用刷新404问题
            }
        }
    }                                                                   
    ```
    
    
    
    
    
    ### 路飞后台部署
    
    #### 上线前配置
    
    ##### prod.py:上线的配置文件,内容拷贝dev.py,前身就是settings.py
    
    ```
    1)需要做上线修改的内容
    DEBUG = False
    ALLOWED_HOSTS = [
        '39.98.144.221'  # 公网ip地址
    ]
    
    CORS_ORIGIN_ALLOW_ALL = True  # 允许所有跨域
    CORS_ORIGIN_WHITELIST = [
    ]
    
    UP_BASE_URL = 'http://39.98.144.221:8080'
    END_BASE_URL = 'http://39.98.144.221:8000'
    ```
    
    ##### wsgi.py 和 manage.py
    
    ```
    1)需要做上线修改的内容
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.prod')
    ```
    
    ##### 清空日志文件
    
    
    
    #### 上线
    
    ```
    1)在项目的虚拟环境安装uwsgi
    >: mkvirtualenv luffy
    >: workon luffy
    # 走下方 pip导入导出依赖 说明,将本地的环境依赖同步到服务器环境中
    >: pip install uwsgi
    
    2)项目根目录配置uwsgi:填入下方内容
    >: mkdir /home/project
    
    # 注:将后台项目移至到/home/project,可以上传,也可以git,项目设置公开(私密需要配置ssl)
    >: cd /home/project && git clone https://gitee.com/doctor_owen/luffyapi.git 
    >: vim /home/project/luffyapi/luffyapi.xml
    
    <uwsgi>    
       <socket>127.0.0.1:8808</socket> <!-- 内部端口,自定义 --> 
       <chdir>/home/project/luffyapi/</chdir> <!-- 项目路径 -->            
       <module>luffyapi.wsgi</module>  <!-- luffyapi为wsgi.py所在目录名--> 
       <processes>4</processes> <!-- 进程数 -->     
       <daemonize>uwsgi.log</daemonize> <!-- 日志文件 -->
    </uwsgi>
    
     ---- 3)配置上线项目的settings:见后台项目部署准备视频
    
    4)去向Nginx配置目录,备份配置,完全更新配置:填入下方内容
    >: vim /usr/local/nginx/conf/nginx.conf
    
    5)在原来基础上添加一个server
    events {
        worker_connections  1024;
    }
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        server {
            listen 8000;
            server_name  127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
            charset utf-8;
            location / {
               include uwsgi_params;
               uwsgi_pass 127.0.0.1:8808;  # 端口要和uwsgi里配置的一样
               uwsgi_param UWSGI_SCRIPT luffyapi.wsgi;  #wsgi.py所在的目录名+.wsgi
               uwsgi_param UWSGI_CHDIR /home/project/luffyapi/; # 项目路径
            }
        }
    }
    
    见下方配置:pip环境 + 数据库设置 + django2.0源码 配置完成后再进行往下(配置教程在下方)
    
    5)启动uwsgi
    >: uwsgi -x /home/project/luffyapi/luffyapi.xml
    
    6)启动nginx
    >: nginx -s stop
    >: nginx
    >: nginx -s reload
    
    7)浏览器测试:http://39.98.144.221:8000/xadmin
    
    8)关闭uwsgi所有进程
    >: pkill -f uwsgi -9
    ```
    
    ### pip导入导出依赖
    
    ```
    1) 本地导出项目环境,上传线上,导入到线上环境中
    
    本地操作
    # 桌面新建env文件夹,开启终端进入文件夹,执行下方命名
    >: cd Desktopenv
    >: pip3 freeze > packages.txt
    # 注:把xadmin删掉
    >: scp -r packages.txt root@39.98.144.221:~
    
    服务器操作
    # 进入虚拟环境
    >: workon luffy
    # 导入
    >: pip3 install -r packages.txt
    # 安装xadmin
    >: pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2
    ```
    
    
    
    ### 数据库设置 + django2.0源码(2.0.7不用修改源码)
    
    ```
    1.管理员连接数据库
    >: mysql -uroot -pOwen1234?
    
    2.创建数据库
    >: create database luffy default charset=utf8;
    
    # 3.设置权限账号密码
    # 拥有公网或局域网,其他主机连mysql
    >: grant all privileges on luffy.* to 'luffy'@'%' identified by 'Luffy123?';
    # 要是本机连mysql连不上,再增加localhost域,本机就可以登录了
    >: grant all privileges on luffy.* to 'luffy'@'localhost' identified by 'Luffy123?';
    # 设置完有权限限制的账号后一定要刷新权限
    >: flush privileges;
    
    4.退出mysql
    quit
    
    5.修改 prod.py | manage.py
    >: vim /home/project/luffyapi/luffyapi/settings/prod.py
    
    "PASSWORD": "Luffy123?"
    
    >: vim /home/project/luffyapi/manage.py
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.prod')
    
    6.源码修改
    >: vim /root/.virtualenvs/luffy/lib/python3.6/site-packages/django/db/backends/mysql/base.py
    
    # 36,37行注释
    
    >: vim /root/.virtualenvs/luffy/lib/python3.6/site-packages/django/db/backends/mysql/operations.py
    
    # 146行添加
        query = query.encode()
    
    7.数据库迁移
    >: cd /home/project/luffyapi/
    >: python3 manage.py makemigrations
    >: python3 manage.py migrate
    
    8.创建超级用户
    >: python3 manage.py createsuperuser
    # 账号密码:admin|admin123
    ```
    
    
    
    
    
    ### 后台样式问题
    
    ##### 设置文件中配置STATIC_ROOT
    
    ```python
    # >: vim /home/project/luffyapi/luffyapi/settings/prod.py
    # 在STATIC_URL下方再添加两句
    STATIC_URL = '/static/'
    STATIC_ROOT = '/home/project/luffyapi/luffyapi/static'  # 服务器的绝对路径
    STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)  # 小luffyapi下要有static文件夹
    # >: esc
    # >: :wq
    ```
    
    ##### 迁移静态样式:项目目录下
    
    ```
    可能错误
    >: mkdir /home/project/luffyapi/luffyapi/static
    >: python3 /home/project/luffyapi/manage.py collectstatic
    ```
    
    ##### Nginx配置静态路径
    
    ```
    >: vim /usr/local/nginx/conf/nginx.conf
    
    events {
        worker_connections  1024;
    }
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        server {
            listen 8000;
            server_name  127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
            charset utf-8;
            location / {
               include uwsgi_params;
               uwsgi_pass 127.0.0.1:8808;  # 端口要和uwsgi里配置的一样
               uwsgi_param UWSGI_SCRIPT luffyapi.wsgi;  #wsgi.py所在的目录名+.wsgi
               uwsgi_param UWSGI_CHDIR /home/project/luffyapi/; # 项目路径
            }
            # 新增的配置静态文件
            location /static {
                alias /home/project/luffyapi/luffyapi/static;
            }
        }
        server {
            listen 80;
            server_name  127.0.0.1; # 改为自己的域名,没域名修改为127.0.0.1:80
            charset utf-8;
            location / {
                root /home/html; # html访问路径
                index index.html; # html文件名称
                try_files $uri $uri/ /index.html; # 解决单页面应用刷新404问题
            }
        }
    }
    
    >: esc
    >: :wq
    ```
    
    ##### 重启服务
    
    ```
    >: pkill -f uwsgi -9
    >: uwsgi -x /home/project/luffyapi/luffyapi.xml
    >: nginx -s reload
    ```
    
    
    
    ## 重点 重点 重点
    
    ```python
    # 1、真实环境和虚拟环境都要安装uwsgi,将真实环境下的uwsgi建立软连接
    
    # 2、redis服务一定要后台启动:redis-server &
    
    # 3、uwsgi启动django项目一定要进入虚拟环境下,因为环境都是安装在虚拟环境中
    
    # 4、服务器的日志都会被记录在于uwsgi配置文件 luffyapi.xml 同类目下的 uwsgi.log 中
    ```
    
    
    
    
    
    ## 添加测试数据
    
    ```
    >: mysql -uluffy -pLuffy123?
    >: use luffy
    ```
    
    ```sql
    INSERT INTO luffy_teacher(id, orders, is_show, is_delete, created_time, updated_time, name, role, title, signature, image, brief) VALUES (1, 1, 1, 0, '2019-07-14 13:44:19.661327', '2019-07-14 13:46:54.246271', 'Alex', 1, '老男孩Python教学总监', '金角大王', 'teacher/alex_icon.png', '老男孩教育CTO & CO-FOUNDER 国内知名PYTHON语言推广者 51CTO学院20162017年度最受学员喜爱10大讲师之一 多款开源软件作者 曾任职公安部、飞信、中金公司、NOKIA中国研究院、华尔街英语、ADVENT、汽车之家等公司');
    
    INSERT INTO luffy_teacher(id, orders, is_show, is_delete, created_time, updated_time, name, role, title, signature, image, brief) VALUES (2, 2, 1, 0, '2019-07-14 13:45:25.092902', '2019-07-14 13:45:25.092936', 'Mjj', 0, '前美团前端项目组架构师', NULL, 'teacher/mjj_icon.png', '是马JJ老师, 一个集美貌与才华于一身的男人,搞过几年IOS,又转了前端开发几年,曾就职于美团网任高级前端开发,后来因为不同意王兴(美团老板)的战略布局而出家做老师去了,有丰富的教学经验,开起车来也毫不含糊。一直专注在前端的前沿技术领域。同时,爱好抽烟、喝酒、烫头(锡纸烫)。 我的最爱是前端,因为前端妹子多。');
    
    INSERT INTO luffy_teacher(id, orders, is_show, is_delete, created_time, updated_time, name, role, title, signature, image, brief) VALUES (3, 3, 1, 0, '2019-07-14 13:46:21.997846', '2019-07-14 13:46:21.997880', 'Lyy', 0, '老男孩Linux学科带头人', NULL, 'teacher/lyy_icon.png', 'Linux运维技术专家,老男孩Linux金牌讲师,讲课风趣幽默、深入浅出、声音洪亮到爆炸');
    
    INSERT INTO luffy_course_category(id, orders, is_show, is_delete, created_time, updated_time, name) VALUES (1, 1, 1, 0, '2019-07-14 13:40:58.690413', '2019-07-14 13:40:58.690477', 'Python');
    
    INSERT INTO luffy_course_category(id, orders, is_show, is_delete, created_time, updated_time, name) VALUES (2, 2, 1, 0, '2019-07-14 13:41:08.249735', '2019-07-14 13:41:08.249817', 'Linux');
    
    INSERT INTO luffy_course(id, orders, is_show, is_delete, created_time, updated_time, name, course_img, course_type, brief, level, pub_date, period, attachment_path, status, students, sections, pub_sections, price, course_category_id, teacher_id) VALUES (1, 1, 1, 0, '2019-07-14 13:54:33.095201', '2019-07-14 13:54:33.095238', 'Python开发21天入门', 'courses/alex_python.png', 0, 'Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土&&&Python从入门到入土', 0, '2019-07-14', 21, '', 0, 231, 120, 120, 0.00, 1, 1);
    
    INSERT INTO luffy_course(id, orders, is_show, is_delete, created_time, updated_time, name, course_img, course_type, brief, level, pub_date, period, attachment_path, status, students, sections, pub_sections, price, course_category_id, teacher_id) VALUES (2, 2, 1, 0, '2019-07-14 13:56:05.051103', '2019-07-14 13:56:05.051142', 'Python项目实战', 'courses/mjj_python.png', 0, '', 1, '2019-07-14', 30, '', 0, 340, 120, 120, 99.00, 1, 2);
    
    INSERT INTO luffy_course(id, orders, is_show, is_delete, created_time, updated_time, name, course_img, course_type, brief, level, pub_date, period, attachment_path, status, students, sections, pub_sections, price, course_category_id, teacher_id) VALUES (3, 3, 1, 0, '2019-07-14 13:57:21.190053', '2019-07-14 13:57:21.190095', 'Linux系统基础5周入门精讲', 'courses/lyy_linux.png', 0, '', 0, '2019-07-14', 25, '', 0, 219, 100, 100, 39.00, 2, 3);
    
    INSERT INTO luffy_course_chapter(id, orders, is_show, is_delete, created_time, updated_time, chapter, name, summary, pub_date, course_id) VALUES (1, 1, 1, 0, '2019-07-14 13:58:34.867005', '2019-07-14 14:00:58.276541', 1, '计算机原理', '', '2019-07-14', 1);
    
    INSERT INTO luffy_course_chapter(id, orders, is_show, is_delete, created_time, updated_time, chapter, name, summary, pub_date, course_id) VALUES (2, 2, 1, 0, '2019-07-14 13:58:48.051543', '2019-07-14 14:01:22.024206', 2, '环境搭建', '', '2019-07-14', 1);
    
    INSERT INTO luffy_course_chapter(id, orders, is_show, is_delete, created_time, updated_time, chapter, name, summary, pub_date, course_id) VALUES (3, 3, 1, 0, '2019-07-14 13:59:09.878183', '2019-07-14 14:01:40.048608', 1, '项目创建', '', '2019-07-14', 2);
    
    INSERT INTO luffy_course_chapter(id, orders, is_show, is_delete, created_time, updated_time, chapter, name, summary, pub_date, course_id) VALUES (4, 4, 1, 0, '2019-07-14 13:59:37.448626', '2019-07-14 14:01:58.709652', 1, 'Linux环境创建', '', '2019-07-14', 3);
    
    INSERT INTO luffy_course_Section(id, is_show, is_delete, created_time, updated_time, name, orders, section_type, section_link, duration, pub_date, free_trail, chapter_id) VALUES (1, 1, 0, '2019-07-14 14:02:33.779098', '2019-07-14 14:02:33.779135', '计算机原理上', 1, 2, NULL, NULL, '2019-07-14 14:02:33.779193', 1, 1);
    
    INSERT INTO luffy_course_Section(id, is_show, is_delete, created_time, updated_time, name, orders, section_type, section_link, duration, pub_date, free_trail, chapter_id) VALUES (2, 1, 0, '2019-07-14 14:02:56.657134', '2019-07-14 14:02:56.657173', '计算机原理下', 2, 2, NULL, NULL, '2019-07-14 14:02:56.657227', 1, 1);
    
    INSERT INTO luffy_course_Section(id, is_show, is_delete, created_time, updated_time, name, orders, section_type, section_link, duration, pub_date, free_trail, chapter_id) VALUES (3, 1, 0, '2019-07-14 14:03:20.493324', '2019-07-14 14:03:52.329394', '环境搭建上', 1, 2, NULL, NULL, '2019-07-14 14:03:20.493420', 0, 2);
    
    INSERT INTO luffy_course_Section(id, is_show, is_delete, created_time, updated_time, name, orders, section_type, section_link, duration, pub_date, free_trail, chapter_id) VALUES (4, 1, 0, '2019-07-14 14:03:36.472742', '2019-07-14 14:03:36.472779', '环境搭建下', 2, 2, NULL, NULL, '2019-07-14 14:03:36.472831', 0, 2);
    
    INSERT INTO luffy_course_Section(id, is_show, is_delete, created_time, updated_time, name, orders, section_type, section_link, duration, pub_date, free_trail, chapter_id) VALUES (5, 1, 0, '2019-07-14 14:04:19.338153', '2019-07-14 14:04:19.338192', 'web项目的创建', 1, 2, NULL, NULL, '2019-07-14 14:04:19.338252', 1, 3);
    
    INSERT INTO luffy_course_Section(id, is_show, is_delete, created_time, updated_time, name, orders, section_type, section_link, duration, pub_date, free_trail, chapter_id) VALUES (6, 1, 0, '2019-07-14 14:04:52.895855', '2019-07-14 14:04:52.895890', 'Linux的环境搭建', 1, 2, NULL, NULL, '2019-07-14 14:04:52.895942', 1, 4);
    ```
    上线相关
  • 相关阅读:
    如何使用maven进行avro序列化
    CDH搭建和集成spark、kafka操作
    spark批量写写数据到Hbase中(bulkload方式)
    Hbase服务报错:splitting is non empty': Directory is not empty
    关于java中的伪共享的认识和解决
    一次流式处理的submit
    Hbase的写入负载均衡
    基于bs4库的HTML标签遍历方法
    BeautifulSoup库的基本元素
    Requests的基本使用
  • 原文地址:https://www.cnblogs.com/wyf20190411-/p/11681473.html
Copyright © 2020-2023  润新知