Python3 Django的补充

Python3 Django的补充


本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/Rozol/article/details/79526637


以下代码以Python3.6.1为例
Less is more!
Python 3.6.1
Django 2.0.2
django-tinymce==2.7.0
django-redis-cache==1.7.1
django-haystack==2.8.0
whoosh==2.7.4
jieba==0.39
celery==4.1.0
celery-with-redis==3.0
django-celery==3.2.2
uwsgi==2.0.17
项目名: Django_templates 应用名: booktest

静态文件

  • 静态文件: js / css / 图片
  • 配置settings:py:

    STATIC_URL = '/static/'
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static'),
    ]
    
  • 路径选择: (STATIC_URL = '/static/')

    • 选择一: 项目下 √
      • STATICFILES_DIRS中路径配置为os.path.join(BASE_DIR, 'static'),
      • 使用:
        • 全路径: /static/booktest/1.jpg
        • 标签生成路径: booktest/1.jpg
    • 选择二: 应用下
      • STATICFILES_DIRS中路径配置为os.path.join(BASE_DIR, 'booktest.static'),
      • 使用:
        • 全路径: /static/1.jpg
        • 标签生成路径: 1.jpg
  • 创建文件夹:
    • 项目下创建static文件夹
    • static下创建应用名booktest文件夹
  • STATIC_URL: 逻辑路径
    • STATIC_URL = '/static/' 时, 路径为 <img src="/static/booktest/1.jpg">
    • STATIC_URL = '/img/' 时, 路径为 <img src="/img/booktest/1.jpg">
  • STATICFILES_DIRS: 物理路径
    • 文件夹名可以与STATIC_URL同名, 也可不同名
    • 文件夹路径添加到列表
  • 路径
    • 方式一: 全路径(静态)
      • <img src="/img/booktest/1.jpg">
    • 方式二: 标签(动态生成) √
      • 写第一行: {% load static from staticfiles %}
      • <img src="{% static 'booktest/1.jpg' %}" />

上传图片

  • 配置路径: MEDIA_ROOT = os.path.join(BASE_DIR,"static/media")
  • 创建文件夹:
    • 在static文件夹下创建media文件夹
  • 上传图片

    • 方式一: 通过表单上传

      • 图片上传表单:

        <!-- 方式一: 通过表单上传文件 -->
        <form method="post" action="upload" enctype="multipart/form-data">
            {% csrf_token %}
            <input type="text" name="title"><br>
            <input type="file" name="pic"/><br>
            <input type="submit" value="上传">
        </form>
      • 图片接收并保存:

        from django.conf import settings
        
        # 接收图片
        
        def upload(request):
            if request.method == "POST":
                # 接收图片
                file = request.FILES['pic']
        
                # 保存图片
                file_name = os.path.join(settings.MEDIA_ROOT, file.name)  # 图片路径
                with open(file_name, 'wb') as pic:
                    for b in file.chunks():
                        pic.write(b)
        
                return HttpResponse('<img src="/static/media/{}"/>'.format(file.name))
            else:
                return HttpResponse('图片接收错误')
    • 方式二: 移动端上传

      • 以Android系统, okhttp3为例

        // --- Android的代码 ---
        // 混合类型(Multipart)
        // python django2 可用
        // java spring 可用
        // RequestBody构建方式一
        RequestBody mbody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("media/type"), file))
                .addFormDataPart("key1", "value1")
                .addFormDataPart("key2", "value2")
                .build();
        
        // RequestBody构建方式二
        RequestBody mbody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"key1\""),
                        RequestBody.create(null, "value1"))
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"key2\""),
                        RequestBody.create(null, "value2"))
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"file\"; filename =\"" + file.getName() + "\""),
                        RequestBody.create(MediaType.parse("media/type"), file))
                .build();
        Request request = new Request.Builder().url(url).post(mbody).build();
        
        new OkHttpClient().newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) { }
        
            @Override
            public void onResponse(Call call, Response response) throws IOException { }
        });
        
        # --- django2的代码 ---
        
        
        # 从移动端接收图片, 移动端使用okhttp库post请求
        
        def uoload_mobile(request):
            http = HttpResponse()
        
            if request.method == "POST":
                # 接收图片
                file = request.FILES['file']
        
                # 保存图片
                file_name = os.path.join(settings.MEDIA_ROOT, file.name)  # 图片路径
                with open(file_name, 'wb') as pic:
                    for b in file.chunks():
                        pic.write(b)
        
                print('key1: {}'.format(request.POST['key1']))
                print('key2: {}'.format(request.POST['key2']))
        
                http.status_code = 200
                http.write('ok')
            else:
                http.status_code = 400
                http.write('err')
        
            return http

