【学习笔记】Django网页开发

Datawhale组队学习第26期:Django网站开发
本次学习的指导老师Tango的个人博客教学视频
本贴为学习记录帖,有任何问题欢迎随时交流~
部分内容可能还不完整,后期随着知识积累逐步完善。
开始时间:2021年6月13日
最新更新:2021年7月11日(更新至Task3编写view

文章目录

一、基础配置

(一)安装好基础软件。

1.Typora

结合Markdown写文章用的,所见即所得,体验感不错。

  • Markdown同样也可以在VS Code上写,同样支持实时预览,需要安装插件Markdown All In OneMarkdown Preview Enhanced
  • 个人习惯在VS Code上,上面也配置了LaTex,方便需要多种公式的写作和排版。

2.VMware和Ubuntu

虚拟机与系统,用于搭建服务器。
个人本机是Windows 10系统,服务器是云ECS服务器,配置的是Ubuntu20.04系统。

3.VSCode

代码编辑器,后面用于远程连接服务器和代码编辑。已安装,但需要配置插件和远程连接。

  • 关于远程连接,使用的是Remote-SSH,会有遇到坑的可能,具体看第三节远程控制那一块内容。
  • 经过组里小伙伴的实践验证,这里不一定需要使用VSCode,可以选用**Pycharm Professionals(专业版)**进行远程控制。其中具有学校邮箱等条件下,可以有一年***的机会,具体到JetBrains官网进行查阅。

4.宝塔

  • 服务器后台管理
  • 既可以用于搭建数据库和设置代理,也可以用于远程服务器的文件管理。

(二)云服务器设置

1.安全组设置

  • 开放22端口,用于远程连接。
  • 开放8888端口,宝塔用的面板端口。
  • 开发3306端口,MySQL服务器端口。

2.用户设置

  • 新增用户并增加管理权限
    $ sudo adduser enguanei
    $ sudo adduser enguanei sudo
    
  • 登录新用户,修改主机名
    sudo hostname ubuntu
    

(三)远程连接

1.VS Code插件安装

打开Visual Studio Code,安装插件:Renite-SSH

2.修改配置

  • VS Code进行远程连接的时候出现“Bad owner or permissions on XXX”的错误
    查资料的时候,发现VSCode和OpenSSH貌似是有冲突。
    经过好几番尝试,最后选用了Git SSH的方案。
  • 把OpenSSH删掉,安装Git。
  • 将Git安装目录下~ Git\bin~ Git\usr\bin加入到系统变量中。
  • 修改VS Code中“Remote-SSH”的设置文件settings.json,在该文件中写入以下内容:
    {
         
      "remote.SSH.path": "Git安装目录\\Git\\usr\\bin\\ssh.exe",
      "remote.SSH.showLoginTerminal": true
    }
    

(四)环境配置

1.定位虚拟环境的位置

  • Desktop下新建文件夹DjangoDev,再进入DjangoDev中新建demoenv文件夹。
  • env用于存储虚拟环境所需要的包,demo用于小文件测试。

2.建立虚拟环境

  • 进入env文件夹,使用python3 -m venv建立虚拟环境
  • 注意:提前使用sudo apt-get install python3-venv进行虚拟环境包安装

3.激活虚拟环境

  • 使用source bin/activate激活虚拟环境
  • 注意:这里使用的是相对路径

4.安装Django

  • 使用pip3 install django --trusted-host mirrors.aliyun.com安装Django。

(五)Python相关的报错注意

报错一:安装ipykernel出现returned non-zero exit status 1

报错原因不清楚,需要进一步查证。
解决方法:参考Tango老师和其他博主的思路。

sudo rm /usr/bin/lsb_release

报错二:Not a trusted or secure host and is being ignored.

修改完后,需要增加原先设置的阿里源存在问题,利用pip安装时需要加上--trusted-host mirrors.aliyun.com才能正常下载,原因阿里源设置时需要用http而不是https。

/usr/bin/python3 /home/enguanei/.vscode-server/extensions/ms-python.python-2021.5.926500501/pythonFiles/pyvsc-run-isolated.py pip install -U ipykernel --user --trusted-host mirrors.aliyun.com
  • 安装完毕后如下所示,左边为脚本环境,右边为交互环境。

  • 在脚本环境下输入#%%可以进入分段交互(挺有趣的知识点)。

(六)HTML的知识点补充

  • 安装插件:HTML Snippets可以用于增强提示。Live Server可以用于本地连接远程的html。

  • 在html界面中更换语言模式,原本是Django html,可以改成html

  • head是定义美观动作,body是组织内容。

  • <tr>表示表行,<td>表示表列。

  • 小实验代码如下:

  • 展示结果:

二、站点设置

(一)添加站点

  • 打开宝塔,这里需要先安装好Nginx和MySQL。

(二)修改保存路径

为了能使用前面设定好的虚拟环境看,修改网页保存的路径

(三)修改权限

  • 不修改权限可能会报错。

三、初始化Django

(一)创建django 项目

    django-admin startproject myDemo

生成的文件如下所示:

(二)修改文件名

为了方便开发,需要对文件名进行调整。

  • 注意不能随意修改文件夹config所依赖的文件名,会报错。

  • 删除无关文件

sudo rm -r -f myDemo/

(三)开启服务

  • 进入服务
python manage.py runserver
  • 创建app
python manage.py startapp blog
  • 修改settings,与新建的blog关联

四、初始化后台管理

(一)创建superuser

  • 创建超级管理员
    设置用户名和密码,用于登陆后台管理。
python manage.py createsuperuser
  • 同步数据库(这一步十分重要!)
python manage.py migrate

(二)登录后台

  • 输入python manage.py runserver可以登进网页(第四部分的“开启服务”)
  • 127.0.0.1:8000后面输入admin可以连上后台管理(url配置文件设定)

(三)后台语言和时区修改

  • 语言修改
    找到config文件夹下的settings.py文件,修改LANGUAGE_CODE
    LANGUAGE_CODE = 'zh-hans'  # en-us 为 英文
    
  • 时区修改
    找到config文件夹下的settings.py文件,修改TIME_ZONE
    TIME_ZONE = 'Asia/Shanghai'
    
  • 修改完毕后,后台管理页面如下所示

(四)博客内容管理

此时的blog模块并未出现相应的内容,需要对blog文件夹下的modelsadmin进行修改。

  • models.py进行修改

  • admin.py进行修改
    将刚写好的blog类导入到admin

  • 同步数据库,否则会报错(No such table
    此时使用的数据库是SQLite

    # 终端上输入
    python manage.py makemigrations
    python manage.py migrate
    
  • 修改完毕后,可以增加blog内容(此时只能修改FILEDNAME)

  • 字段中FILEDNAME和左边列表的Blogs修改为中文
    修改models.py的内容,注意这里的__str__函数的作用在于编辑blog内容的时候,返回的是你创建标题的名字而非object(n)

    class Blog(models.Model):
        FILEDNAME = models.CharField(max_length=255, verbose_name="标题")
        class Meta:
            verbose_name = "博客管理"
    	    verbose_name_plural = verbose_name
    	
    	
        def __str__(self):
        	return self.title
    

    同步数据库(记得每次修改内容时都需要同步数据库)

    # 终端上输入
    python manage.py makemigrations
    python manage.py migrate
    
  • 进一步增加其他字段,如文章内容、阅读次数、创建时间等

    • 继续修改models.py的内容,新建字段一般都会设置默认值,注意IntegerField设置默认值应为数值型的,如0。
    • CharFieldTextFieldIntegerFieldDateTimeField的区别具体可以去查阅官方文档,在运行后也能直观看到各自的区别。
    class Blog(models.Model):
        title = models.CharField(max_length=50, verbose_name="标题", default="")
        content = models.TextField(verbose_name="文章内容", default="")
        count = models.IntegerField(verbose_name="阅读次数", default=0)
        creat_time = models.DateTimeField(
            auto_now=True, verbose_name="创建时间")
    
        class Meta:
            verbose_name = "博客管理"
            verbose_name_plural = verbose_name
    
    • 完成后,同步数据库
  • 显示blog管理中所有文章的信息
    修改admin.py文件

    # 将原来写的内容注释掉,使用装饰器
    # admin.site.register(Blog)
    
    @admin.register(Blog)
    class BlogAdmin(admin.ModelAdmin):
        list_display = ("title", "creat_time", "count")		# 这里的title等是来自于models中的字段
    

    结果如下图所示:

(五)主页面设置

  • 修改blog文件夹下的views.py文件

    • from .models import Blog是从同级目录下的models.py导入Blog(上面写好的)
    • Blog.objects.all()返回的是列表
    • render用于向页面(“index.html”)传递内容(“blogs”)
    # 增加以下内容
    from .models import Blog
    
    # 用于展示博客信息
    def index(request):
    	# blogs = Blog.objects.all() 相当于SQL中的select * from Blog
        blogs = Blog.objects.all().order_by("-creat_time")  # 按时间排序
    	return render(request, "index.html", {
         "blogs": blogs})
    
    # 用于点击获取具体的博客信息
    def read_blog(request, id):
        print(f"{
           id}:我是通过前端点击链接来的")
        blog = Blog.objects.get(id=id)
        return render(request, "content.html", {
         "blog": blog})
    
  • MyBlog文件夹下新建文件夹templates,同时新建网页index.htmlcontent.html该文件夹用于保存网页

  • 修改settings.py文件
    TEMPLATES内容(链接路径)进行修改

    # 修改前先导入os库
    import os
    
    # 修改DIRS,链接到上面创建的文件夹templates
    TEMPLATES = [
        {
         
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, "templates")],
            'APP_DIRS': True,
            'OPTIONS': {
         
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
  • 修改config文件夹下的urls.py
    该文件是用于请求映射,信息传输到浏览器页面上。

    # 额外导入上面写好的 index 函数
    from blog.views import index, read_blog
    
    # 增加 index 和 read_blog 链接路径,其中在后台返回点击的 id 信息
    urlpatterns = [
        path('admin/', admin.site.urls),
        path("", index, name="index"),
        path("read/<int:id>", read_blog, name="read_blog"),
    ]
    
  • 修改主页index.html的页面内容

    <html>
        <head>
            <title>我的首页</title>
        </head>
    
        <body>
            <h1>欢迎来到我的网站!</h1>
            <ul>
                {% for blog in blogs %}
                <li>
                    <a href="{% url 'read_blog' blog.id %}">{
        {blog.title}}</a>
                </li>
                <p>创建于 {
        {blog.creat_time}}</p>
                {% endfor %}
            </ul>
        </body>
    </html>
    
  • 修改子页面content.html的页面内容

    <html>
        <head>
            <title>我的首页</title>
        </head>
    
        <body>
            <a href="/" style="float:right">回到首页</a>
            <h1>{
        {blog.title}}</h1>
    
            <div>
                {
        {blog.content}}
            </div>
        </body>
    </html>
    

五、建立MySQL数据库

(一)初始化MySQL数据库

  • 宝塔上安装MySQL,版本要求是5.7以上(Django官方文档给出的最低要求)
    • 在软件商店上下载
    • 设置好MySQL的账号密码和权限(可以设置为所有人)
  • 虚拟环境中安装pymysql
    pip install pymysql --trusted-host mirrors.aliyun.com
    
  • 修改config文件夹中的settings.py,具体可以参考官方文档
    # 将原有的的DATABASES注释掉
    # DATABASES = {
         
    # 'default': {
         
    # 'ENGINE': 'django.db.backends.sqlite3',
    # 'NAME': BASE_DIR / 'db.sqlite3',
    # }
    # }
    
    DATABASES = {
         
        'default': {
         
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'db_name',
            'USER': 'user_name',
            'PASSWORD': 'pwd',
            'HOST': 'ip',
            'PORT': '3306',
        }
    }
    
  • 修改config文件夹下的__init__.py文件
    import pymysql 
    
    pymysql.install_as_MySQLdb()	# 加载MySQL数据库
    
  • 同步数据库
    # 终端上执行
    python manage.py migrate
    
    • 报错类型中为Can’t connect to MySQL server
      一般是因为防火墙限制,无法远程连接。
      在终端上开放对应端口即可。

      sudo ufw status   # 查看端口号开放情况
      sudo allow 3306   # 开放对应的端口号
      
    • 报错类型中为Access denied for user XXX to database 'blog’
      一般是因为对应用户的db中没有blog
      可以尝试远程连接MySQL看看该用户有哪些数据库有哪些(一般是跟用户名一致),NAME是用户下的数据库中的一个。

    • 更新成功后,登进数据库进行查看

      # 终端执行
      mysql -u user_name -p
      
      show databases;		# 查看有哪些数据库
      use db_name;		# 进入更新的数据库
      show tables;		# 查看该数据库的所有表
      

(二)后台代理设置

1.安装插件

  • 安装supervisor和gunicorn
    # 退出虚拟环境安装supervisor
    sudo apt install supervisor -y	
    
    # 进入虚拟环境安装gunicorn
    pip install gunicorn --trusted-host mirrors.aliyun.com
    

2.配置gunicorn

  • MyBlog的文件夹下新建gunicorn_start.sh,写入以下内容

    • DJANGODIR填入的是django项目路径
    • USERGROUP填入的是用户名
    • source是激活虚拟环境,填入对应虚拟环境的路径
    • exec也是填入虚拟环境的路径
    #!/bin/bash
    
    NAME="MyBlog"
    DJANGODIR=******/MyBlog #Django project directory
    USER=user_name # the user to run as
    GROUP=group_name # the group to run as
    NUM_WORKERS=1 # how many worker processes should Gunicorn spawn
    DJANGO_SETTINGS_MODULE=config.settings # which settings file should Django use
    DJANGO_WSGI_MODULE=config.wsgi # WSGI module name
    
    echo "Starting $NAME as `whoami`"
    
    # Activate the virtual environment
    cd $DJANGODIR
    source ******/env/bin/activate
    export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
    export PYTHONPATH=$DJANGODIR:$PYTHONPATH
    
    # Create the run directory if it doesn't exist
    RUNDIR=$(dirname $SOCKFILE)
    test -d $RUNDIR || mkdir -p $RUNDIR
    
    # Start your Django Unicorn
    # Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
    exec ******/env/bin/gunicorn  ${DJANGO_WSGI_MODULE}:application \
    --name $NAME \
    --workers $NUM_WORKERS \
    --user=$USER --group=$GROUP \
    --log-level=debug \
    --log-file=-
    
  • 在宝塔的文件管理上修改gunicorn_start.sh的权限(777)

  • 终端上执行./gunicorn_start.sh,可以通过网页访问设置好的网站

3.宝塔代理设置

  • 修改config文件夹下的settings.py文件
    DEBUG = False
    ALLOWED_HOSTS = ["*"]
    
  • 修改宝塔的网站配置文件
    增加如下内容
    location / {
    	proxy_set_header X-Real-IP $remote_addr;
    	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    	proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_redirect off;
        if (!-f $request_filename) {
            proxy_pass http://127.0.0.1:8000;
            break;
        }
    }
    

  • 修改完后,在宝塔上对nginx进行重载配置重启

4.nginx代理设置

  • MyBlog文件夹下新建log文件夹,用于存放日志内容。
  • 终端上执行sudo vim /etc/supervisor/conf.d/myblog.conf,进入vim操作。
    按<kbd>i</kbd>进入编辑模式,输入以下内容(******填入对应的路径):
     [program:myblog]
      command = ******/gunicorn_start.sh
      user = server
      autostart=true
      autorestart=true
    
      redirect_stderr = true
      stdout_logfile = ******/log/myblog.log
      stderr_logfile = ******/log/myblog.err
    
  • 终端上更新配置
    sudo supervisorctl update
    sudo supervisorctl reload
    sudo /etc/init.d/nginx restart		# 也可以在宝塔上进行重启Nginx
    
  • 配置完毕
    • 当ngnix启动后,可以直接利用宝塔网站配置文件中监听ip地址进行访问。
    • 通常需要维护网站时,可以将ngnix关闭,外网无法访问。
    • 更新网站内容后,需要执行上面的更新配置。

(三)页面管理

1.后台静态页面丢失问题

上述操作完毕后,登进管理页面,发现显示不正常(CSS和JS丢失):

2.后台静态页面修复

  • 修改settings.py,在STATIC_URL下面增加以下内容:
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, "static")
    ]
    
  • MyBlog文件夹下新建static文件夹,用于存放静态页面内容
  • 执行如下命令,自动在static文件夹下自动生成admin文件夹(包含了CSS和JS)
    python manage.py collectstatic
    
  • 修改宝塔中网站的配置文件
    新增如下内容
    location /static/ {
        alias ******/MyBlog/static/;
        expires max;
        access_log off;
        log_not_found off;
    }
    

六、模块划分

(一)模块分类设计

  • 文章(Post)

    • ID
    • 标题:title
    • 作者:owner
    • 分类(多对一):category多篇文章对应一个分类
    • 标签(多对多):tags
    • 摘要:description
    • 正文:content
    • 状态(草稿、发布、删除):status
    • 发布时间:create_time
  • 分类(Category)

    • ID(Django自动分配)
    • 名称:name
    • 状态:status
    • 作者:owner
    • 创建时间:create_time
    • 是否置顶导航:is_nav
  • 标签(Tag)

    • ID
    • 名称:name
    • 状态:status
    • 作者:owner
    • 创建时间:created_time
  • 友情链接(Link)

    • ID
    • 网站名称:title
    • 链接:href
    • 作者:owner
    • 状态:status
    • 创建时间:created_time
    • 权重:weight
  • 评论(Comment)

    • ID
    • 文章(多对一):post
    • 用户名:nickname
    • 邮箱:email
    • 网站地址:website
    • 内容:content
    • 创建时间:created_time
    • 作者:owner
  • 侧边栏(SideBar)

    • ID
    • 标题:title
    • 类型:type
      • 最新文章
      • 最热文章
      • 最近评论
    • 状态:status
    • 内容:content
    • 创建时间:created_time
    • 作者:owner
  • 用户(User)

    • ID
    • 成就:achievement
    • 头像:

(二)个人博客实体关系图

(三)模块划分脑图

(四)APP模块构建

1.APP模块创建

# python manage.py startapp blog			# 该部分先前已经配置过,不用执行
python manage.py startapp blog_config
python manage.py startapp comments
  • blog_configcomments添加到config文件夹下的settings中。
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'blog_config',
    'comments',
]

