• 通过创建博客学习Django-2


    本篇文章主要对追梦人物的博客《使用 django 开发一个个人博客》进行总结



    https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/65/

    创作后台开启,请开始你的表演

    创建 admin 后台管理员账户

    上文已创建,如果没有创建,则运行

    > pipenv run python manage.py createsuperuser
     
    用户名 (leave blank to use 'yangxg'): admin
    电子邮件地址: admin@example.com
    Password:                                   # 密码输入过程中不会有任何字符显示
    Password (again):
    Superuser created successfully.
    

    在 admin 后台注册模型

    blog/admin.py
     
    from django.contrib import admin
    from .models import Post, Category, Tag
     
    admin.site.register(Post)
    admin.site.register(Category)
    admin.site.register(Tag)
    

    访问 http://127.0.0.1:8000/admin/查看效果

    django admin 后台

    定制 admin 后台

    汉化 blog 应用

    1.blog汉化

    • 在 blog 应用下有一个 app.py 模块
    from django.apps import AppConfig
     
    class BlogConfig(AppConfig):
        name = 'blog'
    
    • 修改 app 在 admin 后台的显示名字,添加 verbose_name 属性
    class BlogConfig(AppConfig):
        name = 'blog'
        verbose_name = '博客'
    
    • 在 settings 中修改注册应用:
    INSTALLED_APPS = [
        'django.contrib.admin',
        ...
     
        'blog.apps.BlogConfig',  # 注册 blog 应用
    ]
    

    2.Post 、Tag 和 Category 模型汉化:

    class Post(models.Model):
        ...
        author = models.ForeignKey(User, on_delete=models.CASCADE)
     
        class Meta:
            verbose_name = '文章'
            verbose_name_plural = verbose_name
     
        def __str__(self):
            return self.title
    
    class Category(models.Model):
        name = models.CharField(max_length=100)
     
        class Meta:
            verbose_name = '分类'
            verbose_name_plural = verbose_name
     
        def __str__(self):
            return self.name
     
     
    class Tag(models.Model):
        name = models.CharField(max_length=100)
     
        class Meta:
            verbose_name = '标签'
            verbose_name_plural = verbose_name
     
        def __str__(self):
            return self.name
    
    • 注册的 model 显示为中文,配置 model 特性是通过 model 的内部类 Meta 中来定义
    • 通过 verbose_name 来指定对应的 model 在 admin 后台的显示名称, verbose_name_plural 用来表示多篇文章时的复数显示形式
    • 英语中,如果有多篇文章,就会显示为 Posts,表示复数,中文没有复数表现形式,所以定义为和 verbose_name一样

    3.post 的表单的 label汉化

    label 由定义在 model 中的 Field 名转换而来,所以在 Field 中修改

    class Post(models.Model):
        title = models.CharField('标题', max_length=70)
        body = models.TextField('正文')
        created_time = models.DateTimeField('创建时间')
        modified_time = models.DateTimeField('修改时间')
        excerpt = models.CharField('摘要', max_length=200, blank=True)
        category = models.ForeignKey(Category, verbose_name='分类', on_delete=models.CASCADE)
        tags = models.ManyToManyField(Tag, verbose_name='标签', blank=True)
        author = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE)
    
    • 位置参数值即为 field 应该显示的名字(如果不传,django 自动根据 field 名生成),参数的名字也叫 verbose_name,绝大部分 field 这个参数都位于第一个位置,
    • 由于 ForeignKeyManyToManyField 第一个参数必须传入其关联的 Model,所以 category、tags 这些字段我们使用了关键字参数 verbose_name

    文章列表显示更加详细的信息

    blog/admin.py
     
    from django.contrib import admin
    from .models import Post, Category, Tag
     
    class PostAdmin(admin.ModelAdmin):
        list_display = ['title', 'created_time', 'modified_time', 'category', 'author']
     
    # 把新增的 Postadmin 也注册进来
    admin.site.register(Post, PostAdmin)
    admin.site.register(Category)
    admin.site.register(Tag)
    

    查看admin Post 列表页面效果

    img

    简化新增文章的表单

    1.在blog/admin.py 中定义PostAdmin 配置 Post 在 admin 后台的一些展现形式

    • list_display 属性控制 Post 列表页展示的字段
    • fields 属性控制表单展现的字段
    class PostAdmin(admin.ModelAdmin):
        list_display = ['title', 'created_time', 'modified_time', 'category', 'author']
        fields = ['title', 'body', 'excerpt', 'category', 'tags']
    

    2.填充文章作者

    • save_model 方法:obj.save()。作用是将此 Modeladmin 关联注册的 model 实例(这里 Modeladmin 关联注册的是 Post)保存到数据库
    • 这个方法接收四个参数,其中前两个,一个是 request,即此次的 HTTP 请求对象,第二个是 obj,即此次创建的关联对象的实例

    通过复写此方法,就可以将 request.user 关联到创建的 Post 实例上,然后将 Post 数据再保存到数据库:

    class PostAdmin(admin.ModelAdmin):
        list_display = ['title', 'created_time', 'modified_time', 'category', 'author']
        fields = ['title', 'body', 'excerpt', 'category', 'tags']
     
        def save_model(self, request, obj, form, change):
            obj.author = request.user
            super().save_model(request, obj, form, change)
    

    3.填充创建时间

    Model 中定义的每个 Field 都接收一个 default 关键字参数,

    • 参数的含义是:如果将 model 的实例保存到数据库时,对应的 Field 没有设置值,那么 django 会取这个 default 指定的默认值,将其保存到数据库
    • 因此,对于文章创建时间这个字段,初始没有指定值时,默认应该指定为当前时间,所以刚好可以通过 default 关键字参数指定:
    from django.utils import timezone
     
    class Post(models.Model):
        ...
        created_time = models.DateTimeField('创建时间', default=timezone.now)
        ...
    
    • default 既可以指定为一个常量值,也可以指定为一个可调用(callable)对象,
    • timezone.now 函数返回当前时间

    4.填充修改时间

    因为每次保存模型时,都应该修改 modified_time 的值

    通过复写 save_model方法,在 model 被 save 到数据库前指定 modified_time 的值为当前时间:

    from django.utils import timezone
     
    class Post(models.Model):
        ...
     
        def save(self, *args, **kwargs):
            self.modified_time = timezone.now()
            super().save(*args, **kwargs)
    
    • 调用父类的 save 以执行数据保存回数据库

    https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/66/

    开发博客文章详情页

    设计文章详情页的 URL

    当用户访问 <网站域名>/posts/1/ 时,显示的是第一篇文章的内容:

    blog/urls.py
     
    from django.urls import path
     
    from . import views
     
    app_name = 'blog'
    urlpatterns = [
        path('', views.index, name='index'),
        path('posts/<int:pk>/', views.detail, name='detail'),
    ]
    
    • 'posts/<int:pk>/' 匹配 URL 规则,含义是以 posts/ 开头,后跟一个整数,并且以 / 符号结尾,如 posts/1/、 posts/255/ 等都是符合规则的
    • <int:pk> 从用户访问的 URL 里把匹配到的数字捕获并作为关键字参数传给其对应的视图函数 detail

    区分视图函数

    • 视图函数命名空间:通过 app_name='blog' 告诉 django 这个 urls.py 模块是属于 blog 应用
    • 通过 app_name 来指定命名空间

    为了方便地生成上述的 URL,我们在 Post 类里定义一个 get_absolute_url 方法,注意 Post 本身是一个 Python 类,在类中我们是可以定义任何方法的

    blog/models.py
     
    from django.contrib.auth.models import User
    from django.db import models
    from django.urls import reverse
    from django.utils import timezone
     
    class Post(models.Model):
        ...
     
        def __str__(self):
            return self.title
     
        # 自定义 get_absolute_url 方法
        # 记得从 django.urls 中导入 reverse 函数
        def get_absolute_url(self):
            return reverse('blog:detail', kwargs={'pk': self.pk})
    
    • URL 配置中的 path('posts/<int:pk>/', views.detail, name='detail') ,设定的 name='detail' 在这里派上了用场
    • reverse 函数,第一个参数的值是 'blog:detail',意思是 blog 应用下的 name=detail 的函数
    • detail 对应的规则是 posts/<int:pk>/ int 部分会被后面传入的参数 pk 替换,所以,如果 Post 的 id(或者 pk,这里 pk 和 id 是等价的) 是 255 的话,那么 get_absolute_url 函数返回是 /posts/255/ ,这样 Post 自己就生成了自己的 URL

    编写 detail 视图函数

    log/views.py
     
    from django.shortcuts import render, get_object_or_404
    from .models import Post
     
    def index(request):
        # ...
     
    def detail(request, pk):
        post = get_object_or_404(Post, pk=pk)
        return render(request, 'blog/detail.html', context={'post': post})
    
    • 视图函数根据从 URL 捕获的文章 id(也就是 pk,这里 pk 和 id 是等价的)获取数据库中文章 id 为该值的记录,然后传递给模板
    • 从 django.shortcuts 模块导入的 get_object_or_404 方法,作用是当传入的 pk 对应的 Post 在数据库存在时,就返回对应的 post,如果不存在,就给用户返回一个 404 错误,表明用户请求的文章不存在

    编写详情页模板

    从下载的博客模板把 single.html 拷贝到 templateslog 目录下(和 index.html 在同一级目录),然后改名为 detail.html

    blogproject
        manage.py
        blogproject
            __init__.py
            settings.py
            ...
        blog/
            __init__.py
            models.py
            ,,,
        templates
            blog
                index.html
                detail.html
    

    在 index 页面博客文章列表的标题继续阅读按钮写上超链接跳转的链接,即文章 post 对应的详情页的 URL,让用户点击后可以跳转到 detail 页面:

    templates/blog/index.html
     
    <article class="post post-{{ post.pk }}">
      <header class="entry-header">
          
          <!-- 修改文章标题 -->
        <h1 class="entry-title">
          <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
        </h1>
        ...
      </header>
      <div class="entry-content clearfix">
        ...
        <div class="read-more cl-effect-14">
            
            <!-- 修改继续阅读按钮的链接 -->
          <a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
        </div>
      </div>
    </article>
    {% empty %}
      <div class="no-post">暂时还没有发布的文章!</div>
    {% endfor %}
    
    • {{ post.get_absolute_url }} 最终会被替换成该 post 自身的 URL

    模板继承

    将 index.html 文件和 detail.html 文件相同的部分放到 base.html,在 templates 目录下新建一个 base.html 文件:

    blogproject
        manage.py
        blogproject
            __init__.py
            settings.py
            ...
        blog
            __init__.py
            models.py
            ,,,
        templates
            base.html
            blog
                index.html
                detail.html
    

    把 index.html 的内容全部拷贝到 base.html 文件里,删掉 main 标签包裹的内容,替换成如下的内容:

    templates/base.html
     
    ...
    <main class="col-md-8">
        {% block main %}
        {% endblock main %}
    </main>
    <aside class="col-md-4">
      {% block toc %}
      {% endblock toc %}
      ...
    </aside>
    ...
    
    • block 模板标签,其作用是占位,{% block main %} {% endblock main %}是一个占位框,main 是给 block 取的名字
    • aside 标签下加了一个 {% block toc %} {% endblock toc %}占位框,因为 detail.html 中 aside 标签下会多一个目录栏
    • {% block toc %} {% endblock toc %} 中没有任何内容时,{% block toc %} {% endblock toc %} 在模板中不会显示;但当其中有内容是,模板就会显示 block 中的内容

    在 index.html 里,我们在文件最顶部使用 {% extends 'base.html' %} 继承 base.html,在 {% block main %} {% endblock main %} 包裹的地方填上 index 页面应该显示的内容:

    templates/blog/index.html
     
    {% extends 'base.html' %}
     
    {% block main %}
        {% for post in post_list %}
            <article class="post post-1">
              ...
            </article>
        {% empty %}
            <div class="no-post">暂时还没有发布的文章!</div>
        {% endfor %}
        <!-- 简单分页效果
        <div class="pagination-simple">
            <a href="#">上一页</a>
            <span class="current">第 6 页 / 共 11 页</span>
            <a href="#">下一页</a>
        </div>
        -->
        <div class="pagination">
          ...
        </div>
    {% endblock main %}
    
    • 模板继承的作用:公共部分的代码放在 base.html 里,而其它页面不同的部分通过替换 {% block main %} {% endblock main %} 占位标签里的内容即可

    detail 页面继承 base.html ,在 {% block main %} {% endblock main %} 里填充 detail.html 页面应该显示的内容,以及在 {% block toc %} {% endblock toc %} 中填写 base.html 中没有的目录部分的内容

    templates/blog/detail.html
     
    {% extends 'base.html' %}
     
    {% block main %}
        <article class="post post-1">
          ...
        </article>
        <section class="comment-area">
          ...
        </section>
    {% endblock main %}
    {% block toc %}
        <div class="widget widget-content">
            <h3 class="widget-title">文章目录</h3>
            <ul>
                <li>
                    <a href="#">教程特点</a>
                </li>
                <li>
                    <a href="#">谁适合这个教程</a>
                </li>
                <li>
                    <a href="#">在线预览</a>
                </li>
                <li>
                    <a href="#">资源列表</a>
                </li>
                <li>
                    <a href="#">获取帮助</a>
                </li>
            </ul>
        </div>
    {% endblock toc %}
    

    修改 article 标签下的一些内容,让其显示文章的实际数据:

    <article class="post post-{{ post.pk }}">
      <header class="entry-header">
        <h1 class="entry-title">{{ post.title }}</h1>
        <div class="entry-meta">
          <span class="post-category"><a href="#">{{ post.category.name }}</a></span>
          <span class="post-date"><a href="#"><time class="entry-date"
                                                    datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
          <span class="post-author"><a href="#">{{ post.author }}</a></span>
          <span class="comments-link"><a href="#">4 评论</a></span>
          <span class="views-count"><a href="#">588 阅读</a></span>
        </div>
      </header>
      <div class="entry-content clearfix">
        {{ post.body }}
      </div>
    </article>
    

    再次从首页点击一篇文章的标题或者继续阅读按钮跳转到详情页面,查看预期效果:

    img


    https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/67/

    让博客支持 Markdown 语法和代码高亮

    安装 Python Markdown

    项目根目录下运行命令 pipenv install markdown

    在 detail 视图中解析 Markdown

    所写的博客文章在 Postbody 属性里,回到详情页视图函数,对 postbody 的值做一下解析,把 Markdown 文本转为 HTML 文本再传递给模板:

    blog/views.py
     
    import markdown
    from django.shortcuts import get_object_or_404, render
     
    from .models import Post
     
    def detail(request, pk):
        post = get_object_or_404(Post, pk=pk)
        post.body = markdown.markdown(post.body,
                                      extensions=[
                                          'markdown.extensions.extra',
                                          'markdown.extensions.codehilite',
                                          'markdown.extensions.toc',
                                      ])
        return render(request, 'blog/detail.html', context={'post': post})
    

    extensions是对 Markdown 语法的拓展:

    extra 包含很多基础拓展,codehilite 是语法高亮拓展,toc 允许自动生成目录

    safe 标签

    为解除转义,在模板变量后使用 safe 过滤器即可,告诉 django,这段文本是安全的

    在模板中找到展示博客文章内容的 {{ post.body }} 部分,为其加上 safe 过滤器:{{ post.body|safe }}

    代码高亮

    代码高亮我们借助 js 插件来实现,其原理就是 js 解析整个 html 页面,然后找到代码块元素,为代码块中的元素添加样式。

    • highlight.js 提供基础的代码高亮
    • 和 highlightjs-line-numbers.js 为代码块添加行号。

    在 base.html 的 head 标签里引入代码高亮的样式,样式文件通过 CDN 引入,同时在 style 标签里自定义了一点元素样式。(style 标签插入后可能出现异常)

    <head>
      ...
      <link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet">
     
      <style>
        .codehilite {
          padding: 0;
        }
     
        /* for block of numbers */
        .hljs-ln-numbers {
          -webkit-touch-callout: none;
          -webkit-user-select: none;
          -khtml-user-select: none;
          -moz-user-select: none;
          -ms-user-select: none;
          user-select: none;
     
          text-align: center;
          color: #ccc;
          border-right: 1px solid #CCC;
          vertical-align: top;
          padding-right: 5px;
        }
     
        .hljs-ln-n {
           30px;
        }
     
        /* for block of code */
        .hljs-ln .hljs-ln-code {
          padding-left: 10px;
          white-space: pre;
        }
      </style>
    </head>
    

    然后引入 js 文件,因为应该等整个页面加载完,插件再去解析代码块,把 js 文件的引入放在 body 底部:

    <body>
      <script src="https://cdn.bootcss.com/highlight.js/9.15.8/highlight.min.js"></script>
      <script src="https://cdn.bootcss.com/highlightjs-line-numbers.js/2.7.0/highlightjs-line-numbers.min.js"></script>
      <script>
        hljs.initHighlightingOnLoad();
        hljs.initLineNumbersOnLoad();
      </script>
    </body>
    

    https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/68/

    Markdown 文章自动生成目录,提升阅读体验

    在文中插入目录

    markdown.extensions.toc 是自动生成目录的拓展

    在书写 Markdown 文本时,在想生成目录的地方插入 [ TOC ] 标记即可。例如新写一篇 Markdown 博文,其 Markdown 文本内容如下:

    [TOC]
     
    ## 我是标题一
     
    这是标题一下的正文
     
    ## 我是标题二
     
    这是标题二下的正文
     
    ### 我是标题二下的子标题
    这是标题二下的子标题的正文
     
    ## 我是标题三
    这是标题三下的正文
    

    在页面的任何地方插入目录

    在侧边栏插入目录

    blog/views.py
     
    def detail(request, pk):
        post = get_object_or_404(Post, pk=pk)
        md = markdown.Markdown(extensions=[
            'markdown.extensions.extra',
            'markdown.extensions.codehilite',
            'markdown.extensions.toc',
        ])
        post.body = md.convert(post.body)
            post.toc = md.toc
     
        return render(request, 'blog/detail.html', context={'post': post})
    
    • 先实例化一个 markdown.Markdown 对象 md,传入 extensions 参数
    • 使用该实例的 convert 方法将 post.body 中的 Markdown 文本解析成 HTML 文本
    • 实例 md 多出一个 toc 属性,这个属性的值就是内容的目录,我们把 md.toc 的值赋给 post.toc 属性(要注意这个 post 实例本身是没有 toc 属性的,我们给它动态添加了 toc 属性)

    在博客文章详情页的文章目录侧边栏渲染文章的目录,删掉占位用的目录内容,替换成如下代码:

    {% block toc %}
        <div class="widget widget-content">
            <h3 class="widget-title">文章目录</h3>
            {{ post.toc|safe }}
        </div>
    {% endblock toc %}
    
    • 用模板变量标签 {{ post.toc }} 显示模板变量的值,注意 post.toc 实际是一段 HTML 代码,我们知道 django 会对模板中的 HTML 代码进行转义,所以要使用 safe 标签防止 django 对其转义

    处理空目录

    • 只有在文章存在目录结构时,才显示侧边栏的目录
    • 分析 toc 的内容,如果有目录结构,ul 标签中就有值,否则就没有值
    • 使用正则表达式来测试 ul 标签中是否包裹有元素来确定是否存在目录

    (注意:可能需要调用re模块)

    def detail(request, pk):
        post = get_object_or_404(Post, pk=pk)
        md = markdown.Markdown(extensions=[
            'markdown.extensions.extra',
            'markdown.extensions.codehilite',
            'markdown.extensions.toc',
        ])
        post.body = md.convert(post.body)
     
        m = re.search(r'<div class="toc">s*<ul>(.*)</ul>s*</div>', md.toc, re.S)
        post.toc = m.group(1) if m is not None else ''
     
        return render(request, 'blog/detail.html', context={'post': post})
    

    正则表达式去匹配生成的目录中包裹在 ul 标签中的内容:

    • 如果不为空,说明目录把 ul 标签中的值提取出来(目的是只要包含目录内容的最核心部分,多余的 HTML 标签结构丢掉)赋值给 post.toc
    • 否则,将 post 的 toc 置为空字符串,然后我们就可以在模板中通过判断 post.toc 是否为空,来决定是否显示侧栏目录
    {% block toc %}
      {% if post.toc %}
        <div class="widget widget-content">
          <h3 class="widget-title">文章目录</h3>
          <div class="toc">
            <ul>
              {{ post.toc|safe }}
            </ul>
          </div>
        </div>
      {% endif %}
    {% endblock toc %}
    

    模板标签 {% if %}表示条件判断,和 Python 中的 if 条件判断是类似的

    美化标题的锚点 URL

    #_1 是锚点,Markdown 在设置锚点时利用的是标题的值,由于通常我们的标题都是中文,需要修改一下传给 extentions 的参数:

    blog/views.py
     
    from django.utils.text import slugify
    from markdown.extensions.toc import TocExtension
     
    def detail(request, pk):
        post = get_object_or_404(Post, pk=pk)
        md = markdown.Markdown(extensions=[
            'markdown.extensions.extra',
            'markdown.extensions.codehilite',
            # 记得在顶部引入 TocExtension 和 slugify
            TocExtension(slugify=slugify),
        ])
        post.body = md.convert(post.body)
     
        m = re.search(r'<div class="toc">s*<ul>(.*)</ul>s*</div>', md.toc, re.S)
        post.toc = m.group(1) if m is not None else ''
     
        return render(request, 'blog/detail.html', context={'post': post})
    
    • extensions 中的 toc 拓展不再是字符串 markdown.extensions.toc ,而是 TocExtension 的实例
    • TocExtension 在实例化时其 slugify 参数可以接受一个函数,这个函数将被用于处理标题的锚点值
    • 使用 django.utils.text 中的 slugify 方法处理中文标题

    效果如下:


    https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/69/

    自动生成文章摘要

    覆写 save 方法

    body 字段存储的是正文,excerpt 字段用于存储摘要。通过覆写模型的 save 方法,在数据被保存到数据库前,先从 body 字段摘取 N 个字符保存到 excerpt 字段中,从而实现自动摘要的目的

    blog/models.py
     
    import markdown
    from django.utils.html import strip_tags
     
    class Post(models.Model):
        # 其它字段...
        body = models.TextField()
        excerpt = models.CharField(max_length=200, blank=True)
     
        # 其它方法...
     
        def save(self, *args, **kwargs):
            self.modified_time = timezone.now()
     
            # 首先实例化一个 Markdown 类,用于渲染 body 的文本。
            # 由于摘要并不需要生成文章目录,所以去掉了目录拓展。
            md = markdown.Markdown(extensions=[
                'markdown.extensions.extra',
                'markdown.extensions.codehilite',
            ])
     
            # 先将 Markdown 文本渲染成 HTML 文本
            # strip_tags 去掉 HTML 文本的全部 HTML 标签
            # 从文本摘取前 54 个字符赋给 excerpt
            self.excerpt = strip_tags(md.convert(self.body))[:54]
     
            super().save(*args, **kwargs)
    

    摘要方案:

    • 先将 body 中的 Markdown 文本转为 HTML 文本,去掉 HTML 文本里的 HTML 标签,然后摘取文本的前 54 个字符作为摘要
    • 然后在模板中适当的地方使用模板标签引用 {{ post.excerpt }} 显示摘要的值
    templates/blog/index.html
     
    <article class="post post-{{ post.pk }}">
      ...
      <div class="entry-content clearfix">
          <p>{{ post.excerpt }}...</p>
          <div class="read-more cl-effect-14">
              <a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
          </div>
      </div>
    </article>
    

    新添加一篇文章,才能触发 save 方法,此前添加的文章不会自动生成摘要,要手动保存一下触发 save 方法

  • 相关阅读:
    出现org.apache.ibatis.binding.BindingException异常
    EasyExcel读写操作
    window下运行nginx出现nginx: [emerg] bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions)
    vue Module build failed: Error: Missing binding E:vuevue-demo ode_modules ode-sa ssvendorwin64
    Axios谷粒学院学习
    springboot中数据库的连接
    多表删除,删除一个表的同时删除中间表
    今天写了一个SSM小项目,运行之后,前端页面的CSS、js样式显示不出来,具体操作如下:
    Java中Iterator(迭代器)实现原理
    写一些东西,记录一下成长的过程
  • 原文地址:https://www.cnblogs.com/chungzhao/p/13307428.html
Copyright © 2020-2023  润新知