分页

# 分页
def pagination(request, index_page):
    books = BookInfo.objects.all()

    '''
    Paginator
    属性:
        count: item总条数
        num_pages: 总页数
        page_range: 页码, range对象
    方法:
        page(num): 指定页, 页码不存在抛InvalidPage异常, 页码非整数抛PageNotAnInteger异常, 页码无页面抛EmptyPage异常

    '''
    # 分页(一次取几页)
    list = Paginator(books, 3)

    '''
    Page: 由Paginator.page()返回的Page对象, 可迭代对象
    属性:
        object_list: 当前页所有对象的列表
        number: 当前第几页 (非item序号)
        paginator: 当前page对象的Paginator对象
    方法:
        has_next(): 是否有下一页
        has_previous(): 是否有上一页
        has_other_pages(): 是否还有其他页
        next_page_number(): 下一页的页码, 不存在抛InvalidPage异常
        previous_page_number(): 上一页的页码, 不存在抛InvalidPage异常
        len(): 当前页面item个数
    '''
    # 单页(一页有多个条目)
    pages = list.page(int(index_page))

    context = {
        'books': pages, 'page_sum': list.num_pages, 'page': pages.number,
    }
    return render(request, 'pagination.html', context)

Ajax

  • 数据通过’get’请求获取, 而非直接让Django生成在模板里, 这能降低耦合
  • 省市区三级联动案例:

    
    # view层处理
    
    
    # 省市区选择测试
    
    def areatest(request):
        return render(request, 'areatest.html')
    
    # 省
    
    def pro(request):
        prolist = AreaInfo.objects.filter(area_parent__isnull=True)
        list = []
        for item in prolist:
            list.append([item.id, item.area_title])  # 采用列表 [[111, 'xxx'],[112, 'xxx']]
        return JsonResponse({'data': list})
    
    # 市
    
    def city(request, id):
        citylist = AreaInfo.objects.filter(area_parent_id=id)
        list = []
        for item in citylist:
            list.append({'id': item.id, 'title': item.area_title})  # 采用字典 [{'id'= 111, 'title'= 'xxx'},{'id'= 112, 'title'= 'xxx'}]
        return JsonResponse({'data': list})
    
    # html页ajax编写
    
    <head>
        <title>Title</title>
        <script src="/static/jquery-1.12.4.min.js"></script>
        <script>
            $(function () { // 执行方法
                // 使用ajax异步加载省信息
                $.get('/booktest/pro', function (list) { // get请求, 并回调
                    pro = $('#pro')
                    // 列表, {data: [[111, 'xxx'][112, 'xxx']]}
                    $.each(list.data, function(index, item) {
                        // <option value="111">xxx</option>
                        pro.append('<option value="' + item[0] + '">' + item[1] + '</option>')
                    });
                })
    
                // 查询市的信息
                $('#pro').change(function () { // 绑定pro改变事件
                    $.get('/booktest/city' + $(this).val(), function (list) { // this是pro的option
                        city = $('#city');
    
                        // 重置
                        city.empty().append('<option value="0">请选择市</option>')
                        $('#dis').empty().append('<option value="0">请选择区县</option>')
    
                        // 字典, {data: [{'id'= 111, 'title'= 'xxx'},{'id'= 112, 'title'= 'xxx'}]}
                        $.each(list.data, function(index, item) {
                            // <option value="111">xxx</option>
                            city.append('<option value="' + item.id + '">' + item.title + '</option>');
                        });
                    });
                });
    
                //查询区县的信息
                $('#city').change(function() {
                    $.get('/booktest/city' + $(this).val(), function(list) {
    
                        // 重置
                        dis = $('#dis').empty().append('<option value="0">请选择区县</option>');
    
                        // {data: [{'id'= 111, 'title'= 'xxx'},{'id'= 112, 'title'= 'xxx'}]}
                        $.each(list.data, function(index, item) {
                            // <option value="111">xxx</option>
                            dis.append('<option value="' + item.id + '">' + item.title + '</option>');
                        });
                    });
                });
            });
        </script>
    </head>
    <body>
    <select id="pro">
        <option value="0">请选择省</option>
    </select>
    <select id="city">
        <option value="0">请选择市</option>
    </select>
    <select id="dis">
        <option value="0">请选择区县</option>
    </select>
    </body>