2.blog模块设置

  • 清理原有blog的内容
  • models.py文件进行修改
    from django.db import models
    from django.db.models.fields import DateTimeField
    from django.contrib.auth.models import User
    
    # Create your models here.
    class Category(models.Model):
        """分类 - ID(Django自动分配) - 名称:name - 状态:status - 作者:owner - 创建时间:create_time - 是否置顶导航:is_nav """
        STATUS_ITEMS = (
            (1, "正常"),
            (0, "删除"),
        )
        name = models.CharField(max_length=50, verbose_name="名称")
        status = models.IntegerField(choices=STATUS_ITEMS, default=1, verbose_name="状态")
        owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者")
        is_nav = models.BooleanField(default=False, verbose_name="是否为导航")
        created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    
        def __str__(self):
            return self.name
        
        class Meta:
            verbose_name = '分类'
            verbose_name_plural = verbose_name
    
    
    class Tag(models.Model):
        """标签 - 名称:name - 状态:status - 作者:owner - 创建时间:created_time """
        STATUS_ITEMS = (
            (1, "正常"),
            (0, "删除"),
        )
        name = models.CharField(max_length=50, verbose_name="名称")
        owner = models.ForeignKey(
            User, on_delete=models.CASCADE, verbose_name="作者")
        status = models.IntegerField(
            choices=STATUS_ITEMS, default=1, verbose_name="状态")
        created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '标签'
            verbose_name_plural = verbose_name
    
    
    class Post(models.Model):
        """文章 - 标题:title - 作者:owner - 分类(多对一):category多篇文章对应一个分类 - 标签(多对多):tags - 摘要:desc - 正文:content - 状态(草稿、发布、删除):status - 发布时间:create_time """
        STATUS_ITEMS = (
            (1, "正常"),
            (0, "删除"),
            (2, "草稿")
        )
        title = models.CharField(max_length=255, verbose_name="标题")
        owner = models.ForeignKey(
            User, on_delete=models.CASCADE, verbose_name="作者")
        category = models.ForeignKey(Category, verbose_name="分类", 
        	on_delete=models.CASCADE)
        tags = models.ManyToManyField(Tag, verbose_name="标签")
        desc = models.CharField(max_length=1024, verbose_name="摘要")
        content = models.TextField(verbose_name="正文")
        status = models.IntegerField(
            choices=STATUS_ITEMS, default=2, verbose_name="状态")
        created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = '文章'
            verbose_name_plural = verbose_name
    
  • admin.py进行修改
    from django.contrib import admin
    from .models import Category, Tag, Post
    
    @admin.register(Category)
    class CategoryAdmin(admin.ModelAdmin):
        pass
    
    
    @admin.register(Tag)
    class TagAdmin(admin.ModelAdmin):
        pass
    
    
    @admin.register(Post)
    class PostAdmin(admin.ModelAdmin):
        pass
    

