美文网首页
一、Django 项目搭建梳理

一、Django 项目搭建梳理

作者: battleMonkey | 来源:发表于2019-03-20 15:02 被阅读0次

    一、 基础搭建


    1.1 新建项目

    $  django-admin startproject BookManager_own
    

    1.2 新建子应用app

    $  cd BookManager_own
    $  python manage.py startapp books
    

    1.3 配置pycharm解释器环境

    1.4 注册子应用

    $ vim BookManager_own/setting.py
    
    INSTALLED_APPS = [
    ...
        ###########  注册app子应用  ################
        'books.apps.BooksConfig',
    ]    
    

    1.5 在 子应用 views 视图 添加 -> 类视图: BookInfo(View)

    from django.views import View
    
    
    class BooksInfoView(View):          # 继承View类( 记得 "V" 需要大写) 
        pass                            # 先添加好 随后 再补充功能
    

    1.6 在子应用中 新建urls.py, 并添加以下内容:

    from books import views
    from django.conf.urls import url
    
    urlpatterns = [
        url(r'^books/$', views.BooksListView.as_view()),    # 路径结尾 记得 加 "/"
    ]                                                       # 记得 最后的 .as_view()
    

    1.7 配置 项目 urls.py

    from django.conf.urls import url, include
    from django.contrib import admin
    
    import books
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        #######  配置子应用 路由  ########
        url(r'^', include("books.urls")),       # 匹配所有
        
        # url(r'^', include(books.urls)),       # 错误示范, 记得 include 加引号
    ]
    

    二、 优化

    2.1 暂时关闭 csrf 验证(修改setting.py 中的中间件):

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

    2.2 查看 主路由(可选)

    ROOT_URLCONF = 'BookManager_own.urls'
    

    2.3 查看 templates配置(可选)

    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [],
            
            # 如配置 需 在根目录新建templates文件夹,此次项目 就不配置了
            # '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',
                ],
            },
        },
    ]
    

    2.4 配置数据库(按照自己需求 修改用户名, 密码…)

    # DATABASES = {
    #     'default': {
    #         'ENGINE': 'django.db.backends.sqlite3',
    #         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    #     }
    # }
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '127.0.0.1', # 数据库主机
            'PORT': 3306,        # 数据库端口
            'USER': 'root',      # 数据库用户名
            'PASSWORD': 'mysql', # 数据库用户密码
            'NAME': 'own_books'  # 数据库名字
        }
    }
    

    2.5 配置 项目支持 pymysql(编辑 project的init)

    • 记得配置
    • 记得配置
    • 记得配置

    否则迁移会报错, 提示没有 pymysql 包

    import pymysql
    
    pymysql.install_as_MySQLdb()
    

    创建数据库:

    mysql> create database own_books charset utf8;
    

    2.6 时区/语言本地化配置

    # LANGUAGE_CODE = 'en-us'
    LANGUAGE_CODE = 'zh-Hans'
    
    # TIME_ZONE = 'UTC'
    TIME_ZONE = 'Asia/Shanghai'
    

    三、 models模型类 配置

    3.1.1 配置 子应用下的 models.py

    from django.db import models
    
    # Create your models here.
    
    # 准备书籍列表信息的模型类
    class BookInfo(models.Model):
        # 创建字段,字段类型...
        name = models.CharField(max_length=20, verbose_name='名称')
        pub_date = models.DateField(verbose_name='发布日期',null=True)
        readcount = models.IntegerField(default=0, verbose_name='阅读量')
        commentcount = models.IntegerField(default=0, verbose_name='评论量')
        is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
        image = models.ImageField(upload_to='book', null=True, verbose_name='图片')
    
        class Meta:
            db_table = 'bookinfo'  # 指明数据库表名
            verbose_name = '图书'  # 在admin站点中显示的名称
    
        def __str__(self):
            """定义每个数据对象的显示信息"""
            return self.name
    
    
    # 准备人物列表信息的模型类
    class PeopleInfo(models.Model):
        GENDER_CHOICES = (
            (0, 'male'),
            (1, 'female')
        )
        name = models.CharField(max_length=20, verbose_name='名称')
        gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
        description = models.CharField(max_length=200, null=True, verbose_name='描述信息')
        book = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # 外键
        is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
    
        class Meta:
            db_table = 'peopleinfo'
            verbose_name = '人物信息'
    
        def __str__(self):
            return self.name
    

    3.2 进行数据库迁移

    python manage.py makemigrations
    python manage.py migrate
    

    3.3 创建管理员/修改密码

    python manage.py createsuperuser            # 创建 管理员用户
    python manager.py changepassword 用户名      # 修改 用户密码
    

    3.4 在子应用的 <admin.py> 注册模型类

    from django.contrib import admin
    
    # Register your models here.
    
    from books.models import BookInfo, PeopleInfo
    
    # 注册站点,实现后台管理
    admin.site.register(BookInfo)
    admin.site.register(PeopleInfo)
    

    3.5 在mysql中, 添加测试数据

    mysql> use own_books;
    
    insert into bookinfo(name, pub_date, readcount,commentcount, is_delete) values
    ('射雕英雄传', '1980-5-1', 12, 34, 0),
    ('天龙八部', '1986-7-24', 36, 40, 0),
    ('笑傲江湖', '1995-12-24', 20, 80, 0),
    ('雪山飞狐', '1987-11-11', 58, 24, 0);
    
    insert into peopleinfo(name, gender, book_id, description, is_delete)  values
        ('郭靖', 1, 1, '降龙十八掌', 0),
        ('黄蓉', 0, 1, '打狗棍法', 0),
        ('黄药师', 1, 1, '弹指神通', 0),
        ('欧阳锋', 1, 1, '蛤蟆功', 0),
        ('梅超风', 0, 1, '九阴白骨爪', 0),
        ('乔峰', 1, 2, '降龙十八掌', 0),
        ('段誉', 1, 2, '六脉神剑', 0),
        ('虚竹', 1, 2, '天山六阳掌', 0),
        ('王语嫣', 0, 2, '神仙姐姐', 0),
        ('令狐冲', 1, 3, '独孤九剑', 0),
        ('任盈盈', 0, 3, '弹琴', 0),
        ('岳不群', 1, 3, '华山剑法', 0),
        ('东方不败', 0, 3, '葵花宝典', 0),
        ('胡斐', 1, 4, '胡家刀法', 0),
        ('苗若兰', 0, 4, '黄衣', 0),
        ('程灵素', 0, 4, '医术', 0),
        ('袁紫衣', 0, 4, '六合拳', 0);
        
    

    四、配置一个视图 测试项目配置 是否正确

    4.1 配置测试view视图(修改子应用的 views.py )

    from django.http import HttpResponse
    
    
    def testView(request):                  # 记得 写 request 形参啊!!!
        return HttpResponse('Hello Django!')
    

    4.2 修改 子应用的 urls.py

    from books import views
    from django.conf.urls import url
    
    urlpatterns = [
        url(r'^books/$', views.BooksListView.as_view()),
        ######### 添加测试路由 #########
        url(r'^test/$', views.testView),
    ]
    
    

    五、配置 使用装饰器(在 views.py操作)

    5.1 定义一个 装饰器, 判断 用户是否登陆

    def login_require(func_view):
        def wrapper(request, *args, **kwargs):
    
            # 定义一个 <status> 的参数, 接收 前端 发来的数据
            # 前端 可通过 url传参,将 状态 发送过来
            #  eg: http://127.0.0.1:8000/books/?status=True 
            status = request.GET.get("status")
          
            if status:
                return func_view(request, *args, **kwargs)
            else:
                return HttpResponse("请先登陆!", status=401)
    
        return wrapper
    

    5.2 构造Mixin扩展类

    # 定义一个类 LoginRequireMixin 
    class LoginRequireMixin(object):
    
        # 定义 类方法  (类方法:不需要 <创建实例对象> 就可以 <直接调用>)
        @classmethod
        # 重写 as_view 方法, 继承顺序 为 调用这个类方法的 类 的 __mro__ 顺序,
        # 即 下面的  BooksListView的 __mro__
        # __mro__:
        #       <1:BooksListView>
        #       <2:LoginRequireMixin>
        #       <3:View>
        #       <4:object>
        def as_view(cls,**kwargs):
            
            # 根据 <1:BooksListView> 的__mro__, super继承 <3:View> 类里的 as_view 方法
            view = super().as_view(**kwargs)
            
            # 添加 上面定义的 装饰器
            view = login_require(view)
            
            # 返回 view 视图对象
            return view
    
    
    
    # 多继承: 继承 LoginRequireMixin 和 View,  在url中,有请求访问时 被调用
    class BooksListView(LoginRequireMixin,View):    
    
        def get(self,request):
            return HttpResponse("OK")
    
        def post(self,request):
            return HttpResponse("OK")
    

    六、需求分析

    6.1 结合需求分析如下(符合restful风格)

    """
    ############  图书管理系统  #############
    
    获取所有书籍      GET         books/
    新增书籍         POST         books/
    
    获取一本书籍      GET         books/id/
    修改一本书籍      PUT         books/id/
    删除一本书籍      DELETE      books/id/
    
    #######################################
    """
    

    6.2 定义 子应用 urls.py:

    urlpatterns = [
        url(r'^test/$', views.testView),
        url(r'^books/$', views.BooksListView.as_view()),        # books/
        url(r'^books/(?P<id>\d+)/$', views.booksView.as_view()), # books/id/
    ]
    

    6.3 编写子应用视图

    class BooksListView(LoginRequireMixin, View):
    
        def get(self, request):
            pass
    
        def post(self, request):
            pass
    
    
    class booksView(LoginRequireMixin, View):
    
        def get(self, request):
            pass
    
        def put(self, request):
            pass
    
        def delete(self, request):
            pass
    

    6.4 优化model, 定义转字典的方法( ^_^ 加鸡腿,超好用):

    class BookInfo(models.Model):
        
        """省略"""
        # 定义字典方法,转换对象 为字典  obj -> {name:xx, pub_date:xxx ...}
        def to_dict(self):
            data_dic = {
                "name": self.name,
                "pub_date": self.pub_date,
                "readcount": self.readcount,
                "commentcount": self.commentcount,
                
                # 获取书籍关联的 所有 <人物对象>,  并转换为字典
                "people": [item.to_dict() for item in self.peopleinfo_set.all()]    
            }
            return data_dic
        
        
    class PeopleInfo(models.Model):
        """省略"""
        # 定义字典方法,转换对象 为字典  obj -> {...}
        def to_dict(self):
            data_dic = {
                "name": self.name,
                "gender": self.gender,
                "description": self.description,
    
                # 将 人物对应的 书籍对象 的 <name值> 取出来
                "book": self.book.name, 
            }
            return data_dic
    
    • 注意: 这里小心递归错误:由书籍查到人物对象, 又由人物对象 查到 书籍对象, 造成无限递归

    RecursionError: maximum recursion depth exceeded
    递归错误:超过最大递归深度

    递归错误

    6.5 编写 书籍类视图

    • 路由错误:
      > r'^books/(?P<id>\d+ / )$' ---> 错误
      最后的 " / " 应该放在括号外面,否则 会导致 后端 接收到形参 id = '1/', 而不是 预期的 id = 1, 导致 根据 id 查询 对应书籍对象 失败.
    路由错误
    class BooksListView(LoginRequireMixin, View):
       # 获取所有书籍 GET  books/
       def get(self, request):
           try:
               books_obj = BookInfo.objects.all()
           except Exception as e:
               return HttpResponse(e, status=400)
    
           books = [item.to_dict() for item in books_obj]
           content = {
               "message": "OK",
               "messno": 0,
               "books": books
           }
           return JsonResponse(content)
    
       # 新增书籍  POST   books/
       def post(self, request):
           try:
               data_dic = json.loads(request.body.decode())
           except Exception as e:
               return HttpResponse(e, status=400)
    
           book = BookInfo()
           book.name = data_dic.get("name")
           book.pub_date = data_dic.get("pub_date")
           book.readcount = data_dic.get("readcount")
           book.commentcount = data_dic.get("commentcount")
           try:
               book.save()
           except Exception as e:
               return HttpResponse(e, status=400)
           content = {
               "message": "OK",
               "messno": 0,
               "data": book.to_dict()
           }
           return JsonResponse(content)
    
    
    class booksView(LoginRequireMixin, View):
    
       # 获取一本书籍 GET books/id/
       def get(self, request, id):
           try:
               book = BookInfo.objects.get(id=id)
           except Exception as e:
               return HttpResponse(e, status=400)
           if not id:
               return JsonResponse({"message": "不存在该书籍"}, status=400 )
    
           content = {
               "message": "OK",
               "messno": 0,
               "data": book.to_dict()
           }
    
           return JsonResponse(content)
    
       # 修改一本书籍 PUT books/id/
       def put(self, request, id):
           try:
               book = BookInfo.objects.get(id=id)
           except Exception as e:
               return HttpResponse(e, status=400)
           if not id:
               return JsonResponse({"message": "不存在该书籍"}, status=400)
    
           # 是否 可以 -> 继承 类BooksListView, 使用他的 post 增加方法 ???
           # self.post(request)  如何处理 book对象的使用问题
    
           try:
               data_dic = json.loads(request.body.decode())
           except Exception as e:
               return HttpResponse(e, status=400)
    
           book.name = data_dic.get("name")
           book.pub_date = data_dic.get("pub_date")
           book.readcount = data_dic.get("readcount")
           book.commentcount = data_dic.get("commentcount")
           try:
               book.save()
           except Exception as e:
               return HttpResponse(e, status=400)
           content = {
               "message": "OK",
               "messno": 0,
               "data": book.to_dict()
           }
           return JsonResponse(content)
    
    
       # 删除一本书籍 DELETE books/id/
       def delete(self, request, id):
           try:
               book = BookInfo.objects.get(id=id)
           except Exception as e:
               return HttpResponse(e, status=400)
           if not id:
               return JsonResponse({"message": "不存在该书籍"}, status=400)
    
           try:
               mess = book.delete()
           except Exception as e:
               return HttpResponse(e, status=400)
    
           content = {
               "message": "OK",
               "messno": 0,
               "data": mess
           }
    
           return JsonResponse(content)
    

    存在问题:

    • 代码冗余, 由于是练习, 暂时就不优化了
    • 返回状态码, 有问题,
    • 返回数据格式不统一

    6.6 优化 返回状态码(定义一个 response_code.py包)

    # coding:utf-8
    
    class RET:
        OK                  = "0"
        DBERR               = "4001"
        NODATA              = "4002"
        DATAEXIST           = "4003"
        DATAERR             = "4004"
        SESSIONERR          = "4101"
        LOGINERR            = "4102"
        PARAMERR            = "4103"
        USERERR             = "4104"
        ROLEERR             = "4105"
        PWDERR              = "4106"
        REQERR              = "4201"
        IPERR               = "4202"
        THIRDERR            = "4301"
        IOERR               = "4302"
        SERVERERR           = "4500"
        UNKOWNERR           = "4501"
    
    error_map = {
        RET.OK                    : u"成功",
        RET.DBERR                 : u"数据库查询错误",
        RET.NODATA                : u"无数据",
        RET.DATAEXIST             : u"数据已存在",
        RET.DATAERR               : u"数据错误",
        RET.SESSIONERR            : u"用户未登录",
        RET.LOGINERR              : u"用户登录失败",
        RET.PARAMERR              : u"参数错误",
        RET.USERERR               : u"用户不存在或未激活",
        RET.ROLEERR               : u"用户身份错误",
        RET.PWDERR                : u"密码错误",
        RET.REQERR                : u"非法请求或请求次数受限",
        RET.IPERR                 : u"IP受限",
        RET.THIRDERR              : u"第三方系统错误",
        RET.IOERR                 : u"文件读写错误",
        RET.SERVERERR             : u"内部错误",
        RET.UNKOWNERR             : u"未知错误",
    }
    
    

    6.7 人物 视图类 (略)

    相关文章

      网友评论

          本文标题:一、Django 项目搭建梳理

          本文链接:https://www.haomeiwen.com/subject/vcsvmqtx.html