• django学习总结


    tips:django官方中文文档(http://python.usyiyi.cn/django/index.html),django基础教程(http://www.ziqiangxuetang.com/django/django-tutorial.html)

    一、关于django

      django是一个python中的web框架,如果想更好的使用它,最好还能有HTML, CSS, JavaScript等方面的知识。它的特点主要体现在几个方面:强大的数据库(具有许多api同时支持执行sql语句)、自带强大的后台功能、利用正则表达式匹配网址(简化网页管理)、模板与视图一一对应、缓存机制、国际化支持等等。

      django适合于敏捷开发,只需要少量代码即可建立高性能web应用。因其已经完成了开发网站的许多常见代码,所以减少了代码的重复度。Ok,让我们首先看下django的主要文件组成。主要文件包括如下几个py文件:

    django工程主要py文件
    文件 功能
    urls.py 网址入口,关联到views中对于的函数
    models.py 与数据库操作相关,建立应用数据模型
    views.py 处理用户发出请求,从urls中对应过来,通过渲染templates中网页显示内容
    settings.py 相关设置,包括数据库设置,邮件设置,静态文件配置等
    forms.py 表单,用户在浏览器端提交的表单数据类
    admin.py 后台代码,大部分已完成

    二、常用命令

      环境搭建这一部分就不作叙述,网上教程很多。常用命令如下:

     1 #新建django project
     2 django-admin.py startproject project-name
     3 #新建django app命令
     4 django-admin.py startapp app-name (or) python manage.py startapp app-name
     5 #同步数据库
     6 python manage.py syncdb
     7 #(django1.7.1及以上版本需要使用以下命令)
     8 python manage.py makemigrations
     9 python manage.py migrate
    10 #运行django项目,不加port默认为8000
    11 python manage.py runserver port
    12 #ex: python manage.py runserver 8080
    13 #清空数据库
    14 python manage.py flush
    15 #创建超级管理员
    16 python manage.py createsuperuser
    17 #修改管理员密码
    18 python manage.py changepassword username
    19 #导出数据
    20 python manage.py dumpdata appname > appname.json
    21 #导入数据
    22 python manage.py loaddata appname.json
    23 #进入django项目命令终端
    24 python manage.py shell
    25 #进入数据库命令行
    26 python manage.py dbshell

    三、视图

      我们已经知道views中的函数是与urls中的网址对应的,接下来我们做一个简单的实例演示。

    1. 首先将应用名称添加到settings.py中

    INSTALLED_APPS = (
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'newReverse',
        'reverse',
    )

    2. 在views中定义视图函数

    def test(request):
        return render(request, 'test.html')

    这里返回的结果有多种,其本质返回的结果是HttpResponse。其他返回情况如

    return HttpResponse("hello world")
    return render_to_response('test.html', '')
    return HttpResponseRedirect('/index/')

    其中render与render_to_respose是一样的,只是render不需要说明ResponseContext而render_to_respose则需要coding出来。render,最后会返回一个字典,以向模板中传递数据。

    return render_to_response('checkmt.html', {'info': info})
    return render(request, 'day2.html', {'info': info})

    3. 设置在urls.py中的地址

    urlpatterns = patterns('',
        url(r'^admin/', include(admin.site.urls)),
        url(r'^login/$', login),
        url(r'^index/$', views.index, name="index"),   
    )

    这里urls设置有多种方式,具体可以查看官方文档,其主要包括两部分,正则表达式格式的网址+views函数。同时网址中还可以传递参数。

    四、模板

      模板是通过views函数渲染的,其实质是一个html网页。一般放在app下的templates中,加载模板时django会自动寻找。那么假如一个工程中有多个应用,每个应用的templates中都有一个index.html,然后直接调用render(request, 'index.html')能不能找到呢?答案是可能会出错。让我们来探究下django模板查找机制。django模板查找过程是在每个app的templates文件夹中查找(而不是当前app中的templates),当在这些应用的templates文件夹列表中查找到index.html模板时停止,所以说可能出现错误;如果最终没有找到则返回一个templates not found错误。

      接下来我们来介绍一下关于模板代码的应用。一般地,网站中都会有一些模板设计,即很多网页都有很多相同的部分(如导航、底部、时间等),这时就可以用使用模板来减少代码重复度了。如假设有一个base.html

     1 <!DOCTYPE html>
     2 <html>
     3 <head>
     4     <title>{% block title %}默认标题{% endblock %} - 自强学堂</title>
     5 </head>
     6 <body>
     7  
     8 {% include 'nav.html' %}
     9  
    10 {% block content %}
    11 <div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
    12 {% endblock %}
    13  
    14 {% include 'bottom.html' %}
    15  
    16 {% include 'tongji.html' %}
    17  
    18 </body>
    19 </html>

    block就是模板可以重写的部分,include即包含其他文件内容,所以我们就可以设计如下index.html

    1 {% extends 'base.html' %}
    2  
    3 {% block title %}欢迎光临首页{% endblock %}
    4  
    5 {% block content %}
    6 {% include 'ad.html' %}
    7 欢迎光临
    8 {% endblock %}

    django模板还提供了用于处理传递过来的字典数据的技术,如循环、条件判断、过滤器、常用标签等。如下就是这些技术的一些相关应用。

    <!-- 双重循环加载表格数据, if判断输出什么数据-->
            <table border="1">
                <tr>
                    <td>会议室时间</td>
                    {%for each in info.time%}
                    <td>{{each}}</td>
                    {%endfor%}
                </tr>
                {% for each in info.rvsdest %}
                <tr>
                    <td>{{each.mtrname}}</td>
                    {% for ev in each.time%}
                    {% if ev == 1%} <td>NO</td> {%else%} <td></td> {%endif%}
                    {% endfor%}
                </tr>
                {% endfor%}
            </table>

    其他的诸如(==, !=, >=, <=, >, <, and, or, not, in, not in)等也可以在模板中使用。

    五、数据模型

      Django模型与数据库相关,其相关代码一般写在models.py中。django支持的数据库包括sqlite3,mysql,PostgreSQL等,只需在settings.py中配置即可,同时数据模型提供了丰富的api。如下即可settings.py的配置样例。

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'reverse',
            'USER': 'root',
            'PASSWORD': '******',
            'HOST': '127.0.0.1',
            'PORT': '3306',
        }
    }

    1. 关于数据模型,首先我们来介绍下field,如下为一个model实例

     1 class RoomReverse(models.Model):
     2     mtno = models.IntegerField(default=0)
     3     STATE_CHOICES = (('st', 'stale'), ('ing', 'underway'), ('ns', 'notstart'), )
     4     state = models.CharField(max_length=20, choices=STATE_CHOICES, default='ns')
     5     rvstime = models.DateTimeField(auto_now_add=True)
     6     mtdate = models.CharField(max_length=100, default=None)
     7     mtrno = models.ForeignKey(Meetingroom)
     8     emails = models.TextField(default=None)
     9     rpemail = models.EmailField(default=None)
    10     class Meta:
    11         table_name = 'RoomReverse'
    12     def __getstr__():
    13         return str(mtno)

    其中包括的field有CharField、DateTimeField、IntegerField、TextField、ForeignKey、EmailField等等,其可以实现数据库的主键设置、外键设置,还能实现一对多、多对一、多对多设置等。另一方面,如果觉得django自带的field不够用,还可以使用自己定义的field。

    2. 接下来我们来介绍下关于数据库的一些操作。首先是将数据模型同步到数据库操作,命令如下:

    1 python manage.py syncdb
    2 #(django1.7.1及以上版本需要使用以下命令)
    3 python manage.py makemigrations
    4 python manage.py migrate

    3. 对数据进行增删改查操作,一般的方法有:

    #新建对象
    User.objects.create(name=name,pwd=pwd) #1
    #处理重复,是否创建
    User.objects.get_or_create(name=name,pwd=pwd)
    #用save创建
    us = User(name=name,pwd=pwd)
    us.save()
    #批量创建
    User.objects.bulk_create(user_object_list)
    User.objects.all()#获取全部对象
    User.objects.all()[:10]#切片操作
    User.objects.get(name=name1)#获取单个数据,第一个查找到的数据
    User.objects.filter(name=name1)#=User.objects.filter(name_exact=name1)#获取多个数据
    User.objects.filter(name__iexact="abc")#不区分大小写
    User.objects.filter(name__contains="abc")#name包含abc
    User.objects.filter(name__icontains="abc")#name不包含abc,且不区分大小写
    User.objects.filter(name__regex="^abc")#正则表达式查询
    User.objects.filter(name__iregex="^abc")#正则表达式不区分大小写
    User.objects.exclude(name__contains="abc")#排除包含abc的对象
    User.objects.filter(name__contains="abc").exclude(pwd=111)#name等于abc但pwd不等于111的对象
    User.objects.filter(name__in=[])#相当于将所有name在列表中的对象提取出来

     4. 对django中的数据模型,存在一些内部方法来设置或修改模型属性值

    model._meta.get_fields()    #获取所有属性
    model._meta.get_field_by_name('id')    #按属性名获取属性
    model._meta.get_all_field_names()    #获取模型所有属性名称
    object.__setattr__(attr, data)    #按属性名设置模型对象对应的属性值
    getattr(model, attr)    #获取模型某一列属性

    5. 在模型对象中还可以设置虚类来实现继承方法

     1 class BaseInfo(models.Model):
     2     '''
     3     模板基本信息,包括创建时间、更新时间、更新人、操作人,为虚类
     4     '''
     5     create_time = models.DateTimeField(auto_now_add=True)
     6     update_time = models.DateTimeField(auto_now=True)
     7     ISVALID = ((CHECKING, 0), (VALID, 1), (INVALID, 2))
     8     valid = models.IntegerField(choices=ISVALID, default=CHECKING)
     9     creator = models.CharField(max_length=30, default=None)
    10     operator = models.CharField(max_length=30, default=None)
    11     class Meta:
    12         abstract = True
    13 
    14 
    15 # 单个节点
    16 class NodeData(BaseInfo):
    17     id = models.AutoField(verbose_name='配置id', primary_key=True, auto_created=True)
    18     script = models.CharField(verbose_name='中文简述', max_length=50, default=None)
    19     category = models.CharField(verbose_name='类别', max_length=30, default=None)
    20     ensuper_class = models.CharField(verbose_name='实体父类', max_length=30, default=None)
    21     evsuper_class = models.CharField(verbose_name='事件父类', max_length=30, default=None)
    22     prsuper_class = models.CharField(verbose_name='属性父类', max_length=30, default=None)
    23     roleid = models.CharField(verbose_name='角色宿主', max_length=30, default=None)
    24     define_area = models.CharField(verbose_name='定义域', max_length=50, default=None)
    25     value_area = models.CharField(verbose_name='值域', max_length=50, default=None)
    26     location_area = models.CharField(verbose_name='位置域', max_length=50, default=None)
    27     statement = models.CharField(verbose_name='声明', max_length=50, default=None)
    28     illustrate = models.CharField(verbose_name='举例', max_length=50, default=None)
    29     combination = models.CharField(verbose_name='中文简述 + 类别', max_length=80, default=None, unique=True)
    30 
    31     class Meta(BaseInfo.Meta):
    32         # managed = True
    33         db_table = 'node_data'
    34 
    35     def toJSON(self):
    36         fields = []
    37         for field in self._meta.fields:
    38             fields.append(field.name)
    39         d = {}
    40         for attr in fields:
    41             if isinstance(getattr(self, attr), datetime.datetime):
    42                 d[attr] = getattr(self, attr).strftime('%Y-%m-%d %H:%M:%S')
    43             elif isinstance(getattr(self, attr), datetime.date):
    44                 d[attr] = getattr(self, attr).strftime('%Y-%m-%d')
    45             else:
    46                 d[attr] = getattr(self, attr)
    47         return json.dumps(d)
    View Code

    六、基于CBS编程

    Class-based views是Django为解决建站过程中的常见的呈现模式而建立的。具有如下几个原则:

    • 代码越少越好
    • 永远不要重复代码
    • View应当只包含呈现逻辑, 不应包括业务逻辑
    • 保持view逻辑清晰简单
    • 不要将CBVs用作403, 404, 500的错误处理程序
    • 保持mixin简单明了

    1. 使用django自身的cbvs

        cbvs是可扩展的,但在也增加了复杂度,有时甚至出现8个import引入关系。django自带的view如下表所示:

    类名功能例子
    View 基本View, 可以在任何时候使用 见后面详细介绍
    RedirectView 重新定向到其他URL 将访问"/log-in/"的用户重新定向到"/login/"
    TemplateView 显示Django HTML template 一般网站中使用模板显示的页
    ListView 显示对象列表 文章列表页
    DetailView 显示对象详情 文章详细页
    FormView 提交From 网站联系我们或emai订阅form
    CreateView 创建对象 创建新文章页
    UpdateView 更新对象 修改文章页
    DeleteView 删除对象 删除文章页
    Generic date views 显示一段时间内的对象 按时间归类的博客

    2. View中基本元素

     1 class ModelCreatView(CreateView):
     2     def __init__(self, model, template_name, context_object_name, success_url, success_msg, fields):
     3         """
     4         初始化函数,这里以creatview为例,其他view大同小异
     5         :param model:  对应要操作的模型
     6         :param template_name: 对应的模板名称,一般为html页面
     7         :param context_object_name: 返回给前台的对象
     8         :param success_url: 操作成功定向地址
     9         :param success_msg: 返回的成功信息
    10         :param fields: 需要操作的对应的模型中的属性
    11         """
    12         self.model = model 
    13         self.template_name = template_name
    14         self.context_object_name = context_object_name
    15         self.success_url = success_url
    16         self.success_msg = success_msg
    17         self.fields = fields
    18     # 
    19     # self.model = MODEL
    20     # self.fields = ['event_attribute', 'role_attribute', 'entiry_value', 'event_type', 'direction_flag', 'unique_flag',
    21     #                'appear_flag']
    22     # self.context_object_name = 'knowledgeGraph'
    23     # self.success_url = '/knowledgeGraph/'
    24     # self.template_name = 'knowledge_graph/add.html'
    View Code

    3. ListView简介

          listview是一个展示列表的view,返回的是一个template,包含两个关键方法:

    def get_context_data(self, **kwargs):
    def get_queryset(self):

          第一个方法返回一个字典给前端,包括分页信息,列表信息,已经其他自定义的信息;第二个方法返回数据库中获取到的数据(可能经过条件迭代),具体实现的实例代码如下:

     1     def get_context_data(self, **kwargs):
     2         """
     3         用来获取返回给前端页面的dict数据,前端页面可直接通过应用dict中的key来获取value
     4         :param kwargs:
     5         :return: context,一个字典类型的数据
     6         """
     7         try:
     8             context = super(ModelListView, self).get_context_data(**kwargs)
     9             log.debug("get_context_data")
    10             page_obj = context['page_obj']
    11             pages = []
    12             for i in range(1, page_obj.paginator.num_pages + 1):
    13                 if i == 1 or i == page_obj.paginator.num_pages:
    14                     pages.append(i)
    15                 elif i >= page_obj.number - 2 and i <= page_obj.number + 2:
    16                     pages.append(i)
    17                 elif pages[-1] != None:
    18                     pages.append(None)
    19                 else:
    20                     pass
    21             context['pages'] = pages
    22             context['order_type'] = self.order_type
    23             context['length'] = len(self.object_list)
    24             if 'pagination_num' not in context:
    25                 context['pagination_num'] = self.paginate_by
    26             self.echo_search(context)
    27             if self.model == NodeData:
    28                 context['tree'] = self.get_tree()
    29             return context
    View Code

    4. Mixin实现

         view中如果觉得自带的post、get方法不够好,也可以重写post、get方法,这样就与函数式编程没有什么区别。有时需要前后端异步方式加载数据,就需要使用ajax来完成,这时就可以使用mixin来解决。

         使用mixin可以为class提供额外的功能,但它自身却不能单独使用的类. 在具有多继承能力的编程语言中, mixin可以为类增加额外功能或方法. 在Django中, 我们可以使用mixin为CBVs提供更多的扩展性, 当然在类继承过程中, 我们推荐以下原则:

    • Django自身提供的View永远在最右边
    • mixin依次在以上view的左边
    • mixin永远继承自Python的object类型
    • 推荐mixin库django-braces
    class ModelValidView(LoginRequiredMixin, AjaxResponseMixin, View):
        def __init__(self, model):
            self.model = model
        def post_ajax(self, request, *args, **kwargs):
            pass

    七、文件上传下载

    django中文件上传一般是将文件拷本到服务器上,然后访问服务器上数据;下载文件则可以直接通过访问url完成。

    1. 文件上传:

    前端:

     1 {% include "./_meta.html" %}
     2 {% load staticfiles %}
     3 
     4 
     5 </head>
     6 <body>
     7 <article class="page-container">
     8         <form action="{% url 'freedomSegger_fileupload' %}" method="post" class="form form-horizontal" id="form-admin-role-add" enctype="multipart/form-data">{% csrf_token %}
     9                 <div class="row cl">
    10                         <label class="form-label col-xs-4 col-sm-3">模板下载:</label>
    11                         <div class="formControls col-xs-8 col-sm-9">
    12                                 <a href="TODO">点我下载</a>
    13                         </div>
    14                 </div>
    15                 <div class="row cl">
    16                         <label class="form-label col-xs-4 col-sm-3"><span class="c-red">*</span>上传文件:</label>
    17                         <div class="formControls col-xs-8 col-sm-9">
    18                                 <input type="file" class="input-text"  name="file">
    19                         </div>
    20                 </div>
    21                 <div class="row cl">
    22                         <div class="col-xs-8 col-sm-9 col-xs-offset-4 col-sm-offset-3">
    23                                 <button  type="submit" class="btn btn-success radius" id="admin-role-save" name="admin-role-save"><i class="icon-ok"></i> 确定</button>
    24                         </div>
    25                 </div>
    26         </form>
    27 </article>
    28 
    29 {% include "./_footer.html" %}
    30 
    31 </body>
    32 </html>
    View Code

    后台:

    1)工具方法:

     1 def handle_uploaded_file(f):
     2     """
     3     将上传的文件写入服务器对应目录下
     4     :param f: 上传文件
     5     :return: 
     6     """
     7     file_name = ""
     8     try:
     9         path = "uplodfile" + time.strftime('/%Y/%m/%d/%H/%M/%S/')
    10         if not os.path.exists(path):
    11             os.makedirs(path)
    12             file_name = path + f.name
    13             destination = open(file_name, 'wb+')
    14             for chunk in f.chunks():
    15                 destination.write(chunk)
    16             destination.close()
    17     except Exception, e:
    18         print e
    19     return file_name
    View Code

    2)加载上传页面

    1 @login_required
    2 def tofileUpload(request):
    3     return render(request, "knowledge_graph/file_upload.html", )
    View Code

    3)上传文件并存入数据库中

     1 @login_required
     2 def fileUpload(request):
     3     success_url = '/knowledgeGraph/'
     4     failed_url = '/knowledgeGraph/toFileUpload'
     5     return common_file_upload(request, save_data, success_url, failed_url, MODEL)
     6 
     7 def common_file_upload(request, save_data, success_url, failed_url, MODEL):
     8     """
     9     批量导入文件
    10     :param request:
    11     :param save_data: 针对每个model,在对应的view文件中自定义
    12     :param success_url: 导入成功时返回的url
    13     :param failed_url: 导入失败时返回的url
    14     :param MODEL: 对应的模型
    15     :return: 返回对应的页面
    16     """
    17     file_path = file_upload.handle_uploaded_file(request.FILES['file'])
    18     try:
    19         text = list(set(open(file_path).readlines()))
    20     except Exception as e:
    21         log.error(MODEL._meta.object_name + " 文件打开错误"+str(e))
    22         messages.info(request, "文件打开错误"+str(e))
    23         return HttpResponseRedirect(failed_url)
    24     except_info, failed = save_data(text, request)
    25     if failed == 0:
    26         messages.success(request,"400")
    27         messages.info(request, "导入成功")
    28         log.info(MODEL._meta.object_name + " 文件上传成功")
    29         return HttpResponseRedirect(success_url)
    30     else:
    31         log.warning(MODEL._meta.object_name + " " + except_info)
    32         messages.info(request, except_info)
    33         return HttpResponseRedirect(failed_url)
    View Code

    4)url设置

    1     # toFileUpload
    2     url(r'^knowledgeGraph/toFileUpload/$', knowledge_graph.tofileUpload, name="knowledgeGraph_to_fileupload"),
    3     # FileUpload
    4     url(r'^knowledgeGraph/FileUpload/$', knowledge_graph.fileUpload, name="knowledgeGraph_fileupload"),
    View Code

    2. 文件下载:

    文件下载可通过直接的url访问然后返回response完成,注意此时不能使用ajax异步操作。

    1)下载单个文件:

     1 def download_file(request,data):
     2     try:
     3         write_model(data)
     4         file_name = "/".join(str(os.path.dirname(__file__)).replace("\", "/").split("/")[0:-2]) + "/download/" + data
     5         wrapper = FileWrapper(file(file_name))
     6         response = HttpResponse(wrapper, content_type="application/octet-stream")
     7         response['Content-Length'] = os.path.getsize(file_name)
     8         response['Content-Disposition'] = 'attachment;filename=%s'% data
     9         messages.info(request, "下载成功")
    10         log.info("下载成功")
    11         return response
    12     except Exception as e:
    13         messages.warning(request, "下载失败 " + str(e))
    14         log.warning("下载失败 " + str(e))
    15         return HttpResponseRedirect("/index/")
    View Code

    2)下载多个文件,此时一般情况下使用zip下载,或者使用服务器推送服务(并不知道怎么实现)

    这种方法是网上可以轻松找到,但是我实现的时候每次下载的zip包大小都是0kb,通过调试,猜测应该是临时文件问题,无法把临时文件放入压缩包内,导致压缩包大小为空。

     1 def download_zipfile(request,data):
     2 
     3     try:
     4         write_model(model="KnowledgeGraph")
     5         temp = tempfile.TemporaryFile()
     6         archive = zipfile.ZipFile(temp, mode='w')
     7         dir_name = "/".join(str(os.path.dirname(__file__)).replace("\", "/").split("/")[0:-2]) + "/download/"
     8         filelist = []
     9         for root, dirlist, files in os.walk(dir_name):
    10             for filename in files:
    11                 filelist.append(os.path.join(root, filename))
    12         for eachfile in filelist:
    13             destfile = eachfile[len(dir_name):]
    14             print "Zip file %s..." % destfile
    15             archive.write(eachfile, destfile)
    16         archive.close()
    17         wrapper = FileWrapper(temp)
    18         response = http.HttpResponse(wrapper, content_type='application/x-zip-compressed')
    19         response['Content-Disposition'] = 'attachment; filename=model.zip'
    20         response['Content-Length'] = temp.tell()
    21         print temp.tell()
    22         temp.seek(0)
    23         return response
    24     except Exception as e:
    25         print e
    26         messages.info(request, "下载失败")
    27         return HttpResponseRedirect("/index/")
    View Code

    第二种方法成功实现压缩包下载,这种方法是在github上找到,供参考:

     1 def download_zipfile(request,data):
     2     try:
     3         dir_name = "/".join(str(os.path.dirname(__file__)).replace("\", "/").split("/")[0:-2]) + "/download/"
     4         files = []
     5         for ev in data:
     6             input_file = dir_name + ev
     7             text = open(input_file).readlines()
     8             #创建临时文件
     9             output_file = tempfile.NamedTemporaryFile(mode='w+b', prefix='base', suffix='.txt', delete=False)
    10             #output_file = tempfile.NamedTemporaryFile(mode='w+b', prefix='editor', suffix='.xml', delete=False)
    11             output_file.writelines(text)
    12             files.append(output_file.name)
    13             output_file.close()
    14         zip_filename = "model.zip"
    15         s = StringIO.StringIO()
    16         zf = zipfile.ZipFile(s, "a")
    17         #将临时文件加入zip包
    18         for i in range(0, len(files)):
    19             #设置文件名
    20             zip_path = os.path.join(data[i])
    21             zf.write(files[i], zip_path)
    22         zf.close()
    23         response = HttpResponse(s.getvalue(), content_type="application/x-zip-compressed")
    24         response['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
    25         messages.info(request, "下载成功")
    26         log.info("下载成功")
    27         return response
    28     except Exception as e:
    29         messages.warning(request, "下载失败 " + str(e))
    30         log.warning("下载失败 " + str(e))
    31         return HttpResponseRedirect("/index/")
    View Code
  • 相关阅读:
    awk线程号
    std::string::substr函数
    计数器表的简单使用
    vim + oh-my-zsh + git搭建开发环境
    <<代码大全>>阅读笔记之二 变量名的力量
    <<代码大全>>阅读笔记之一 使用变量的一般事项
    压测工具ab的简单使用
    nginx配置文件详解
    numba初体验
    Linux查找文件内容小技巧
  • 原文地址:https://www.cnblogs.com/z1987/p/5490033.html
Copyright © 2020-2023  润新知