3.blog_config模块设置

  • 修改models.py
    from django.contrib.admin.decorators import display
    from django.db import models
    from django.contrib.auth.models import User
    
    # Create your models here.
    class Link(models.Model):
        """友情链接 - 网站名称:title - 链接:href - 作者:owner - 状态:status - 创建时间:created_time - 权重:weight """
        STATUS_ITEMS = (
            (1, "正常"),
            (0, "删除"),
        )
        title = models.CharField(max_length=50, verbose_name="网站名称")
        href = models.URLField(verbose_name="链接")
        status = models.IntegerField(
            choices=STATUS_ITEMS, default=1, verbose_name="状态")
        owner = models.ForeignKey(
            User, on_delete=models.CASCADE, verbose_name="作者")
        created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
        weight = models.IntegerField(choices=zip(
            range(1, 11), range(1, 11)), default=1, verbose_name="权重")
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '友情链接'
            verbose_name_plural = verbose_name
    
    
    class SideBar(models.Model):
        """侧边栏 - 标题:title - 类型:type - 状态:status - 内容:content - 创建时间:created_time - 作者:owner """
        TYPE_ITEMS = (
            (1, "HTML"),
            (2, "最新文章"),
            (3, "最热评论"),
            (4, "最近评论"),
        )
        STATUS_ITEMS = (
            (1, "展示"),
            (0, "隐藏"),
        )
        title = models.CharField(max_length=50, verbose_name="标题")
        display_type = models.IntegerField(
            choices=TYPE_ITEMS, default=1, verbose_name="展示类型")
        status = models.IntegerField(
            choices=STATUS_ITEMS, default=1, verbose_name="状态")
        content = models.CharField(max_length=500, verbose_name="内容") 
        owner = models.ForeignKey(
            User, on_delete=models.CASCADE, verbose_name="作者")
        created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '侧边栏'
            verbose_name_plural = verbose_name
    
  • 修改admin.py
    from django.contrib import admin
    from .models import Link, SideBar
    
    
    @admin.register(Link)
    class LinkAdmin(admin.ModelAdmin):
        pass
    
    
    @admin.register(SideBar)
    class SideBarAdmin(admin.ModelAdmin):
        pass
    