富文本编辑 (安装包: django-tinymce==2.7.0)

  • 使用`django-tinymce==2.7.0为例
  • setting.py配置

    • INSTALLED_APPS列表添加 'tinymce',
    • 添加 (展示于admin)

      TINYMCE_DEFAULT_CONFIG = {
          'theme': 'advanced',
          'width': 600,
          'height': 400,
      }
      
    • 项目下urls.py添加 path('tinymce/', include('tinymce.urls')),

  • models.py模型(可在admin界面使用)

    from django.db import models
    from tinymce.models import HTMLField
    
    class BookInfo(models.Model):
        book_name = models.CharField(max_length=10)
        book_content = HTMLField()
    
        def __str__(self):
            return self.book_name
  • html页面配置:

    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    
        <!-- 富文本编辑器js脚本 -->
        <script type="text/javascript" src='/static/tiny_mce/tiny_mce.js'></script>
        <script type="text/javascript">
            tinyMCE.init({
                'mode':'textareas',
                'theme':'advanced',
                'width':400,
                'height':100
            });
        </script>
    
    </head>
    <body>
    
        <form method="post" action="content">
            {% csrf_token %}
            <input type="text" name="book_name">
            <br>
            <!-- textarea 将被替换成富文本编辑器 -->
            <textarea name='book_content'>内容多行文本框</textarea>
            <br>
            <input type="submit" value="提交">
        </form>
    
    </body>
  • 通过post传递数据:

    def html_content(request):
        name = request.POST['book_name']
        content = request.POST['book_content']
    
        book = BookInfo()
        book.book_name = name
        book.book_content = content
        book.save()
    
        return render(request, 'html_content.html', {'book_content': book.book_content})

缓存 (选装包: django-redis-cache==1.7.1)

  • Django具有不同级别的缓存粒度
  • 缓存被用于缓存
    • 需要计算的程序
    • 不变的数据和模板
  • settings.py配置:

    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'TIMEOUT': 60,  # 默认300s, 永久None, 立即失效0
        }
    }
    
  • 可以缓存到django-redis-cache (默认库1)

    CACHES = {
        "default": {
            "BACKEND": "redis_cache.cache.RedisCache",
            "LOCATION": "localhost:6379",
            'TIMEOUT': 60,
        },
    }
    
    • redis命令(cd到redis安装目录):
      • 启动: redis-server redis.windows.conf
      • 连接: 运行redis-cli
      • 切换数据库: select 1
      • 查看键: keys *
      • 查看值: get [键]
      • 删除数据: del [键/*]
  • 缓存指定数据:

    • 指定views视图:

      from django.views.decorators.cache import cache_page
      
      @cache_page(60)  # 缓存指定视图
      def index(request):
          return render(request, 'index.html')
    • 指定模板片段:

      <!-- 写第一行-->
      {% load cache %}
      
      <!-- 缓存指定位置的模板碎片(可缓存DTL/HTML) -->
      {% cache 60 cache_name %}
      
      <img src="{% static '1.jpg' %}" />
      
      {% endcache %}
    • 指定值:

      from django.core.cache import cache
      
      # 缓存指定值
      
      def cachetest(request):
      
          # 设置缓存
          cache.set('key', 'value', 600)
          # # 获取缓存
          cache_content = cache.get('key')
          # 删除缓存
          cache.delete('key')
          # 清空缓存
          cache.clear()
      
          return HttpResponse('缓存: {}'.format(cache_content))

中文分词全文检索 (安装包: django-haystack==2.8.0 / whoosh==2.7.4 / jieba==0.39)

  • 比 字段模糊查询 效率高, 并能对中文进行分词
  • django-haystack: Django的包, 通过whoosh, solr, Xapian, Elasticsearc四种全文检索引擎 检索model里的内容
    • whoosh: 全文检索引擎, 全python写的, 性能一般, 用于中小型网站
  • jieba: 中文分词包, 效果一般, 免费
  • settings.py配置

    • 列表中添加 INSTALLED_APPS'haystack',
    • 添加

      # 搜索引擎
      HAYSTACK_CONNECTIONS = {
          'default': {
              'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
              'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
          }
      }
      
    • 添加

      # 自动更新 whoosh_index 索引
      HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
      
  • 项目urls.py中添加 path('search', include('haystack.urls')),

  • 应用下创建search_indexes.py文件

    from haystack import indexes
    from .models import BookInfo
    
    
    class BookInfoIndex(indexes.SearchIndex, indexes.Indexable):
        # 将来将对这种字段进行检索
        text = indexes.CharField(document=True, use_template=True)
    
        # 获取模型对象
        def get_model(self):
            return BookInfo
    
        # 对哪些数据进行检索
        def index_queryset(self, using=None):
            return self.get_model().objects.all()  # 检索所有数据
    
  • 目录templates/search/indexes/booktest[应用名称]/下创建BookInfo[模型类名]_text.txt文件

    # 列出要对哪些字段进行检索
    {{ object.book_name }}
    {{ object.book_content }}
    
  • 在目录templates/search/下创建search.html文件

    {% if query %}
        <h3>搜索结果如下: </h3>
        {% for result in page.object_list %}
            {{ result.object.id }} {{ result.object.book_name }} {{ result.object.book_content }}
            <br/>
        {% empty %}
            <p>没有检索到内容</p>
        {% endfor %}
    
        <!-- 分页 -->
        {% if page.has_previous or page.has_next %}
            <div>
                {% if page.has_previous %}<a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">{% endif %}&laquo; 上一页{% if page.has_previous %}</a>{% endif %}
            |
                {% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}下一页 &raquo;{% if page.has_next %}</a>{% endif %}
            </div>
        {% endif %}
    {% endif %}
    
  • haystack安装目录python\Lib\site-packages\haystack\backends\下创建ChineseAnalyzer.py文件

    import jieba
    from whoosh.analysis import Tokenizer, Token
    
    
    class ChineseTokenizer(Tokenizer):
        def __call__(self, value, positions=False, chars=False,
                     keeporiginal=False, removestops=True,
                     start_pos=0, start_char=0, mode='', **kwargs):
            t = Token(positions, chars, removestops=removestops, mode=mode,
                      **kwargs)
            seglist = jieba.cut(value, cut_all=True)
            for w in seglist:
                t.original = t.text = w
                t.boost = 1.0
                if positions:
                    t.pos = start_pos + value.find(w)
                if chars:
                    t.startchar = start_char + value.find(w)
                    t.endchar = start_char + value.find(w) + len(w)
                yield t
    
    
    def ChineseAnalyzer():
        return ChineseTokenizer()
    
  • haystack安装目录复制whoosh_backend.py文件, 改名为whoosh_cn_backend.py

    • 导入 from .ChineseAnalyzer import ChineseAnalyzer
    • 查找 analyzer=StemmingAnalyzer() 替换为 analyzer=ChineseAnalyzer()
  • 生成索引python manage.py rebuild_index

    • 向数据库中添加数据会自动生成索引, 不用执行命令
  • 模板中创建搜索栏

    <!-- 搜索栏 -->
    {% comment %}
        action="/search/" // 自动匹配到localhost/search/
    {% endcomment %}
    <form method='get' action="/search/" target="_blank">
        {% csrf_token %}
        <input type="text" name="q">
        <input type="submit" value="查询">
    </form>
    

Celery 分布式任务队列 (安装: celery==4.1.0 / celery-with-redis==3.0 / django-celery==3.2.2)

  • 用于解决耗时操作 和 定时任务
  • 名词解析:
    • task: 任务, 用于封装耗时操作
    • queue: 队列, 存放任务
    • worker: 工人, 在新进程中, 执行队列中的任务
    • broker: 代理人, 把任务加入队列中
  • 配置settings.py

    • INSTALLED_APPS列表中添加 'djcelery',
    • 添加

      import djcelery
      djcelery.setup_loader()
      BROKER_URL = 'redis://127.0.0.1:6379/0'
      CELERY_IMPORTS = ('booktest[应用名].task')  # 将耗时任务定义到该task文件里
      
    • 在应用下创建task.py文件

      • 封装任务: 用@task语法糖注解

        import time
        from celery import task
        
        @task
        def tasktest(par):
            print('hello')
            time.sleep(10)
            print('world')
        
    • 迁移数据 python manage.py migrate

    • 启动redis
    • 启动worker python manage.py celery worker --loglevel=info
    • 调用:

      def celery(request):
          # celery调用耗时任务
          tasktest.delay('参数')
          return HttpResponse('hello!!!')
      

布署到服务器

  • 收集虚拟环境所有安装的python包:
    • cd到项目根目录下, 执行命令 pip freeze > plist.txt
  • 将项目传到服务器
  • 安装虚拟环境
  • 安装需要的python包:
    • cd到指定目录下, 执行 pip install -r plist.txt
  • 更改settings.py配置:

    DEBUG = False
    ALLOW_HOSTS=['*',]表示可以访问服务器的ip
    
  • 启动服务器 (注: 使用runserver运行的服务器, 静态文件在生产环境是无法访问的)

服务器 (安装: uwsgi==2.0.17)

  • uWSGI

    • 开发环境: python manage.py runserver = 生成环境: WSGI(协议)
    • 实现WSGI的服务器
    • 安装: pip install uwsgi==2.0.17
    • 配置:

      • 项目下创建usgi.ini文件
      • 编写(使用要去掉注释)

        [uwsgi]
        #socket = 127.0.0.1:8000  # 外网ip:端口 (使用nginx连接时, 使用socket)
        http = 127.0.0.1:8000  # 外网ip:端口 (直接做web服务器, 使用http)
        chdir = C:\Code\Python_Vir\Django_other  # 项目根目录
        wsgi-file = Django_other\wsgi.py  # 项目中wsgi.py文件的目录, 相对于项目根目录
        processes = 2  # 运行进程数
        threads = 2  # 每个进程的线程数
        master = True
        pidfile = uwsgi.pid
        daemonize = uwsgi.log
        
    • 命令

      • 启动: uwsgi --ini uwsgi.ini
        • 查看是否启动 ps ajx|grep uwsgi
      • 停止: uwsgi --stop uwsgi.pid
      • 重启: uwsgi --reload uwsgi.pid
  • nginx

    • 作用:
      • 负载均衡: 多态服务器轮流处理请求
      • 反射代理: 隐藏真实服务器
    • 实现: IE <-> nginx <-> uwsgi <-> django (静态文件: IE <-> nginx)
    • 安装: sudo apt-get install nginx
    • 配置(版本是不同版本的nginx配置不同, 其他配置操作一样):

      • 查看路径:
        • 安装目录: ps -ef | grep nginx
        • 配置文件目录: nginx -t
      • 版本一(默认安装在/usr/local/niginx目录下):

        • 编辑 vim conf/nginx.conf文件, http的server下

          listen              80; # 监听端口 
          server_name    luzhuo.me; # 服务名(域名)
          
          location / {
              include uwsgi_params; # 将所有的参数转到uwsgi下
              uwsgi_pass 127.0.0.1:8000; # uwsgi的ip与端口
          }
          
          location /static {
              alias /var/www/django[项目名]/static/;
          }
          
      • 版本二(默认安装在/etc/nginx目录下)

        • 编辑 vim /etc/nginx/sites-enabled/default 文件, server下

          # 注释掉, 另外一个listen...
          listen 外网ip:80 default_server  # 端口 服务名
          
          location / {
              # 注释掉, try_files ...
              include uwsgi_params; # 将所有的参数转到uwsgi下
              uwsgi_pass 127.0.0.1:8000; # uwsgi的ip与端口
          }
          
          location /static {
              alias /var/www/django[项目名]/static/;
          }
          
      • cd /var下创建目录/var/www/django/static

        • 修改权限 sudo chmod 777 static
        • 所有静态文件都放到static文件夹中
      • 修改uwsgi.ini文件
        • 使用socket
        • 禁用http
      • 修改settings.py文件

        STATIC_URL='/static/'
        STATIC_ROOT = '/var/www/django[项目名]/static/'  # 用于nginx采集静态文件
        
      • 收集项目所有静态文件, 并移到STATIC_ROOT指定目录: python manage.py collectstatic

    • 重启nginx, uwsgi (注意顺序)

    • 命令:
      • 版本一(cd到目录/usr/local/niginx目录):
        • 版本: sudo sbin/nginx -v
        • 启动: sudo sbin/nginx
        • 停止: sudo sbin/nginx -s stop
        • 重启: sudo sbin/nginx -s reload
      • 版本二:
        • 版本: sudo nginx -v
        • 启动: sudo nginx
        • 停止: sudo nginx -s stop
        • 重启: sudo nginx -s reload
    • 注意: listen端口为80, location 和 uwsgi.ini 的端口为8000, ip地址均为外网ip

项目实战

  • 应用
    • 商品
    • 用户
      • models:
        • UserInfo
  • 共用页面

    • 页头
    • 页尾
  • 目录选择

    • 模板文件: 应用下templates
    • 静态文件: 项目下static
  • 常用命令

    • 生成迁移: python manage.py makemigrations
    • 执行迁移: python manage.py migrate

问题解答

  • 如何 服务器/虚拟机里的服务器 里的网站?

    • runserver访问: python manage.py runserver 外网ip:端口
      • 访问: http://外网ip:端口
  • 安装uWSGI时报错

    • 先安装 sudo apt-get install python3.6-dev (注意版本号, 直接在虚拟环境安装就可以了)
    • 再安装uWSGI(也安装到虚拟环境)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值