Django ---- blog项目学习所得
一、登录功能
1、采用ajax 提交form表单的方式
2、后台生成随机验证码,登录时提交验证码
3、用PLI库生成随机验证码,置于session中,登录时与前台提交的code进行upeer()的验证
- <div class="col-lg-6">
- <img height="" width="" src="/get_code/" alt="">
- </div>
二、首页
1、index.html分别采用头和container的布局
- <div class="my_head">
- <div class="container-fluid">
- <div class="row">
- <div class="col-md-3">
- <div class="panel panel-danger">
- <div class="panel-heading">广告信息</div>
- <div class="panel-body">
- 重金求子
- </div>
- </div>
- <div class="col-md-6">
- <div class="col-md-3">
2、head处理时,如果已经登录,则显示username
- {% if request.user.is_authenticated %}
- <li><a href="/login/">{{ request.user.username }}</a></li>
- <li><a href="#"></a></li>
- <li class="dropdown">
- <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
- aria-haspopup="true" aria-expanded="false">更多信息 <span class="caret"></span></a>
- <ul class="dropdown-menu">
- <li><a href="#">修改密码</a></li>
- <li><a href="#">修改头像</a></li>
- <li><a href="/logout/">退出登录</a></li>
- </ul>
- </li>
- {% else %}
- <li><a href="/login/">登录</a></li>
- <li><a href="/register/">注册</a></li>
- {% endif %}
3、class="media-left"将div分别放于同一行左右侧
- class="glyphicon glyphicon-comment"图标的话可以采用bootcss的、也可以用static里的font-awesome的拿来即用
- <div class="col-md-6">
- {% for articles in articles_list %}
- <p><h4><a href="">{{ articles.title }}</a></h4></p>
- <div class="media-left"><img height="" width=""
- src="/media/{{ articles.user.avatar }}"
- alt=""></div>
- <div class="media-right">
- {{ articles.desc }}
- </div>
- <div style="margin-top: 10px">
- <span><a href="">{{ articles.user.username }}</a></span> 
- <span>发布于:{{ articles.create_time|date:'Y-m-d' }}</span> 
- <span class="glyphicon glyphicon-comment">评论数({{ articles.comment_count}})</span> 
- <span class="glyphicon glyphicon-thumbs-up">点赞数({{ articles.up_count}})</span> 
- </div>
- <hr>
- {% endfor %}
- </div>
- def index(request):
- articles_list = models.Article.objects.all()
- return render(request, 'index.html', locals())
三、注册的头像设计
1、为了使得点击字体或者图片都能发生jquery的change事件,label标签的for属性和input的id属性一样即可
- <div class="form-group">
- <label for="my_file">头像
- <img height="60px" width="60px" src="/static/img/default.png" id="id_img">
- </label>
- <input type="file" id="my_file">
- </div>
2、读取图片内容到img标签里
- $("#my_file").change(function () {
- alert()
- //$("#my_file")[0].files[0] 取出文件
- var obj=$("#my_file")[].files[]
- //生成一个文件阅读器
- var read=new FileReader()
- //把文件读到我的对象里
- read.readAsDataURL(obj)
- read.onload=function(){
- $("#id_img").attr('src',read.result)
- }
- {#$("#id_img").attr('aa','bb')#}
- })
3、同时需要将input文件的字去掉
- <style>
- #my_file {
- display: none;
- }
- </style>
4、form表单有两个功能,一个是校验字段,一个是生成input标签
- {% for foo in form_obj %}
- <div class="form-group">
- <label for="">{{ foo.label }}</label>
- {{ foo }} <span class="errors"></span>
- </div>
- {% endfor %}
5、1和2的方式会在form里面直接提交form表单,此处想用ajax的方式提交,采用3的方法
- {# 1、 <input type="submit">#}
- {# 2、 <button></button>#}
- 3、 <input type="button" id="id_button" class="btn btn-success" value="注册">
四、form组件局部钩子和全局钩子使用
1、在view.py里创建forms类,有全局和局部钩子
- from django import forms
- from django.forms import widgets
- class RegForms(forms.Form):
- name = forms.CharField(max_length=, min_length=, label='用户名',
- widget=widgets.TextInput(attrs={'class': 'form-control'}),
- error_messages={'max_length': '太长了', 'min_length': '太短了'}
- )
- pwd = forms.CharField(max_length=, min_length=, label='密码',
- widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
- error_messages={'max_length': '太长了', 'min_length': '太短了'}
- )
- re_pwd = forms.CharField(max_length=, min_length=, label='确认密码',
- widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
- error_messages={'max_length': '太长了', 'min_length': '太短了'}
- )
- email = forms.EmailField(label='邮箱',
- widget=widgets.EmailInput(attrs={'class': 'form-control'}),
- )
- def clean_name(self):
- name = self.cleaned_data.get('name')
- user = models.UserInfo.objects.filter(username=name).first()
- if user:
- raise ValidationError('用户已经存在')
- else:
- return name
- def clean(self):
- pwd = self.cleaned_data.get('pwd')
- re_pwd = self.cleaned_data.get('re_pwd')
- if pwd == re_pwd:
- return self.cleaned_data
- else:
- raise ValidationError('两次密码不一致')
2、form校验了request.post之后存在两种数据:
- # form_obj.cleaned_data
- # form_obj.errors
- def register(request):
- form_obj = RegForms()
- back_msg = {}
- if request.is_ajax():
- name = request.POST.get('name')
- pwd = request.POST.get('pwd')
- re_pwd = request.POST.get('re_pwd')
- email = request.POST.get('email')
- myfile = request.FILES.get('myfile')
- print(myfile)
- print(re_pwd)
- form_obj = RegForms(request.POST)
- if form_obj.is_valid():
- # form_obj.cleaned_data
- # form_obj.errors
- if myfile:
- user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email, avatar=myfile)
- else:
- user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email)
- back_msg['user'] = name
- back_msg['msg'] = '注册成功'
- else:
- back_msg['msg'] = form_obj.errors
- print(form_obj.errors)
- print(type(form_obj.errors))
- return JsonResponse(back_msg)
- return render(request, 'register.html', {'form_obj': form_obj})
3、ajax提交文件的话,用formdata
1、each函数,循环
2、if(index=="__all__")判断是否为全局钩子,
3、加class=has-error的属性,即被错误提示选中
4、setTimeout(function () 定时函数执行某件事
- $("#id_button").click(function () {
- var formdata=new FormData()
- var tt=$("#my_form").serializeArray()
- $.each(tt,function (index,value) {
- //console.log(value.name)
- //console.log(value.value)
- formdata.append(value.name,value.value)
- })
- formdata.append('myfile',$("#my_file")[].files[])
- // console.log(tt)
- $.ajax({
- url:'',
- type:'post',
- processData:false,
- contentType:false,
- data:formdata,
- success:function (data) {
- //console.log(data)
- if(data.user){
- location.href='/login/'
- }else{
- $.each(data.msg,function (index,value) {
- console.log(index)
- console.log(value)
- if(index=="__all__"){
- $("#id_re_pwd").next().text(value[])
- }
- $("#id_"+index).next().text(value[]).parent().addClass('has-error')
- //$("#id_"+index).next().parent().addClass('has-error')
- })
- setTimeout(function () {
- $(".form-group").removeClass('has-error')
- $('span').text("")
- },)
- }
- }
- })
- })
五、注册
1、var tt=$("#my_form").serializeArray() 自动的将my_form里的name和value打包起来
2、传文件的话,必须
- processData:false,
- contentType:false,否则会以url-encoded的方法传送了
- $("#id_button").click(function () {
- var formdata=new FormData()
- var tt=$("#my_form").serializeArray()
- $.each(tt,function (index,value) {
- //console.log(value.name)
- //console.log(value.value)
- formdata.append(value.name,value.value)
- })
- formdata.append('myfile',$("#my_file")[].files[])
- // console.log(tt)
- $.ajax({
- url:'',
- type:'post',
- processData:false,
- contentType:false,
- data:formdata,
- success:function (data) {
- //console.log(data)
- if(data.user){
- location.href='/login/'
- }else{
- $.each(data.msg,function (index,value) {
- console.log(index)
- console.log(value)
- if(index=="__all__"){
- $("#id_re_pwd").next().text(value[])
- }
- $("#id_"+index).next().text(value[]).parent().addClass('has-error')
- //$("#id_"+index).next().parent().addClass('has-error')
- })
- setTimeout(function () {
- $(".form-group").removeClass('has-error')
- $('span').text("")
- },)
- }
- }
- })
- })
4、create生成数据时,avatar=myfile,即直接传入文件对象即可
- def register(request):
- form_obj = RegForms()
- back_msg = {}
- if request.is_ajax():
- name = request.POST.get('name')
- pwd = request.POST.get('pwd')
- re_pwd = request.POST.get('re_pwd')
- email = request.POST.get('email')
- myfile = request.FILES.get('myfile')
- print(myfile)
- print(re_pwd)
- form_obj = RegForms(request.POST)
- if form_obj.is_valid():
- # form_obj.cleaned_data
- # form_obj.errors
- if myfile:
- user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email, avatar=myfile)
- else:
- user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email)
- back_msg['user'] = name
- back_msg['msg'] = '注册成功'
- else:
- back_msg['msg'] = form_obj.errors
- print(form_obj.errors)
- print(type(form_obj.errors))
- return JsonResponse(back_msg)
- return render(request, 'register.html', {'form_obj': form_obj})
5、models里加入此句的意思 upload_to='avatars/', default="/avatars/default.png",上传到MEDIA_ROOT + avatars路径,前面不能再加media,否则路径多了一层media的路径
- settings.py
- # 设置用户上传头像的根路径
- MEDIA_ROOT=os.path.join(BASE_DIR,'media')
6、默认图片在media/avatars/default.png
- class UserInfo(AbstractUser):
- """
- 用户信息
- """
- nid = models.AutoField(primary_key=True)
- telephone = models.CharField(max_length=, null=True, unique=True)
- avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png")
- # auto_now_add=True
- '''
- auto_now_add
- 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
- auto_now
- 配置上auto_now=True,每次更新数据记录的时候会更新该字段。
- '''
- create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
- blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE)
- def __str__(self):
- return self.username
六、media文件夹的配置
1、static文件夹下的内容外部人都可以通过url访问到
- STATIC_URL = '/static/'
- STATICFILES_DIRS=[
- os.path.join(BASE_DIR,'static'),
- ]
- # 设置用户上传头像的根路径
MEDIA_ROOT=os.path.join(BASE_DIR,'media')
2、用户上传的图片放在static不合适,需要一个专门的media的文件夹存储,直接127.0.0.1/media/avatars/default.png就可以访问到了
- from django.views.static import serve
#def serve(request, path, document_root=None, show_indexes=False):
- url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
慎用此方法,如不小心,代码会暴露被外部访问到
3、如此,即可在首页index.html里将路径改为 src="/media/{{ articles.user.avatar }},即可访问
- {% for articles in articles_list %}
- <p><h4><a href="">{{ articles.title }}</a></h4></p>
- <div class="media-left"><img height="" width=""
- src="/media/{{ articles.user.avatar }}"
- alt=""></div>
- <div class="media-right">
- {{ articles.desc }}
- </div>
- <div style="margin-top: 10px">
- <span><a href="">{{ articles.user.username }}</a></span> 
- <span>发布于:{{ articles.create_time|date:'Y-m-d' }}</span> 
- <span class="glyphicon glyphicon-comment">评论数({{ articles.comment_count}})</span> 
- <span class="glyphicon glyphicon-thumbs-up">点赞数({{ articles.up_count}})</span> 
- </div>
- <hr>
- {% endfor %}
七、分组查询
1、views.py
- url(r'^(?P<username>\w+)/(?P<condition>tag|category|time|\w+)/(?P<search>.*)', views.homesite),
- url(r'^(?P<username>\w+)/', views.homesite),
2、后台数据分析
- 2.1 blog = user.blog 此为查找时的对象
- 2.2 分组查询1 group by谁,用谁做基表
- # 2 filter在前,表示查询 ,filter在后,表示过滤,having
- # 3 values在前,表示group by 在后,取字段
- def homesite(request, username, **kwargs):
- print(kwargs)
- username=username
- user = models.UserInfo.objects.filter(username=username).first()
- if not user:
- return render(request, 'errors.html')
- blog = user.blog
- article_list = models.Article.objects.filter(user=user)
- if kwargs:
- condition=kwargs.get('condition')
- search=kwargs.get('search')
- if condition=='tag':
- article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
- elif condition=='category':
- article_list = models.Article.objects.filter(user=user).filter(category__title=search)
- elif condition=='time':
- ll=search.split('-')
- article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[],create_time__month=ll[])
- else:
- return render(request, 'errors.html')
- # print(article_list)
- from django.db.models import Count
- # 查询每个标签下的文章数(分组查询)
- # 分组查询1 group by谁,用谁做基表
- # filter在前,表示查询 ,filter在后,表示过滤,having
- # values在前,表示group by 在后,取字段
- # 查询当前站点下每个标签下的文章数(分组查询)
- tag_count = models.Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values_list('title', 'c')
- # 查询当前站点下每个分类下的文章数
- category_count = models.Category.objects.filter(blog=blog).annotate(c=Count('article__nid')).values_list('title',
- 'c')
- from django.db.models.functions import TruncMonth
- # 查询当前站点每个月份下的文章数
- # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
- # for i in time_count:
- # print(i.title)
- # print(i.y_m)
- time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
- c=Count('y_m')).values_list('y_m', 'c')
- print(tag_count)
- print(category_count)
- print(time_count)
- # 统计每个出版社书籍个数
- # Publish.object.all().annotate(Count('book__title'))
- return render(request, 'homesite.html', locals())
2、homesite.html
1、用admin添加数据
2、{{ foo.0 }}({{ foo.1 }}) ------------- xxxxxxxxxxx(xx)
- <div class="container-fluid" style="margin-top: 10px">
- <div class="row">
- <div class="col-md-3">
- <div class="panel panel-danger">
- <div class="panel-heading">我的标签</div>
- <div class="panel-body">
- {% for foo in tag_count %}
- <p><a href="/{{ username }}/tag/{{ foo.0 }}">{{ foo. }}({{ foo. }})</a></p>
- {% endfor %}
- </div>
- </div>
- <div class="panel panel-info">
- <div class="panel-heading">随笔分类</div>
- <div class="panel-body">
- {% for foo in category_count %}
- <p><a href="/{{ username }}/category/{{ foo.0 }}">{{ foo. }}({{ foo. }})</a></p>
- {% endfor %}
- </div>
- </div>
- <div class="panel panel-danger">
- <div class="panel-heading">随笔档案</div>
- <div class="panel-body">
- {% for foo in time_count %}
- <p><a href="/{{ username }}/time/{{ foo.0|date:'Y-m' }}">{{ foo.|date:'Y-m' }}({{ foo. }})</a></p>
- {% endfor %}
- </div>
- </div>
- </div>
八、个人站点路由设计
将此条至于最下方的目的,前面都匹配不了的时候,到此可以匹配成功进入视图函数,有则看,无资源则显示404
- url(r'^(?P<username>\w+)/', views.homesite),
九、个人站点过滤
1、总的设计
- def homesite(request, username, **kwargs):
- print(kwargs)
- username=username
- user = models.UserInfo.objects.filter(username=username).first()
- if not user:
- return render(request, 'errors.html')
- blog = user.blog
- article_list = models.Article.objects.filter(user=user)
- if kwargs:
- condition=kwargs.get('condition')
- search=kwargs.get('search')
- if condition=='tag':
- article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
- elif condition=='category':
- article_list = models.Article.objects.filter(user=user).filter(category__title=search)
- elif condition=='time':
- ll=search.split('-')
- article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[],create_time__month=ll[])
- else:
- return render(request, 'errors.html')
- # print(article_list)
- from django.db.models import Count
- # 查询每个标签下的文章数(分组查询)
- # 分组查询1 group by谁,用谁做基表
- # filter在前,表示查询 ,filter在后,表示过滤,having
- # values在前,表示group by 在后,取字段
- # 查询当前站点下每个标签下的文章数(分组查询)
- tag_count = models.Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values_list('title', 'c')
- # 查询当前站点下每个分类下的文章数
- category_count = models.Category.objects.filter(blog=blog).annotate(c=Count('article__nid')).values_list('title',
- 'c')
- from django.db.models.functions import TruncMonth
- # 查询当前站点每个月份下的文章数
- # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
- # for i in time_count:
- # print(i.title)
- # print(i.y_m)
- time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
- c=Count('y_m')).values_list('y_m', 'c')
- print(tag_count)
- print(category_count)
- print(time_count)
- # 统计每个出版社书籍个数
- # Publish.object.all().annotate(Count('book__title'))
- return render(request, 'homesite.html', locals())
2、按时间分类,用TruncMonth截断年月,会在原表的基础上在加一条时间为年月的数据,再以此分类(group by谁,用谁做基表)
- from django.db.models.functions import TruncMonth
- # 查询当前站点每个月份下的文章数
- # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
- # for i in time_count:
- # print(i.title)
- # print(i.y_m)
- time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
- c=Count('y_m')).values_list('y_m', 'c')
3、分类取得数据,页面展示article_list数据,只会显示符合的文章show
- if kwargs:
- condition=kwargs.get('condition')
- search=kwargs.get('search')
- if condition=='tag':
- article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
- elif condition=='category':
- article_list = models.Article.objects.filter(user=user).filter(category__title=search)
- elif condition=='time':
- ll=search.split('-')
- article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[],create_time__month=ll[])
- else:
- return render(request, 'errors.html')
4、相应的前台html是如下,加入a连接和url的处理,可以看得相应的数据
- <div class="panel panel-danger">
- <div class="panel-heading">我的标签</div>
- <div class="panel-body">
- {% for foo in tag_count %}
- <p><a href="/{{ username }}/tag/{{ foo.0 }}">{{ foo. }}({{ foo. }})</a></p>
- {% endfor %}
- </div>
- </div>
- <div class="panel panel-info">
- <div class="panel-heading">随笔分类</div>
- <div class="panel-body">
- {% for foo in category_count %}
- <p><a href="/{{ username }}/category/{{ foo.0 }}">{{ foo. }}({{ foo. }})</a></p>
- {% endfor %}
- </div>
- </div>
- <div class="panel panel-danger">
- <div class="panel-heading">随笔档案</div>
- <div class="panel-body">
- {% for foo in time_count %}
- <p><a href="/{{ username }}/time/{{ foo.0|date:'Y-m' }}">{{ foo.|date:'Y-m' }}({{ foo. }})</a></p>
- {% endfor %}
- </div>
- </div>
十、后台管理页面
1、布局上还是采用了上为header,左三右九的布局
- <style>
- .home_head {
height: 60px;
background: #1b6d85;
}
</style>
- <div class="home_head"></div>
- <div class="container-fluid" style="margin-top: 10px">
- <div class="row">
- <div class="col-md-3"。。。。。。。。。。。。
- <div class="col-md-9"
- </div>
- </div>
- </body>
2、中间采用bootstrap的标签页的方式,达到点击之后会用切换content和header标签的效果
- <div class="col-md-9">
- <div>
- <!-- Nav tabs -->
- <ul class="nav nav-tabs" role="tablist">
- <li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab"
- data-toggle="tab">文章</a></li>
- <li role="presentation"><a href="#profile" aria-controls="profile" role="tab"
- data-toggle="tab">随笔</a></li>
- <li role="presentation"><a href="#messages" aria-controls="messages" role="tab"
- data-toggle="tab">交友</a></li>
- <li role="presentation"><a href="#settings" aria-controls="settings" role="tab"
- data-toggle="tab">园子</a></li>
- </ul>
- <!-- Tab panes -->
- <div class="tab-content">
- <div role="tabpanel" class="tab-pane active" id="home">
- {% block back_content %}
- {% endblock %}
- </div>
- <div role="tabpanel" class="tab-pane" id="profile">随笔</div>
- <div role="tabpanel" class="tab-pane" id="messages">...</div>
- <div role="tabpanel" class="tab-pane" id="settings">...</div>
- </div>
- </div>
3、需要用户登录了之后才能进入后台用户管理界面,故login_required装饰器判断是否登录,未登录则直接跳转到login_url='/login/'界面
- from django.contrib.auth.decorators import login_required
- @login_required(login_url='/login/')
- def back_home(request):
- article_list=models.Article.objects.filter(user=request.user)
- return render(request,'back/back_home.html',locals())
4、左侧采用collapse方式,点击会展开
- <div class="col-md-3">
- <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
- <div class="panel panel-default">
- <div class="panel-heading" role="tab" id="headingOne">
- <h4 class="panel-title">
- <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
- aria-expanded="true" aria-controls="collapseOne">
- 操作
- </a>
- </h4>
- </div>
- <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
- aria-labelledby="headingOne">
- <div class="panel-body">
- <a href="/addarticle/">添加文章</a>
- </div>
- <div class="panel-body">
- <a href="">添加随笔</a>
- </div>
- </div>
- </div>
- </div>
5、用block 导入基准html
- {% extends 'back/back_base.html' %}
- {% block back_content %}
- <table class="table table-striped table-hover">
- <thead>
- <tr>
- <th>文章标题</th>
- <th>评论数</th>
- <th>点赞数</th>
- <th>操作</th>
- <th>操作</th>
- </tr>
- </thead>
- <tbody>
- {% for article in article_list %}
- <tr>
- <td>{{ article.title }}</td>
- <td>{{ article.comment_count }}</td>
- <td>{{ article.up_count }}</td>
- <td><a href="">修改</a></td>
- <td><a href="">删除</a></td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
- {% endblock %}
十一、后台管理保存文章
1、在html页面使用了Kindedit编辑器,只需将库包至于static目录下即可,需保证
- <textarea name="content" id="editor_id" cols="50" rows="10"></textarea>的id与kindedit一样,即可被调用
- {% extends 'back/back_base.html' %}
- {% block back_content %}
- <div>
- <form action="" method="post">
- {% csrf_token %}
- <div class="text-info"><h4>添加文章</h4></div>
- <p>标题</p>
- <p><input type="text" class="form-control" name="title"></p>
- <p>内容(Kindedit编辑器,不支持拖放/粘贴上传图片)</p>
- <p>
- <textarea name="content" id="editor_id" cols="" rows=""></textarea>
- </p>
- <p>
- <button class="btn btn-primary">提交</button>
- </p>
- </form>
- </div>
- <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
- <script>
- KindEditor.ready(function (K) {
- window.editor = K.create('#editor_id',
- {
- width: '100%',
- height: '600px',
- resizeType: ,
- }
- );
- });
- </script>
- {% endblock %}
2、上传文字需要用bs4清洗,html显示的时候只需要content内容,但是详情页面的时候需要html样式等,故存储str(content)
- # 安装 pip3 install lxml
# pip3 install BeautifulSoup4
@login_required(login_url='/login/')
def addarticle(request):
if request.method=='POST':
title=request.POST.get('title')
content=request.POST.get('content'
)
# content='<p>sddd</p><a>oooo</a><script>alert(123)</script>'
#生成一个soup的对象,传两个参数,第一个是要解析的html,第二个是使用的解析器
soup=BeautifulSoup(content,'html.parser')
print(str(soup))
# 拿到html内的文本内容
desc=soup.text[0:150]+'...'
# 查找html内所有的标签,放到一个列表里
ll=soup.find_all()
# print(ll) #[obj,obj...]
for tag in ll:
print(tag.name)
print(type(tag))
# tag.name 标签的名称
if tag.name=='script':
# 从整个html文档里删掉当前标签
tag.decompose()
# print(ll)
# aa=soup.find(name='script')
# print(aa)
# print(str(soup)) #<p>sddd</p><a>oooo</a><script>alert(123)</script>
- models.Article.objects.create(title=title,content=str(soup),desc=desc,user=request.user)
return redirect('/backhome/')- return render(request,'back/article_add.html')
3、在back_base界面, <a href="/addarticle/">添加文章</a>关联上述所诉逻辑
- <div class="col-md-3">
- <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
- <div class="panel panel-default">
- <div class="panel-heading" role="tab" id="headingOne">
- <h4 class="panel-title">
- <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
- aria-expanded="true" aria-controls="collapseOne">
- 操作
- </a>
- </h4>
- </div>
- <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
- aria-labelledby="headingOne">
- <div class="panel-body">
- <a href="/addarticle/">添加文章</a>
- </div>
- <div class="panel-body">
- <a href="">添加随笔</a>
- </div>
- </div>
- </div>
- </div>
十二、点赞、点踩
1、articledetail页面的设计,继承自base.html,评论html直接引用自博客园的html和css样式
- {% extends 'base.html' %}
- {% block detail %}
- <style>
- #div_digg {
- float: right;
- margin-bottom: 10px;
- margin-right: 30px;
- font-size: 12px;
- width: 125px;
- text-align: center;
- margin-top: 10px;
- }
- .diggit {
- float: left;
- width: 46px;
- height: 52px;
- background: url(/static/img/upup.gif) no-repeat;
- text-align: center;
- cursor: pointer;
- margin-top: 2px;
- padding-top: 5px;
- }
- .buryit {
- float: right;
- margin-left: 20px;
- width: 46px;
- height: 52px;
- background: url(/static/img/downdown.gif) no-repeat;
- text-align: center;
- cursor: pointer;
- margin-top: 2px;
- padding-top: 5px;
- }
- .clear {
- clear: both;
- }
- </style>
- <div>
- <p><h4 class="text-center">{{ article.title }}</h4></p>
- <p>{{ article.content|safe }}</p>
- <div class="clearfix">
- <div id="div_digg">
- <div class="diggit action">
- <span class="diggnum" id="digg_count">{{ article.up_count }}</span>
- </div>
- <div class="buryit action">
- <span class="burynum" id="bury_count">{{ article.down_count }}</span>
- </div>
- <div class="clear"></div>
- <div class="diggword" id="digg_tips" style="color: red;"></div>
- </div>
- </div>
2、is_up = $(this).hasClass('diggit')结果要么是true或者false,
- var obj = $(this).children('span'),当点击时要么是点up,down的,this代指diggit、buryit事件,这句话的意思是obj为其子节点为span的对象
- <script>
- var par_id = '';
- $(".action").click(function () {
- var is_up = $(this).hasClass('diggit')
- //alert(is_up)
- var obj = $(this).children('span')
- $.ajax({
- url: '/diggit/',
- type: 'post',
- //谁对那篇文章点赞或点踩
- data: {
- 'csrfmiddlewaretoken': '{{ csrf_token }}',
- 'article_id':{{ article.pk }},
- 'is_up': is_up
- },
- success: function (data) {
- console.log(data)
- $(".diggword").text(data.msg)
- if (data.status == ) {
- obj.text(Number(obj.text()) + )
- }
- }
- })
- })
3、articledetail页面展示,因为展示方法重复了,可以把下面重用的包装成共用函数
- url(r'^(?P<username>\w+)/article/(?P<pk>\d+)', views.article_detail),
- url(r'^diggit/', views.diggit),
- def article_detail(request,username,pk):
- # 正常情况下应该有一堆安全性校验
- #
- user=models.UserInfo.objects.filter(username=username).first()
- blog=user.blog
- article=models.Article.objects.filter(pk=pk).first()
- # 获取当前文章下所有评论
- comment_list=models.Comment.objects.filter(article_id=pk)
- from django.db.models import Count
- # 查询每个标签下的文章数(分组查询)
- # 分组查询1 group by谁,用谁做基表
- # filter在前,表示查询 ,filter在后,表示过滤,having
- # values在前,表示group by 在后,取字段
- # 查询当前站点下每个标签下的文章数(分组查询)
- # tag_count = models.Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values_list('title', 'c')
- tag_count = models.Tag.objects.all().values('pk').filter(blog=blog).annotate(c=Count("article__title")).values_list(
- 'title', 'c')
- # 查询当前站点下每个分类下的文章数
- category_count = models.Category.objects.filter(blog=blog).annotate(c=Count('article__nid')).values_list('title',
- 'c')
- from django.db.models.functions import TruncMonth
- # 查询当前站点每个月份下的文章数
- # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
- # for i in time_count:
- # print(i.title)
- # print(i.y_m)
- time_count = models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values(
- 'y_m').annotate(
- c=Count('y_m')).values_list('y_m', 'c')
- return render(request,'article_detail.html',locals())
4、注释的时候将logic写清楚
post传过来的都是str类型的,需要转化成python认识的bool类型,通过json反序列化,is_up=json.loads(is_up)
- import json
- from django.db.models import F
- def diggit(request):
- back_msg={'status':None,'msg':None}
- if request.user.is_authenticated:
- # 先查询是否已经点过
- # 如果没有点过,去点赞表存数据
- # 去文章表修改点赞数据
- article_id=request.POST.get('article_id')
- # 注意:
- is_up=request.POST.get('is_up')
- print(type(is_up))
- is_up=json.loads(is_up)
- ret=models.ArticleUpDown.objects.filter(user=request.user,article_id=article_id)
- if not ret:
- models.ArticleUpDown.objects.create(user=request.user,article_id=article_id,is_up=is_up)
- if is_up:
- models.Article.objects.filter(pk=article_id).update(up_count=F('up_count')+)
- back_msg['status']=
- back_msg['msg'] = '点赞成功'
- else:
- models.Article.objects.filter(pk=article_id).update(down_count=F('down_count') + )
- back_msg['status'] =
- back_msg['msg'] = '点踩成功'
- else:
- back_msg['status'] =
- back_msg['msg'] = '您已经点过了'
- else:
- back_msg['status'] =
- back_msg['msg'] = '您没有登录'
- return JsonResponse(back_msg)
十三、评论(render显示和ajax显示)
1、提交评论
- $(".btn_submit").click(function () {
- var content = $("#comment_content").val()
- if (par_id) {
- var num = content.indexOf('\n') +
- content = content.slice(num)
- alert(content)
- }
- $.ajax({
- url: '/comment/',
- type: 'post',
- data: {
- 'csrfmiddlewaretoken': '{{ csrf_token }}',
- 'article_id':{{ article.pk }},
- 'comment': content,
- 'par_id': par_id
- },
- success: function (data) {
- $("#comment_content").val("")
- console.log(data)
为了确保数据写入,创建事务with transaction.atomic():,失败则回滚
- def comment(request):
- back_msg={'status':False,'msg':None}
- if request.user.is_authenticated:
- # 评论表添加数据
- # 文章表,修改评论个数
- article_id=request.POST.get('article_id')
- comment=request.POST.get('comment')
- par_id=request.POST.get('par_id')
- # 事务
- with transaction.atomic():
- ret=models.Comment.objects.create(user=request.user,article_id=article_id,content=comment,parent_comment_id=par_id)
- models.Article.objects.filter(pk=article_id).update(comment_count=F('comment_count')+)
- back_msg['status']=True
- back_msg['user_name']=ret.user.username
- back_msg['time']=ret.create_time.strftime('%Y-%m-%d')
- back_msg['content']=ret.content
- back_msg['msg']='评论成功'
- else:
- back_msg['status'] = False
- back_msg['msg'] = '您没有登录'
- return JsonResponse(back_msg)
2、render显示,刷新界面才显示评论更新上传了
- <div>
- <ul class="list-group comment_list">
- {% for comment in comment_list %}
- {# #17楼 -- : 隔壁古二蛋 #}
- <li class="list-group-item">
- <span>#{{ forloop.counter }}楼</span>
- <span>{{ comment.create_time|date:'Y-m-d' }}</span>
- <span>{{ comment.user.username }}</span>
- <span><a class="pull-right my_reply" user="{{ comment.user.username }}"
- comment_id="{{ comment.pk }}">回复</a></span><p>{{ comment.content }}</p>
- </li>
- {% endfor %}
- </ul>
- </div>
3、提交根评论及ajax显示,及render显示,
- {# 评论 #}
- <div>
- <p>发表评论</p>
- <p>
- 昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="" value="刘清政">
- </p>
- <p>评论内容</p>
- <p><textarea name="" id="comment_content" cols="" rows=""></textarea></p>
- <button class="btn btn-primary btn_submit">回复</button>
- </div>
es6字符串替换,<span>${ time }</span>,需要什么比如time,根据后台去取
- if (data.status) {
- var username = data.user_name;
- var time = data.time;
- var content = data.content
- var par_name=data.par_name
- var par_content=data.par_content
- var ss=''
- if(par_name){
- ss = `
- <li class="list-group-item">
- <span>${ time }</span>
- <span>${ username}</span>
- <div class="well">
- <span>@ ${par_name}</span>
- <p>${par_content}</p>
- </div>
- <p>${ content }</p>
- </li>
- `
- }else{
- ss = `
- <li class="list-group-item">
- <span>${ time }</span>
- <span>${ username}</span>
- <p>${ content }</p>
- </li>
- `
- }
- $(".comment_list").append(ss)
- }
- }
- })
- })
后台
- from django.db import transaction
- def comment(request):
- back_msg={'status':False,'msg':None}
- if request.user.is_authenticated:
- # 评论表添加数据
- # 文章表,修改评论个数
- article_id=request.POST.get('article_id')
- comment=request.POST.get('comment')
- par_id=request.POST.get('par_id')
- # 事物
- with transaction.atomic():
- ret=models.Comment.objects.create(user=request.user,article_id=article_id,content=comment,parent_comment_id=par_id)
- models.Article.objects.filter(pk=article_id).update(comment_count=F('comment_count')+)
- if par_id:
- back_msg['par_name']=ret.parent_comment.user.username
- back_msg['par_content']=ret.parent_comment.content
- back_msg['status']=True
- back_msg['user_name']=ret.user.username
- back_msg['time']=ret.create_time.strftime('%Y-%m-%d')
- back_msg['content']=ret.content
- back_msg['msg']='评论成功'
- else:
- back_msg['status'] = False
- back_msg['msg'] = '您没有登录'
- return JsonResponse(back_msg)
4、子评论,点击回复触发事件,同时将要用的父评论ID等信息直接放置于attr属性上,方便调用
- <ul class="list-group comment_list">
- {% for comment in comment_list %}
- {# #17楼 -- : 隔壁古二蛋 #}
- <li class="list-group-item">
- <span>#{{ forloop.counter }}楼</span>
- <span>{{ comment.create_time|date:'Y-m-d' }}</span>
- <span>{{ comment.user.username }}</span>
- <span><a class="pull-right my_reply" user="{{ comment.user.username }}"
- comment_id="{{ comment.pk }}">回复</a></span>
- {% if comment.parent_comment %}
- <div class="well">
- <span>@{{ comment.parent_comment.user.username }}</span>
- <p>{{ comment.parent_comment.content }}</p>
- </div>
使得点击回复之后直接focus到textarea标签,写响应子评论
父评论par_id,定义全局,使得后面都可调用
- $(".my_reply").click(function () {
- var name = "@" + $(this).attr('user') + '\n'
- par_id = $(this).attr('comment_id')
- $("#comment_content").focus()
- $("#comment_content").val(name)
- })
5、判断是否为子评论,若是,则截取第一段内容,将后面的保存在content里,若不是,则不处理
- $(".btn_submit").click(function () {
- var content = $("#comment_content").val()
- if (par_id) {
- var num = content.indexOf('\n') +
- content = content.slice(num) #slice去索引之后的值
- alert(content)
- }
判断是否有父评论,有则加@父id+comment
- <ul class="list-group comment_list">
- {% for comment in comment_list %}
- {# #17楼 -- : 隔壁古二蛋 #}
- <li class="list-group-item">
- <span>#{{ forloop.counter }}楼</span>
- <span>{{ comment.create_time|date:'Y-m-d' }}</span>
- <span>{{ comment.user.username }}</span>
- <span><a class="pull-right my_reply" user="{{ comment.user.username }}"
- comment_id="{{ comment.pk }}">回复</a></span>
- {% if comment.parent_comment %}
- <div class="well">
- <span>@{{ comment.parent_comment.user.username }}</span>
- <p>{{ comment.parent_comment.content }}</p>
- </div>
- {% endif %}
- <p>{{ comment.content }}</p>
同时再做个ajax显示功能,及一直带有render的显示功能,需要什么参数,需要什么参数,去后台返回
- $(".btn_submit").click(function () {
- var content = $("#comment_content").val()
- if (par_id) {
- var num = content.indexOf('\n') +
- content = content.slice(num)
- alert(content)
- }
- $.ajax({
- url: '/comment/',
- type: 'post',
- data: {
- 'csrfmiddlewaretoken': '{{ csrf_token }}',
- 'article_id':{{ article.pk }},
- 'comment': content,
- 'par_id': par_id
- },
- success: function (data) {
- $("#comment_content").val("")
- console.log(data)
- if (data.status) {
- var username = data.user_name;
- var time = data.time;
- var content = data.content
- var par_name=data.par_name
- var par_content=data.par_content
- var ss=''
- if(par_name){
- ss = `
- <li class="list-group-item">
- <span>${ time }</span>
- <span>${ username}</span>
- <div class="well">
- <span>@ ${par_name}</span>
- <p>${par_content}</p>
- </div>
- <p>${ content }</p>
- </li>
- `
- }else{
- ss = `
- <li class="list-group-item">
- <span>${ time }</span>
- <span>${ username}</span>
- <p>${ content }</p>
- </li>
- `
- }
- $(".comment_list").append(ss)
- }
- }
- })
- })
6、总的,有根评论、子评论的ajax、render显示功能
- {% extends 'base.html' %}
- {% block detail %}
- <style>
- #div_digg {
- float: right;
- margin-bottom: 10px;
- margin-right: 30px;
- font-size: 12px;
- width: 125px;
- text-align: center;
- margin-top: 10px;
- }
- .diggit {
- float: left;
- width: 46px;
- height: 52px;
- background: url(/static/img/upup.gif) no-repeat;
- text-align: center;
- cursor: pointer;
- margin-top: 2px;
- padding-top: 5px;
- }
- .buryit {
- float: right;
- margin-left: 20px;
- width: 46px;
- height: 52px;
- background: url(/static/img/downdown.gif) no-repeat;
- text-align: center;
- cursor: pointer;
- margin-top: 2px;
- padding-top: 5px;
- }
- .clear {
- clear: both;
- }
- </style>
- <div>
- <p><h4 class="text-center">{{ article.title }}</h4></p>
- <p>{{ article.content|safe }}</p>
- <div class="clearfix">
- <div id="div_digg">
- <div class="diggit action">
- <span class="diggnum" id="digg_count">{{ article.up_count }}</span>
- </div>
- <div class="buryit action">
- <span class="burynum" id="bury_count">{{ article.down_count }}</span>
- </div>
- <div class="clear"></div>
- <div class="diggword" id="digg_tips" style="color: red;"></div>
- </div>
- </div>
- {# 评论列表#}
- <div>
- <ul class="list-group comment_list">
- {% for comment in comment_list %}
- {# #17楼 -- : 隔壁古二蛋 #}
- <li class="list-group-item">
- <span>#{{ forloop.counter }}楼</span>
- <span>{{ comment.create_time|date:'Y-m-d' }}</span>
- <span>{{ comment.user.username }}</span>
- <span><a class="pull-right my_reply" user="{{ comment.user.username }}"
- comment_id="{{ comment.pk }}">回复</a></span>
- {% if comment.parent_comment %}
- <div class="well">
- <span>@{{ comment.parent_comment.user.username }}</span>
- <p>{{ comment.parent_comment.content }}</p>
- </div>
- {% endif %}
- <p>{{ comment.content }}</p>
- </li>
- {% endfor %}
- </ul>
- </div>
- {# 评论 #}
- <div>
- <p>发表评论</p>
- <p>
- 昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="" value="刘清政">
- </p>
- <p>评论内容</p>
- <p><textarea name="" id="comment_content" cols="" rows=""></textarea></p>
- <button class="btn btn-primary btn_submit">回复</button>
- </div>
- </div>
- <script>
- var par_id = '';
- $(".action").click(function () {
- var is_up = $(this).hasClass('diggit')
- //alert(is_up)
- var obj = $(this).children('span')
- $.ajax({
- url: '/diggit/',
- type: 'post',
- //谁对那篇文章点赞或点踩
- data: {
- 'csrfmiddlewaretoken': '{{ csrf_token }}',
- 'article_id':{{ article.pk }},
- 'is_up': is_up
- },
- success: function (data) {
- console.log(data)
- $(".diggword").text(data.msg)
- if (data.status == ) {
- obj.text(Number(obj.text()) + )
- }
- }
- })
- })
- $(".btn_submit").click(function () {
- var content = $("#comment_content").val()
- if (par_id) {
- var num = content.indexOf('\n') +
- content = content.slice(num)
- alert(content)
- }
- $.ajax({
- url: '/comment/',
- type: 'post',
- data: {
- 'csrfmiddlewaretoken': '{{ csrf_token }}',
- 'article_id':{{ article.pk }},
- 'comment': content,
- 'par_id': par_id
- },
- success: function (data) {
- $("#comment_content").val("")
- console.log(data)
- if (data.status) {
- var username = data.user_name;
- var time = data.time;
- var content = data.content
- var par_name=data.par_name
- var par_content=data.par_content
- var ss=''
- if(par_name){
- ss = `
- <li class="list-group-item">
- <span>${ time }</span>
- <span>${ username}</span>
- <div class="well">
- <span>@ ${par_name}</span>
- <p>${par_content}</p>
- </div>
- <p>${ content }</p>
- </li>
- `
- }else{
- ss = `
- <li class="list-group-item">
- <span>${ time }</span>
- <span>${ username}</span>
- <p>${ content }</p>
- </li>
- `
- }
- $(".comment_list").append(ss)
- }
- }
- })
- })
- $(".my_reply").click(function () {
- var name = "@" + $(this).attr('user') + '\n'
- par_id = $(this).attr('comment_id')
- $("#comment_content").focus()
- $("#comment_content").val(name)
- })
- </script>
- {% endblock %}
- from django.db import transaction
- def comment(request):
- back_msg={'status':False,'msg':None}
- if request.user.is_authenticated:
- # 评论表添加数据
- # 文章表,修改评论个数
- article_id=request.POST.get('article_id')
- comment=request.POST.get('comment')
- par_id=request.POST.get('par_id')
- # 事物
- with transaction.atomic():
- ret=models.Comment.objects.create(user=request.user,article_id=article_id,content=comment,parent_comment_id=par_id)
- models.Article.objects.filter(pk=article_id).update(comment_count=F('comment_count')+)
- if par_id:
- back_msg['par_name']=ret.parent_comment.user.username
- back_msg['par_content']=ret.parent_comment.content
- back_msg['status']=True
- back_msg['user_name']=ret.user.username
- back_msg['time']=ret.create_time.strftime('%Y-%m-%d')
- back_msg['content']=ret.content
- back_msg['msg']='评论成功'
- else:
- back_msg['status'] = False
- back_msg['msg'] = '您没有登录'
- return JsonResponse(back_msg)
Django ---- blog项目学习所得的更多相关文章
- Django blog项目知识点总结
数据库操作部分 当我们在Django项目中的models.py下写好创建表的代码后.为了创建好这些数据库表,我们再一次请出我的工程管理助手 manage.py.激活虚拟环境,切换到 manage.py ...
- Django快速学习搭建blog项目
新手学习Django,本文学习的文档是<Django Web开发指南>.好了我也是新手,没什么好说了,go!- 首先先确定环境,我是在linux(Ubuntu14.04 gnome)下. ...
- Django学习笔记(20)——BBS+Blog项目开发(4)Django如何使用Bootstrap
本文学习如何通过Django使用Bootstrap.其实在之前好几个Django项目中已经尝试使用过了Bootstrap,而且都留有学习记录,我已经大概有了一个大的框架,那么本文就从头再走一遍流程,其 ...
- Django学习笔记(19)——BBS+Blog项目开发(3)细节知识点补充
本文将BBS+Blog项目开发中所需要的细节知识点进行补充,其中内容包括KindEditor编辑器的使用,BeautifulSoup 模块及其防XSS攻击,Django中admin管理工具的使用,me ...
- Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程
这篇博客主要完成一个BBS+Blog项目,那么主要是模仿博客园的博客思路,使用Django框架进行练习. 准备:项目需求分析 在做一个项目的时候,我们首先做的就是谈清楚项目需求,功能需求,然后才开始写 ...
- Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现
本文主要学习验证码功能的实现,为了项目BBS+Blog项目打下基础. 为了防止机器人频繁登陆网站或者破坏分子恶意登陆,很多用户登录和注册系统都提供了图形验证码功能. 验证码(CAPTCHA)是“Com ...
- django form使用学习记录
Django forms使用容易, 又方便扩展, 因此Django admin和CBVs基本都基于forms使用. 事实上, 由于django forms的强大验证功能, 大多数Django API ...
- Django练习项目之搭建博客
背景:自从今年回家过年后,来到公司给我转了试用,我的学习效率感觉不如从前,而且刚步入社会我总是想要怎么想明白想清楚一些事,这通常会花掉,消耗我大量的精力,因为我想把我的生活管理规划好了,而在it技术学 ...
- django创建项目
django创建项目 安装django pip install django==1.9 Note: C:\Python34\Scripts\pip.exe 创建项目 django-admin star ...
随机推荐
- Oracle中如何停止正在执行SQL语句
oracle的用P/SQL客户端中,如何停止正在执行的SQL语句? 我们使用oracle语句查询某个表时,如果查询的表数据太多,如何停止正在执行操作 如查询的表数据超过上万条时,如何停止查询操作
- laravel模型表建立外键约束的使用:
模型: //表->posts class Post extends Model { //关联用户: public function user(){ //belongsTo,第一个参数:外键表,第 ...
- day040 数据库索引补充 存储过程 事务等
1.正确使用索引 视图: 关键词 view 视图是体格虚拟表 创建视图 : create view 视图名称 as sql语句; 例: create view t_view as select * f ...
- 多态概念,C++
body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...
- 运行java程序的方法-DOS命令和Eclipse方法
● 运行java程序的方法(使用DOS命令) 首先进行一个"文件夹选项"的设置: 在D:\Android\java_code目录下新建了一个Hello_World.java文件(不 ...
- python作业学员管理系统(第十二周)
作业需求: 用户角色,讲师\学员, 用户登陆后根据角色不同,能做的事情不同,分别如下 讲师视图 管理班级,可创建班级,根据学员qq号把学员加入班级 可创建指定班级的上课纪录,注意一节上课纪录对应多条学 ...
- 输入三个double型的数据,放入到a,b,c三个变量中去,使用条件结构与交换逻辑将这三个变量中的值从小到大排列。
import java.util.Scanner; public class C8{ public static void main(String []args){ /* 8.输入三个double型的 ...
- linux Bash 常用
linux 帮助文档 man + [命令] eg: man ls[命令] + --help eg:ls --helphelp +[命令] eg:help ceinfo + [命令] eg:info l ...
- 解决MySQL不允许远程连接的问题
进入MySQL:mysql -u root -p mysql> GRANT ALL privileges ON *.* TO 'root' @'localhost' IDENTIFIED ...
- ubantu-vim操作
vim其实就是vi的升级版,vi里的所有命令vim里都可以用,一般使用来说几乎没什么差别. 注:本篇文章区分大小写! vi / vim三级模式的关系: 命令行模式 任何时候,不管用户处于何种模式,只要 ...