4.comments模块设置

  • 修改models.py
    from django.db import models
    from blog.models import Post
    
    # Create your models here.
    class Comment(models.Model):
        """评论 - 文章(多对一):post - 用户名:nickname - 邮箱:email - 网站地址:website - 内容:content - 创建时间:created_time - 作者:owner """
        target = models.ForeignKey(
            Post, verbose_name="评论目标", on_delete=models.CASCADE)
        nickname = models.CharField(max_length=50, verbose_name="昵称")
        email = models.EmailField(verbose_name="邮箱")
        website = models.URLField(verbose_name="网站地址")
        content = models.TextField(verbose_name="评论内容")
        created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    
        def __str__(self):
            return f"{
           self.target} By {
           self.nickname}"
    
        class Meta:
            verbose_name = '评论'
            verbose_name_plural = verbose_name
    
    • 修改admin.py
    from django.contrib import admin
    from .models import Comment
    
    # Register your models here.
    
    @admin.register(Comment)
    class CommentAdmin(admin.ModelAdmin):
        pass
    

5.更新数据库

  • 上面的所有设定方便SQL进行数据写入,交给框架处理SQL
    # 终端上执行
    python manage.py makemigrations
    python manage.py migrate
    

6.进入后台管理

  • 开启Debug
    • 修改settings.py文件
    DEBUG = True
    
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, "static")
    ]
    
    # STATIC_ROOT = os.path.join(BASE_DIR, "static") # 这个需要注释掉
    
  • 开启服务
    # 终端上执行
    python manage.py runserver 127.0.0.1:8899
    

(五)Admin自定义设置

1.浏览页面展示设置

  • 修改blog下的admin.py
    owner是外键,不允许为空,而fields不包含owner,导致报错
    @admin.register(Category)
    class CategoryAdmin(admin.ModelAdmin):
        list_display = ('name', 'status', 'is_nav', 'created_time')		# 首页展示
        # fields = ('name', 'status', 'is_nav') # 这种写法错误
        fields = ('name', 'status', 'owner', 'is_nav')		# 详细页展示
    

2.设置标签名与登录用户保持一致

  • 修改blog文件夹下的admin.py
    @admin.register(Category)
    class CategoryAdmin(admin.ModelAdmin):
        list_display = ('name', 'status', 'owner', 'is_nav', 'created_time')
        fields = ('name', 'status', 'is_nav')
    
        def save_model(self, request, obj, form, change):
            obj.owner = request.user
            return super(CategoryAdmin, self).save_model(request, obj, form, change)
    

3.设置文章的搜索框、保存/删除的位置

  • 修改blog下的admin.py
    • exclude与fields相反
    • actions_on_top和actions_on_bottom是控制执行动作的位置
    • save_on_top是控制页面编辑的位置
    • search_fields是增加搜索功能
    • 注意tuple内,若需要查询的是外键,需要加上__符号。
    class PostAdmin(admin.ModelAdmin):
        list_display = ("category","title", "owner", "status")
        exclude = ("owner",)
    
        actions_on_top = True
        save_on_top = True
        search_fields = ("title", "category__name") 
        
    
        def save_model(self, request, obj, form, change):
            obj.owner = request.user
            return super(PostAdmin, self).save_model(request, obj, form, change)
    

4.显示对应标签文章的数量

  • 修改blog下的admin.py
    @admin.register(Category)
    class CategoryAdmin(admin.ModelAdmin):
        list_display = ('name', 'status', 'owner', 'is_nav', 'post_count', 'created_time')
        fields = ('name', 'status', 'is_nav')
    
        # select * from Post where p.category = 'name'
        def post_count(self, obj):
            return obj.post_set.count()
    
        post_count.short_description = "文章数"
    
        def save_model(self, request, obj, form, change):
            obj.owner = request.user
            return super(CategoryAdmin, self).save_model(request, obj, form, change)
    

(六)Views自定义设置

1.预备工作

  • 通过宝塔上传模板文件,模板文件可以去Datawhale中获取。

2.创建base.html

  • 这里的base.html是整个网页的框架,基本上是不怎么变动的,因此可以独立出来修改,再与其他页面互相链接。

  • 复制index.html并更名为base.html

  • 通过对模板网页结构的分析,可以将main list部分去除并保留在index.html中,其他内容为base.html的核心。

  • blog文件夹下的views.py写入函数,用于识别base.html,向前端传递信息

    from django.shortcuts import render
    
    def index(request):
    
        return render(request, "base.html")
    
  • config文件夹下的urls.py导入上面写好的index函数,添加识别路径

    from os import name
    from django.contrib import admin
    from django.urls import path
    from blog.views import index
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path("", index, name="index"),
    ]
    
  • base.html页面结果如下,大致形成了网站的基本框架。

3.导航界面修改

  • 在这一模块下,简单来说是,views.py获取到查询的结果,以列表的形式传递给了urls.pyurls.py将其做成网站地址传递给了前端,base.htmlurls.py接收到变量的信息,反映到网页上。后续的大部分内容都是依照这个思路不断改进和完善。
  • 先对导航的内容进行查询,修改blog文件夹下的views.py
    from django.shortcuts import render
    from .models import Category
    
    def index(request):
        categories = Category.objects.filter(is_nav=True , status=1)
        print(categories)
        return render(request, "base.html", 
        {
         'categories': categories}
        )	# 将categories结果返回给前端
    
    此时刷新页面时,终端会返回查询的结果(是列表形式),如:
    <QuerySet [<Category: test1>, <Category: Python>, <Category: C++>]>
    
  • 导航界面是有base.html控制的,修改base.html
    上面返回给前端的结果是一个列表,可以在前端采用for循环取值。
    <ul class="layui-nav layui-bg-cyan">
        <li class="layui-nav-item layui-this">
            <a href="">首页</a>
        </li>
        {% for category in categories %}
        <li class="layui-nav-item">
            <a href="">{
        {category.name}}<span class="layui-badge-dot"></span></a>
        </li>
        {% endfor %}
    </ul>
    
  • 结果如下,导航栏显示了后台设定的分类。

4.关联网页模块

  • 增加base.htmlindex.html的链接
    • 修改base.html,在刚才删除主体内容的位置增加block模块
    <div class="layui-col-md8">
        {% block content %}
    
        {% endblock  %}
    </div>
    
    • 修改index.html,同样增加block模块,同时通过extendbase.html链接
    {% extends 'base.html' %}
    
    {% block content %}
    	base.html删除的内容
    {% endblock  %}
    
    • 修改views.py,将原来render返回的base.html修改为index.html,完成链接。

5.修改主体内容

  • 原理同上,先查blog内容,然后通过render返回信息到前端(修改views.py

    from django.shortcuts import render
    from .models import Category, Post
    
    def index(request):
        categories = Category.objects.filter(is_nav=True , status=1)    
        blogs = Post.objects.filter(status=1)
        
        return render(request, "index.html", 
        {
         
            'categories': categories,
            'blogs': blogs
        })	
    
  • 分析index.html的框架
    可以发现,每个blog内容都是由以下框架构成:

    <div class="layui-col-md12 margin20"></div>
    <div class="layui-col-md12">
        ...
    </div>
    
    • 删除上述的框架,仅保留一个
      后续可以使用for循环遍历blog的内容

  • 在后台添加新的文章
    这里需要注意的是,上面views.py文件中执行的查询是有条件的,即是导航栏且非草稿。因此添加文章时需要按条件来添加,以便观察效果。

  • 修改框架的主体内容

    • 上面的操作完成了页面的基本展示,但标题、标签、时间等都没有改变(仍然是模板的内容),这部分内容是在index.html上。
    • 只需找到对应的文本位置进行修改即可,如:
    title="{
        {blog.title}}"
    
    • 其中展示文章的标签,可以使用for循环
    <div class="layui-col-xs3 layui-col-md3 Label">
        <i class="layui-icon layui-icon-note"></i>
        {% for tag in blog.tags.all %}
        <a href="javascript:;">{
        {tag.name}}</a>
        {% endfor %}
    </div>
    

6.通过detail.html建立链接指向

  • 实现“阅读原文”指向对应的blog文章
    • 修改blog文件夹下的views.py,增加detail
    def detail(request, id):
        blog = Post.objects.get(id=id)
        return render(request, "detail.html",
        {
         
            'blog': blog
        })
    
    • 修改detail.html内容,仅保留文章的主体内容<div class="layui-row">
    {% extends 'base.html' %}
    
    {% block content %}
    
    <div class="layui-row">
        xxx
    </div>
    
    {% endblock  %}
    
    • 修改base.html内容,在href上增加链接
    {% extends 'base.html' %}
    
    {% block content %}
    
    <div class="layui-row">
        xxx
    </div>
    
    {% endblock  %}
    

7.博客页面导航栏设置

  • 目的是同步导航栏,与首页一致
  • views.py上将categories设置为全局变量,在detail中增加categories
    from django.shortcuts import render
    from .models import Category, Post
    
    categories = None
    
    def index(request):
        global categories
        categories = Category.objects.filter(is_nav=True , status=1)
        blogs = Post.objects.filter(status=1)
        for blog in blogs:
            print(blog.tags.all())
    
        return render(request, "index.html", 
        {
         
            'categories': categories,
            'blogs': blogs
        })
    
    def detail(request, id):
        blog = Post.objects.get(id=id)
        return render(request, "detail.html",
        {
         
            'categories': categories,
            'blog': blog
        })
    

8.首页链接设置

  • 修改base.html,首页上的链接修改成<a href="/">首页</a>

9.设置导航栏的链接

  • blog文件夹下的views.py中新增一个函数,用于获取分类下的文章,即建立查询

    def get_posts_by_category(request, id):
        blogs = Post.objects.filter(status=1, category_id=id)
        print(blogs)
        return render(request, "index.html",
                      {
         
                          'categories': categories,
                          'blogs': blogs
                      })
    
  • config文件夹下的urls.py中导入上面的函数,添加路径

    from os import name
    from django.contrib import admin
    from django.urls import path
    # from blog.views import index, read_blog
    from blog.views import index, detail, get_posts_by_category
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path("", index, name="index"),
        path("detail/<int:id>", detail, name="detail"),
        path("category/detail/<int:id>", get_posts_by_category, name="category_posts"),
    ]
    
  • 修改base.html中的categories循环中修改href

    href="{% url 'category_posts' category.id %}"
    

参考资料

  1. https://www.bilibili.com/video/BV1w5411u7kc?t=4913
  2. https://github.com/datawhalechina/team-learning-program/tree/master/Django
  3. https://zhuanlan.zhihu.com/p/380235160
  4. https://blog.csdn.net/TongxinV/article/details/94883101
  5. https://www.bilibili.com/video/BV1Ab4y1o7JV?t=643
  6. https://www.cnblogs.com/longbigbeard/p/10028746.html
  7. https://www.infoq.cn/u/tango/publish
  8. https://blog.csdn.net/tdcqfyl/article/details/52104476
全部评论

相关推荐

一天代码十万三:这都不能算简历吧
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客企业服务