一、登录功能

  1、采用ajax 提交form表单的方式

  2、后台生成随机验证码,登录时提交验证码

  3、用PLI库生成随机验证码,置于session中,登录时与前台提交的code进行upeer()的验证  

  1. <div class="col-lg-6">
  2. <img height="" width="" src="/get_code/" alt="">
  3. </div>

二、首页

1、index.html分别采用头和container的布局

  1. <div class="my_head">
  2. <div class="container-fluid">
  3. <div class="row">
  4. <div class="col-md-3">
  5. <div class="panel panel-danger">
  6. <div class="panel-heading">广告信息</div>
  7. <div class="panel-body">
  8. 重金求子
  9. </div>
  10. </div>
  11. <div class="col-md-6">
  12. <div class="col-md-3">

2、head处理时,如果已经登录,则显示username

  1. {% if request.user.is_authenticated %}
  2. <li><a href="/login/">{{ request.user.username }}</a></li>
  3. <li><a href="#"></a></li>
  4. <li class="dropdown">
  5. <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
  6. aria-haspopup="true" aria-expanded="false">更多信息 <span class="caret"></span></a>
  7. <ul class="dropdown-menu">
  8. <li><a href="#">修改密码</a></li>
  9. <li><a href="#">修改头像</a></li>
  10. <li><a href="/logout/">退出登录</a></li>
  11. </ul>
  12. </li>
  13. {% else %}
  14. <li><a href="/login/">登录</a></li>
  15. <li><a href="/register/">注册</a></li>
  16. {% endif %}

3、class="media-left"将div分别放于同一行左右侧

  1. class="glyphicon glyphicon-comment"图标的话可以采用bootcss的、也可以用static里的font-awesome的拿来即用
  1. <div class="col-md-6">
  2. {% for articles in articles_list %}
  3. <p><h4><a href="">{{ articles.title }}</a></h4></p>
  4. <div class="media-left"><img height="" width=""
  5. src="/media/{{ articles.user.avatar }}"
  6. alt=""></div>
  7. <div class="media-right">
  8. {{ articles.desc }}
  9. </div>
  10. <div style="margin-top: 10px">
  11. <span><a href="">{{ articles.user.username }}</a></span>&nbsp
  12. <span>发布于:{{ articles.create_time|date:'Y-m-d' }}</span>&nbsp
  13. <span class="glyphicon glyphicon-comment">评论数({{ articles.comment_count}})</span>&nbsp
  14. <span class="glyphicon glyphicon-thumbs-up">点赞数({{ articles.up_count}})</span>&nbsp
  15.  
  16. </div>
  17. <hr>
  18. {% endfor %}
  19. </div>
  1. def index(request):
  2. articles_list = models.Article.objects.all()
  3. return render(request, 'index.html', locals())

三、注册的头像设计

1、为了使得点击字体或者图片都能发生jquery的change事件,label标签的for属性和input的id属性一样即可

  1. <div class="form-group">
  2.  
  3. <label for="my_file">头像
  4. <img height="60px" width="60px" src="/static/img/default.png" id="id_img">
  5. </label>
  6. <input type="file" id="my_file">
  7. </div>

2、读取图片内容到img标签里

  1. $("#my_file").change(function () {
  2. alert()
  3. //$("#my_file")[0].files[0] 取出文件
  4. var obj=$("#my_file")[].files[]
  5. //生成一个文件阅读器
  6. var read=new FileReader()
  7. //把文件读到我的对象里
  8. read.readAsDataURL(obj)
  9. read.onload=function(){
  10. $("#id_img").attr('src',read.result)
  11. }
  12.  
  13. {#$("#id_img").attr('aa','bb')#}
  14.  
  15. })

3、同时需要将input文件的字去掉

  1. <style>
  2. #my_file {
  3. display: none;
  4. }
  5. </style>

4、form表单有两个功能,一个是校验字段,一个是生成input标签

  1. {% for foo in form_obj %}
  2. <div class="form-group">
  3. <label for="">{{ foo.label }}</label>
  4. {{ foo }} <span class="errors"></span>
  5. </div>
  6. {% endfor %}

5、1和2的方式会在form里面直接提交form表单,此处想用ajax的方式提交,采用3的方法

  1. {# 1 <input type="submit">#}
  2. {# 2 <button></button>#}
  3. 3 <input type="button" id="id_button" class="btn btn-success" value="注册">

四、form组件局部钩子和全局钩子使用

1、在view.py里创建forms类,有全局和局部钩子

  1. from django import forms
  2. from django.forms import widgets
  3.  
  4. class RegForms(forms.Form):
  5. name = forms.CharField(max_length=, min_length=, label='用户名',
  6. widget=widgets.TextInput(attrs={'class': 'form-control'}),
  7. error_messages={'max_length': '太长了', 'min_length': '太短了'}
  8. )
  9. pwd = forms.CharField(max_length=, min_length=, label='密码',
  10. widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
  11. error_messages={'max_length': '太长了', 'min_length': '太短了'}
  12. )
  13. re_pwd = forms.CharField(max_length=, min_length=, label='确认密码',
  14. widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
  15. error_messages={'max_length': '太长了', 'min_length': '太短了'}
  16. )
  17. email = forms.EmailField(label='邮箱',
  18. widget=widgets.EmailInput(attrs={'class': 'form-control'}),
  19. )
  20.  
  21. def clean_name(self):
  22. name = self.cleaned_data.get('name')
  23. user = models.UserInfo.objects.filter(username=name).first()
  24. if user:
  25. raise ValidationError('用户已经存在')
  26. else:
  27. return name
  28.  
  29. def clean(self):
  30. pwd = self.cleaned_data.get('pwd')
  31. re_pwd = self.cleaned_data.get('re_pwd')
  32. if pwd == re_pwd:
  33. return self.cleaned_data
  34. else:
  35. raise ValidationError('两次密码不一致')

2、form校验了request.post之后存在两种数据:

  1. # form_obj.cleaned_data
  2. # form_obj.errors
  1. def register(request):
  2. form_obj = RegForms()
  3. back_msg = {}
  4. if request.is_ajax():
  5. name = request.POST.get('name')
  6. pwd = request.POST.get('pwd')
  7. re_pwd = request.POST.get('re_pwd')
  8. email = request.POST.get('email')
  9. myfile = request.FILES.get('myfile')
  10. print(myfile)
  11. print(re_pwd)
  12. form_obj = RegForms(request.POST)
  13. if form_obj.is_valid():
  14. # form_obj.cleaned_data
  15. # form_obj.errors
  16. if myfile:
  17. user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email, avatar=myfile)
  18. else:
  19. user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email)
  20. back_msg['user'] = name
  21. back_msg['msg'] = '注册成功'
  22. else:
  23. back_msg['msg'] = form_obj.errors
  24. print(form_obj.errors)
  25. print(type(form_obj.errors))
  26. return JsonResponse(back_msg)
  27.  
  28. 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 ()  定时函数执行某件事

  1. $("#id_button").click(function () {
  2. var formdata=new FormData()
  3. var tt=$("#my_form").serializeArray()
  4. $.each(tt,function (index,value) {
  5. //console.log(value.name)
  6. //console.log(value.value)
  7. formdata.append(value.name,value.value)
  8. })
  9. formdata.append('myfile',$("#my_file")[].files[])
  10. // console.log(tt)
  11.  
  12. $.ajax({
  13. url:'',
  14. type:'post',
  15. processData:false,
  16. contentType:false,
  17. data:formdata,
  18. success:function (data) {
  19. //console.log(data)
  20. if(data.user){
  21. location.href='/login/'
  22. }else{
  23. $.each(data.msg,function (index,value) {
  24. console.log(index)
  25. console.log(value)
  26. if(index=="__all__"){
  27. $("#id_re_pwd").next().text(value[])
  28. }
  29. $("#id_"+index).next().text(value[]).parent().addClass('has-error')
  30. //$("#id_"+index).next().parent().addClass('has-error')
  31. })
  32. setTimeout(function () {
  33. $(".form-group").removeClass('has-error')
  34. $('span').text("")
  35. },)
  36. }
  37.  
  38. }
  39.  
  40. })
  41.  
  42. })

五、注册

1、var tt=$("#my_form").serializeArray()  自动的将my_form里的name和value打包起来

2、传文件的话,必须

  1.       processData:false,
  2. contentType:false,否则会以url-encoded的方法传送了
  1. $("#id_button").click(function () {
  2. var formdata=new FormData()
  3. var tt=$("#my_form").serializeArray()
  4. $.each(tt,function (index,value) {
  5. //console.log(value.name)
  6. //console.log(value.value)
  7. formdata.append(value.name,value.value)
  8. })
  9. formdata.append('myfile',$("#my_file")[].files[])
  10. // console.log(tt)
  11.  
  12. $.ajax({
  13. url:'',
  14. type:'post',
  15. processData:false,
  16. contentType:false,
  17. data:formdata,
  18. success:function (data) {
  19. //console.log(data)
  20. if(data.user){
  21. location.href='/login/'
  22. }else{
  23. $.each(data.msg,function (index,value) {
  24. console.log(index)
  25. console.log(value)
  26. if(index=="__all__"){
  27. $("#id_re_pwd").next().text(value[])
  28. }
  29. $("#id_"+index).next().text(value[]).parent().addClass('has-error')
  30. //$("#id_"+index).next().parent().addClass('has-error')
  31. })
  32. setTimeout(function () {
  33. $(".form-group").removeClass('has-error')
  34. $('span').text("")
  35. },)
  36. }
  37.  
  38. }
  39.  
  40. })
  41.  
  42. })

4、create生成数据时,avatar=myfile,即直接传入文件对象即可

  1. def register(request):
  2. form_obj = RegForms()
  3. back_msg = {}
  4. if request.is_ajax():
  5. name = request.POST.get('name')
  6. pwd = request.POST.get('pwd')
  7. re_pwd = request.POST.get('re_pwd')
  8. email = request.POST.get('email')
  9. myfile = request.FILES.get('myfile')
  10. print(myfile)
  11. print(re_pwd)
  12. form_obj = RegForms(request.POST)
  13. if form_obj.is_valid():
  14. # form_obj.cleaned_data
  15. # form_obj.errors
  16. if myfile:
  17. user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email, avatar=myfile)
  18. else:
  19. user = models.UserInfo.objects.create_user(username=name, password=pwd, email=email)
  20. back_msg['user'] = name
  21. back_msg['msg'] = '注册成功'
  22. else:
  23. back_msg['msg'] = form_obj.errors
  24. print(form_obj.errors)
  25. print(type(form_obj.errors))
  26. return JsonResponse(back_msg)
  27.  
  28. return render(request, 'register.html', {'form_obj': form_obj})

5、models里加入此句的意思 upload_to='avatars/', default="/avatars/default.png",上传到MEDIA_ROOT + avatars路径,前面不能再加media,否则路径多了一层media的路径

  1. settings.py
  2.  
  3. # 设置用户上传头像的根路径
  4. MEDIA_ROOT=os.path.join(BASE_DIR,'media')

6、默认图片在media/avatars/default.png

  1. class UserInfo(AbstractUser):
  2. """
  3. 用户信息
  4. """
  5. nid = models.AutoField(primary_key=True)
  6. telephone = models.CharField(max_length=, null=True, unique=True)
  7. avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png")
  8. # auto_now_add=True
  9. '''
  10. auto_now_add
  11. 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
  12. auto_now
  13. 配置上auto_now=True,每次更新数据记录的时候会更新该字段。
  14. '''
  15. create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
  16.  
  17. blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE)
  18.  
  19. def __str__(self):
  20. return self.username

六、media文件夹的配置

1、static文件夹下的内容外部人都可以通过url访问到

  1. STATIC_URL = '/static/'
  2. STATICFILES_DIRS=[
  3. os.path.join(BASE_DIR,'static'),
  4. ]
  1. # 设置用户上传头像的根路径
    MEDIA_ROOT=os.path.join(BASE_DIR,'media')

2、用户上传的图片放在static不合适,需要一个专门的media的文件夹存储,直接127.0.0.1/media/avatars/default.png就可以访问到了

  1. from django.views.static import serve

  2.   #def serve(request, path, document_root=None, show_indexes=False):
  1. url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),

慎用此方法,如不小心,代码会暴露被外部访问到

3、如此,即可在首页index.html里将路径改为   src="/media/{{ articles.user.avatar }},即可访问

  1. {% for articles in articles_list %}
  2. <p><h4><a href="">{{ articles.title }}</a></h4></p>
  3. <div class="media-left"><img height="" width=""
  4. src="/media/{{ articles.user.avatar }}"
  5. alt=""></div>
  6. <div class="media-right">
  7. {{ articles.desc }}
  8. </div>
  9. <div style="margin-top: 10px">
  10. <span><a href="">{{ articles.user.username }}</a></span>&nbsp
  11. <span>发布于:{{ articles.create_time|date:'Y-m-d' }}</span>&nbsp
  12. <span class="glyphicon glyphicon-comment">评论数({{ articles.comment_count}})</span>&nbsp
  13. <span class="glyphicon glyphicon-thumbs-up">点赞数({{ articles.up_count}})</span>&nbsp
  14.  
  15. </div>
  16.  
  17. <hr>
  18. {% endfor %}

七、分组查询

1、views.py

  1. url(r'^(?P<username>\w+)/(?P<condition>tag|category|time|\w+)/(?P<search>.*)', views.homesite),
  2. url(r'^(?P<username>\w+)/', views.homesite),

2、后台数据分析

  1. 2.1 blog = user.blog  此为查找时的对象
  1. 2.2 分组查询1 group by谁,用谁做基表
  2. # 2 filter在前,表示查询 ,filter在后,表示过滤,having
  3. # 3 values在前,表示group by 在后,取字段
  1. def homesite(request, username, **kwargs):
  2. print(kwargs)
  3. username=username
  4. user = models.UserInfo.objects.filter(username=username).first()
  5. if not user:
  6. return render(request, 'errors.html')
  7. blog = user.blog
  8. article_list = models.Article.objects.filter(user=user)
  9. if kwargs:
  10. condition=kwargs.get('condition')
  11. search=kwargs.get('search')
  12. if condition=='tag':
  13. article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
  14. elif condition=='category':
  15. article_list = models.Article.objects.filter(user=user).filter(category__title=search)
  16. elif condition=='time':
  17. ll=search.split('-')
  18. article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[],create_time__month=ll[])
  19. else:
  20. return render(request, 'errors.html')
  21.  
  22. # print(article_list)
  23. from django.db.models import Count
  24. # 查询每个标签下的文章数(分组查询)
  25. # 分组查询1 group by谁,用谁做基表
  26. # filter在前,表示查询 ,filter在后,表示过滤,having
  27. # values在前,表示group by 在后,取字段
  28. # 查询当前站点下每个标签下的文章数(分组查询)
  29. tag_count = models.Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values_list('title', 'c')
  30. # 查询当前站点下每个分类下的文章数
  31. category_count = models.Category.objects.filter(blog=blog).annotate(c=Count('article__nid')).values_list('title',
  32. 'c')
  33. from django.db.models.functions import TruncMonth
  34. # 查询当前站点每个月份下的文章数
  35. # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
  36. # for i in time_count:
  37. # print(i.title)
  38. # print(i.y_m)
  39. time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
  40. c=Count('y_m')).values_list('y_m', 'c')
  41.  
  42. print(tag_count)
  43. print(category_count)
  44. print(time_count)
  45.  
  46. # 统计每个出版社书籍个数
  47. # Publish.object.all().annotate(Count('book__title'))
  48.  
  49. return render(request, 'homesite.html', locals())

2、homesite.html

  1、用admin添加数据

  2、{{ foo.0 }}({{ foo.1 }})       -------------  xxxxxxxxxxx(xx)

  1. <div class="container-fluid" style="margin-top: 10px">
  2. <div class="row">
  3. <div class="col-md-3">
  4.  
  5. <div class="panel panel-danger">
  6. <div class="panel-heading">我的标签</div>
  7. <div class="panel-body">
  8. {% for foo in tag_count %}
  9. <p><a href="/{{ username }}/tag/{{ foo.0 }}">{{ foo. }}({{ foo. }})</a></p>
  10.  
  11. {% endfor %}
  12.  
  13. </div>
  14. </div>
  15. <div class="panel panel-info">
  16. <div class="panel-heading">随笔分类</div>
  17. <div class="panel-body">
  18. {% for foo in category_count %}
  19. <p><a href="/{{ username }}/category/{{ foo.0 }}">{{ foo. }}({{ foo. }})</a></p>
  20.  
  21. {% endfor %}
  22. </div>
  23. </div>
  24. <div class="panel panel-danger">
  25. <div class="panel-heading">随笔档案</div>
  26. <div class="panel-body">
  27. {% for foo in time_count %}
  28. <p><a href="/{{ username }}/time/{{ foo.0|date:'Y-m' }}">{{ foo.|date:'Y-m' }}({{ foo. }})</a></p>
  29.  
  30. {% endfor %}
  31.  
  32. </div>
  33. </div>
  34.  
  35. </div>

八、个人站点路由设计

将此条至于最下方的目的,前面都匹配不了的时候,到此可以匹配成功进入视图函数,有则看,无资源则显示404

  1. url(r'^(?P<username>\w+)/', views.homesite),

九、个人站点过滤

1、总的设计

  1. def homesite(request, username, **kwargs):
  2. print(kwargs)
  3. username=username
  4. user = models.UserInfo.objects.filter(username=username).first()
  5. if not user:
  6. return render(request, 'errors.html')
  7. blog = user.blog
  8. article_list = models.Article.objects.filter(user=user)
  9. if kwargs:
  10. condition=kwargs.get('condition')
  11. search=kwargs.get('search')
  12. if condition=='tag':
  13. article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
  14. elif condition=='category':
  15. article_list = models.Article.objects.filter(user=user).filter(category__title=search)
  16. elif condition=='time':
  17. ll=search.split('-')
  18. article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[],create_time__month=ll[])
  19. else:
  20. return render(request, 'errors.html')
  21.  
  22. # print(article_list)
  23. from django.db.models import Count
  24. # 查询每个标签下的文章数(分组查询)
  25. # 分组查询1 group by谁,用谁做基表
  26. # filter在前,表示查询 ,filter在后,表示过滤,having
  27. # values在前,表示group by 在后,取字段
  28. # 查询当前站点下每个标签下的文章数(分组查询)
  29. tag_count = models.Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values_list('title', 'c')
  30. # 查询当前站点下每个分类下的文章数
  31. category_count = models.Category.objects.filter(blog=blog).annotate(c=Count('article__nid')).values_list('title',
  32. 'c')
  33. from django.db.models.functions import TruncMonth
  34. # 查询当前站点每个月份下的文章数
  35. # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
  36. # for i in time_count:
  37. # print(i.title)
  38. # print(i.y_m)
  39. time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
  40. c=Count('y_m')).values_list('y_m', 'c')
  41.  
  42. print(tag_count)
  43. print(category_count)
  44. print(time_count)
  45.  
  46. # 统计每个出版社书籍个数
  47. # Publish.object.all().annotate(Count('book__title'))
  48.  
  49. return render(request, 'homesite.html', locals())

2、按时间分类,用TruncMonth截断年月,会在原表的基础上在加一条时间为年月的数据,再以此分类(group by谁,用谁做基表)

  1. from django.db.models.functions import TruncMonth
  2. # 查询当前站点每个月份下的文章数
  3. # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
  4. # for i in time_count:
  5. # print(i.title)
  6. # print(i.y_m)
  7. time_count=models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values('y_m').annotate(
  8. c=Count('y_m')).values_list('y_m', 'c')

3、分类取得数据,页面展示article_list数据,只会显示符合的文章show

  1. if kwargs:
  2. condition=kwargs.get('condition')
  3. search=kwargs.get('search')
  4. if condition=='tag':
  5. article_list = models.Article.objects.filter(user=user).filter(tags__title=search)
  6. elif condition=='category':
  7. article_list = models.Article.objects.filter(user=user).filter(category__title=search)
  8. elif condition=='time':
  9. ll=search.split('-')
  10. article_list=models.Article.objects.filter(user=user).filter(create_time__year=ll[],create_time__month=ll[])
  11. else:
  12. return render(request, 'errors.html')

4、相应的前台html是如下,加入a连接和url的处理,可以看得相应的数据

  1. <div class="panel panel-danger">
  2. <div class="panel-heading">我的标签</div>
  3. <div class="panel-body">
  4. {% for foo in tag_count %}
  5. <p><a href="/{{ username }}/tag/{{ foo.0 }}">{{ foo. }}({{ foo. }})</a></p>
  6.  
  7. {% endfor %}
  8.  
  9. </div>
  10. </div>
  11. <div class="panel panel-info">
  12. <div class="panel-heading">随笔分类</div>
  13. <div class="panel-body">
  14. {% for foo in category_count %}
  15. <p><a href="/{{ username }}/category/{{ foo.0 }}">{{ foo. }}({{ foo. }})</a></p>
  16.  
  17. {% endfor %}
  18. </div>
  19. </div>
  20. <div class="panel panel-danger">
  21. <div class="panel-heading">随笔档案</div>
  22. <div class="panel-body">
  23. {% for foo in time_count %}
  24. <p><a href="/{{ username }}/time/{{ foo.0|date:'Y-m' }}">{{ foo.|date:'Y-m' }}({{ foo. }})</a></p>
  25.  
  26. {% endfor %}
  27.  
  28. </div>
  29. </div>

十、后台管理页面

1、布局上还是采用了上为header,左三右九的布局

  1. <style>
  2.  
  3. .home_head {
    height: 60px;
    background: #1b6d85;
    }
    </style>
  1. <div class="home_head"></div>
  2. <div class="container-fluid" style="margin-top: 10px">
  3. <div class="row">
  4. <div class="col-md-3"。。。。。。。。。。。。
  5. <div class="col-md-9"
  6. </div>
  7. </div>
  8. </body>

2、中间采用bootstrap的标签页的方式,达到点击之后会用切换content和header标签的效果

  1. <div class="col-md-9">
  2. <div>
  3. <!-- Nav tabs -->
  4. <ul class="nav nav-tabs" role="tablist">
  5. <li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab"
  6. data-toggle="tab">文章</a></li>
  7. <li role="presentation"><a href="#profile" aria-controls="profile" role="tab"
  8. data-toggle="tab">随笔</a></li>
  9. <li role="presentation"><a href="#messages" aria-controls="messages" role="tab"
  10. data-toggle="tab">交友</a></li>
  11. <li role="presentation"><a href="#settings" aria-controls="settings" role="tab"
  12. data-toggle="tab">园子</a></li>
  13. </ul>
  14.  
  15. <!-- Tab panes -->
  16. <div class="tab-content">
  17. <div role="tabpanel" class="tab-pane active" id="home">
  18. {% block back_content %}
  19.  
  20. {% endblock %}
  21. </div>
  22. <div role="tabpanel" class="tab-pane" id="profile">随笔</div>
  23. <div role="tabpanel" class="tab-pane" id="messages">...</div>
  24. <div role="tabpanel" class="tab-pane" id="settings">...</div>
  25. </div>
  26. </div>

3、需要用户登录了之后才能进入后台用户管理界面,故login_required装饰器判断是否登录,未登录则直接跳转到login_url='/login/'界面

  1. from django.contrib.auth.decorators import login_required
  2. @login_required(login_url='/login/')
  3. def back_home(request):
  4.  
  5. article_list=models.Article.objects.filter(user=request.user)
  6.  
  7. return render(request,'back/back_home.html',locals())

4、左侧采用collapse方式,点击会展开

  1. <div class="col-md-3">
  2. <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
  3. <div class="panel panel-default">
  4. <div class="panel-heading" role="tab" id="headingOne">
  5. <h4 class="panel-title">
  6. <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
  7. aria-expanded="true" aria-controls="collapseOne">
  8. 操作
  9. </a>
  10. </h4>
  11. </div>
  12. <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
  13. aria-labelledby="headingOne">
  14. <div class="panel-body">
  15. <a href="/addarticle/">添加文章</a>
  16. </div>
  17. <div class="panel-body">
  18. <a href="">添加随笔</a>
  19. </div>
  20. </div>
  21. </div>
  22. </div>

5、用block 导入基准html

  1. {% extends 'back/back_base.html' %}
  2.  
  3. {% block back_content %}
  4.  
  5. <table class="table table-striped table-hover">
  6. <thead>
  7. <tr>
  8. <th>文章标题</th>
  9. <th>评论数</th>
  10. <th>点赞数</th>
  11. <th>操作</th>
  12. <th>操作</th>
  13. </tr>
  14. </thead>
  15. <tbody>
  16. {% for article in article_list %}
  17. <tr>
  18. <td>{{ article.title }}</td>
  19. <td>{{ article.comment_count }}</td>
  20. <td>{{ article.up_count }}</td>
  21. <td><a href="">修改</a></td>
  22. <td><a href="">删除</a></td>
  23. </tr>
  24. {% endfor %}
  25. </tbody>
  26. </table>
  27. {% endblock %}

十一、后台管理保存文章

1、在html页面使用了Kindedit编辑器,只需将库包至于static目录下即可,需保证

  1. <textarea name="content" id="editor_id" cols="50" rows="10"></textarea>的id与kindedit一样,即可被调用
  1. {% extends 'back/back_base.html' %}
  2.  
  3. {% block back_content %}
  4. <div>
  5. <form action="" method="post">
  6. {% csrf_token %}
  7. <div class="text-info"><h4>添加文章</h4></div>
  8. <p>标题</p>
  9. <p><input type="text" class="form-control" name="title"></p>
  10. <p>内容(Kindedit编辑器,不支持拖放/粘贴上传图片)</p>
  11. <p>
  12. <textarea name="content" id="editor_id" cols="" rows=""></textarea>
  13. </p>
  14. <p>
  15. <button class="btn btn-primary">提交</button>
  16. </p>
  17.  
  18. </form>
  19. </div>
  20.  
  21. <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
  22. <script>
  23. KindEditor.ready(function (K) {
  24. window.editor = K.create('#editor_id',
  25.  
  26. {
  27. width: '100%',
  28. height: '600px',
  29. resizeType: ,
  30. }
  31. );
  32. });
  33. </script>
  34. {% endblock %}

2、上传文字需要用bs4清洗,html显示的时候只需要content内容,但是详情页面的时候需要html样式等,故存储str(content)

  1. # 安装 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>
  1. models.Article.objects.create(title=title,content=str(soup),desc=desc,user=request.user)
    return redirect('/backhome/')
  2.  
  3. return render(request,'back/article_add.html')

3、在back_base界面, <a href="/addarticle/">添加文章</a>关联上述所诉逻辑

  1. <div class="col-md-3">
  2. <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
  3. <div class="panel panel-default">
  4. <div class="panel-heading" role="tab" id="headingOne">
  5. <h4 class="panel-title">
  6. <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne"
  7. aria-expanded="true" aria-controls="collapseOne">
  8. 操作
  9. </a>
  10. </h4>
  11. </div>
  12. <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel"
  13. aria-labelledby="headingOne">
  14. <div class="panel-body">
  15. <a href="/addarticle/">添加文章</a>
  16. </div>
  17. <div class="panel-body">
  18. <a href="">添加随笔</a>
  19. </div>
  20. </div>
  21. </div>
  22. </div>

十二、点赞、点踩

1、articledetail页面的设计,继承自base.html,评论html直接引用自博客园的html和css样式

  1. {% extends 'base.html' %}
  2.  
  3. {% block detail %}
  4. <style>
  5. #div_digg {
  6. float: right;
  7. margin-bottom: 10px;
  8. margin-right: 30px;
  9. font-size: 12px;
  10. width: 125px;
  11. text-align: center;
  12. margin-top: 10px;
  13. }
  14.  
  15. .diggit {
  16. float: left;
  17. width: 46px;
  18. height: 52px;
  19. background: url(/static/img/upup.gif) no-repeat;
  20. text-align: center;
  21. cursor: pointer;
  22. margin-top: 2px;
  23. padding-top: 5px;
  24. }
  25.  
  26. .buryit {
  27. float: right;
  28. margin-left: 20px;
  29. width: 46px;
  30. height: 52px;
  31. background: url(/static/img/downdown.gif) no-repeat;
  32. text-align: center;
  33. cursor: pointer;
  34. margin-top: 2px;
  35. padding-top: 5px;
  36. }
  37.  
  38. .clear {
  39. clear: both;
  40. }
  41.  
  42. </style>
  43. <div>
  44. <p><h4 class="text-center">{{ article.title }}</h4></p>
  45. <p>{{ article.content|safe }}</p>
  46.  
  47. <div class="clearfix">
  48.  
  49. <div id="div_digg">
  50. <div class="diggit action">
  51. <span class="diggnum" id="digg_count">{{ article.up_count }}</span>
  52. </div>
  53. <div class="buryit action">
  54. <span class="burynum" id="bury_count">{{ article.down_count }}</span>
  55. </div>
  56. <div class="clear"></div>
  57. <div class="diggword" id="digg_tips" style="color: red;"></div>
  58. </div>
  59.  
  60. </div>

2、is_up = $(this).hasClass('diggit')结果要么是true或者false,

  1. var obj = $(this).children('span'),当点击时要么是点up,down的,this代指diggitburyit事件,这句话的意思是obj为其子节点为span的对象
  1. <script>
  2. var par_id = '';
  3. $(".action").click(function () {
  4. var is_up = $(this).hasClass('diggit')
  5. //alert(is_up)
  6. var obj = $(this).children('span')
  7.  
  8. $.ajax({
  9. url: '/diggit/',
  10. type: 'post',
  11. //谁对那篇文章点赞或点踩
  12. data: {
  13. 'csrfmiddlewaretoken': '{{ csrf_token }}',
  14. 'article_id':{{ article.pk }},
  15. 'is_up': is_up
  16. },
  17. success: function (data) {
  18. console.log(data)
  19. $(".diggword").text(data.msg)
  20. if (data.status == ) {
  21. obj.text(Number(obj.text()) + )
  22. }
  23. }
  24. })
  25. })

3、articledetail页面展示,因为展示方法重复了,可以把下面重用的包装成共用函数

  1. url(r'^(?P<username>\w+)/article/(?P<pk>\d+)', views.article_detail),
  1. url(r'^diggit/', views.diggit),
  1. def article_detail(request,username,pk):
  2. # 正常情况下应该有一堆安全性校验
  3. #
  4. user=models.UserInfo.objects.filter(username=username).first()
  5. blog=user.blog
  6. article=models.Article.objects.filter(pk=pk).first()
  7. # 获取当前文章下所有评论
  8. comment_list=models.Comment.objects.filter(article_id=pk)
  9.  
  10. from django.db.models import Count
  11. # 查询每个标签下的文章数(分组查询)
  12. # 分组查询1 group by谁,用谁做基表
  13. # filter在前,表示查询 ,filter在后,表示过滤,having
  14. # values在前,表示group by 在后,取字段
  15. # 查询当前站点下每个标签下的文章数(分组查询)
  16. # tag_count = models.Tag.objects.filter(blog=blog).annotate(c=Count("article__title")).values_list('title', 'c')
  17. tag_count = models.Tag.objects.all().values('pk').filter(blog=blog).annotate(c=Count("article__title")).values_list(
  18. 'title', 'c')
  19. # 查询当前站点下每个分类下的文章数
  20. category_count = models.Category.objects.filter(blog=blog).annotate(c=Count('article__nid')).values_list('title',
  21. 'c')
  22. from django.db.models.functions import TruncMonth
  23. # 查询当前站点每个月份下的文章数
  24. # time_count=models.Article.objects.annotate(y_m=TruncMonth('create_time'))
  25. # for i in time_count:
  26. # print(i.title)
  27. # print(i.y_m)
  28. time_count = models.Article.objects.filter(user=user).annotate(y_m=TruncMonth('create_time')).values(
  29. 'y_m').annotate(
  30. c=Count('y_m')).values_list('y_m', 'c')
  31.  
  32. return render(request,'article_detail.html',locals())

4、注释的时候将logic写清楚

  post传过来的都是str类型的,需要转化成python认识的bool类型,通过json反序列化,is_up=json.loads(is_up)

  1. import json
  2. from django.db.models import F
  3. def diggit(request):
  4. back_msg={'status':None,'msg':None}
  5. if request.user.is_authenticated:
  6. # 先查询是否已经点过
  7. # 如果没有点过,去点赞表存数据
  8. # 去文章表修改点赞数据
  9. article_id=request.POST.get('article_id')
  10. # 注意:
  11. is_up=request.POST.get('is_up')
  12. print(type(is_up))
  13. is_up=json.loads(is_up)
  14. ret=models.ArticleUpDown.objects.filter(user=request.user,article_id=article_id)
  15. if not ret:
  16. models.ArticleUpDown.objects.create(user=request.user,article_id=article_id,is_up=is_up)
  17. if is_up:
  18. models.Article.objects.filter(pk=article_id).update(up_count=F('up_count')+)
  19. back_msg['status']=
  20. back_msg['msg'] = '点赞成功'
  21. else:
  22. models.Article.objects.filter(pk=article_id).update(down_count=F('down_count') + )
  23. back_msg['status'] =
  24. back_msg['msg'] = '点踩成功'
  25. else:
  26. back_msg['status'] =
  27. back_msg['msg'] = '您已经点过了'
  28. else:
  29. back_msg['status'] =
  30. back_msg['msg'] = '您没有登录'
  31. return JsonResponse(back_msg)

十三、评论(render显示和ajax显示)

1、提交评论

  1. $(".btn_submit").click(function () {
  2. var content = $("#comment_content").val()
  3. if (par_id) {
  4. var num = content.indexOf('\n') +
  5. content = content.slice(num)
  6. alert(content)
  7. }
  8. $.ajax({
  9. url: '/comment/',
  10. type: 'post',
  11. data: {
  12. 'csrfmiddlewaretoken': '{{ csrf_token }}',
  13. 'article_id':{{ article.pk }},
  14. 'comment': content,
  15. 'par_id': par_id
  16. },
  17. success: function (data) {
  18. $("#comment_content").val("")
  19. console.log(data)

为了确保数据写入,创建事务with transaction.atomic():,失败则回滚

  1. def comment(request):
  2. back_msg={'status':False,'msg':None}
  3. if request.user.is_authenticated:
  4. # 评论表添加数据
  5. # 文章表,修改评论个数
  6. article_id=request.POST.get('article_id')
  7. comment=request.POST.get('comment')
  8. par_id=request.POST.get('par_id')
  9. # 事务
  10. with transaction.atomic():
  11. ret=models.Comment.objects.create(user=request.user,article_id=article_id,content=comment,parent_comment_id=par_id)
  12. models.Article.objects.filter(pk=article_id).update(comment_count=F('comment_count')+)
  13.  
  14. back_msg['status']=True
  15. back_msg['user_name']=ret.user.username
  16. back_msg['time']=ret.create_time.strftime('%Y-%m-%d')
  17. back_msg['content']=ret.content
  18. back_msg['msg']='评论成功'
  19. else:
  20. back_msg['status'] = False
  21. back_msg['msg'] = '您没有登录'
  22.  
  23. return JsonResponse(back_msg)

2、render显示,刷新界面才显示评论更新上传了

  1. <div>
  2. <ul class="list-group comment_list">
  3. {% for comment in comment_list %}
  4. {# #17楼 -- : 隔壁古二蛋 #}
  5. <li class="list-group-item">
  6. <span>#{{ forloop.counter }}楼</span>
  7. <span>{{ comment.create_time|date:'Y-m-d' }}</span>
  8. <span>{{ comment.user.username }}</span>
  9. <span><a class="pull-right my_reply" user="{{ comment.user.username }}"
  10. comment_id="{{ comment.pk }}">回复</a></span><p>{{ comment.content }}</p>
  11.  
  12. </li>
  13. {% endfor %}
  14. </ul>
  15. </div>

3、提交根评论及ajax显示,及render显示,

  1. {# 评论 #}
  2. <div>
  3. <p>发表评论</p>
  4. <p>
  5. 昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="" value="刘清政">
  6. </p>
  7. <p>评论内容</p>
  8. <p><textarea name="" id="comment_content" cols="" rows=""></textarea></p>
  9. <button class="btn btn-primary btn_submit">回复</button>
  10. </div>

es6字符串替换,<span>${ time }</span>,需要什么比如time,根据后台去取

  1. if (data.status) {
  2. var username = data.user_name;
  3. var time = data.time;
  4. var content = data.content
  5. var par_name=data.par_name
  6. var par_content=data.par_content
  7.  
  8. var ss=''
  9. if(par_name){
  10. ss = `
  11. <li class="list-group-item">
  12. <span>${ time }</span>
  13. <span>${ username}</span>
  14. <div class="well">
  15. <span>@ ${par_name}</span>
  16. <p>${par_content}</p>
  17. </div>
  18. <p>${ content }</p>
  19. </li>
  20. `
  21. }else{
  22. ss = `
  23. <li class="list-group-item">
  24. <span>${ time }</span>
  25. <span>${ username}</span>
  26. <p>${ content }</p>
  27. </li>
  28. `
  29. }
  30. $(".comment_list").append(ss)
  31. }
  32. }
  33. })
  34. })

后台

  1. from django.db import transaction
  2. def comment(request):
  3. back_msg={'status':False,'msg':None}
  4. if request.user.is_authenticated:
  5. # 评论表添加数据
  6. # 文章表,修改评论个数
  7. article_id=request.POST.get('article_id')
  8. comment=request.POST.get('comment')
  9. par_id=request.POST.get('par_id')
  10. # 事物
  11. with transaction.atomic():
  12. ret=models.Comment.objects.create(user=request.user,article_id=article_id,content=comment,parent_comment_id=par_id)
  13. models.Article.objects.filter(pk=article_id).update(comment_count=F('comment_count')+)
  14. if par_id:
  15. back_msg['par_name']=ret.parent_comment.user.username
  16. back_msg['par_content']=ret.parent_comment.content
  17. back_msg['status']=True
  18. back_msg['user_name']=ret.user.username
  19. back_msg['time']=ret.create_time.strftime('%Y-%m-%d')
  20. back_msg['content']=ret.content
  21. back_msg['msg']='评论成功'
  22. else:
  23. back_msg['status'] = False
  24. back_msg['msg'] = '您没有登录'
  25.  
  26. return JsonResponse(back_msg)

4、子评论,点击回复触发事件,同时将要用的父评论ID等信息直接放置于attr属性上,方便调用

  1. <ul class="list-group comment_list">
  2. {% for comment in comment_list %}
  3. {# #17楼 -- : 隔壁古二蛋 #}
  4. <li class="list-group-item">
  5. <span>#{{ forloop.counter }}楼</span>
  6. <span>{{ comment.create_time|date:'Y-m-d' }}</span>
  7. <span>{{ comment.user.username }}</span>
  8. <span><a class="pull-right my_reply" user="{{ comment.user.username }}"
  9. comment_id="{{ comment.pk }}">回复</a></span>
  10. {% if comment.parent_comment %}
  11. <div class="well">
  12. <span>@{{ comment.parent_comment.user.username }}</span>
  13. <p>{{ comment.parent_comment.content }}</p>
  14. </div>

使得点击回复之后直接focus到textarea标签,写响应子评论

父评论par_id,定义全局,使得后面都可调用

  1. $(".my_reply").click(function () {
  2.  
  3. var name = "@" + $(this).attr('user') + '\n'
  4. par_id = $(this).attr('comment_id')
  5. $("#comment_content").focus()
  6. $("#comment_content").val(name)
  7. })

5、判断是否为子评论,若是,则截取第一段内容,将后面的保存在content里,若不是,则不处理

  1. $(".btn_submit").click(function () {
  2. var content = $("#comment_content").val()
  3. if (par_id) {
  4. var num = content.indexOf('\n') +
  5. content = content.slice(num) #slice去索引之后的值
  6. alert(content)
  7. }

判断是否有父评论,有则加@父id+comment

  1. <ul class="list-group comment_list">
  2. {% for comment in comment_list %}
  3. {# #17楼 -- : 隔壁古二蛋 #}
  4. <li class="list-group-item">
  5. <span>#{{ forloop.counter }}楼</span>
  6. <span>{{ comment.create_time|date:'Y-m-d' }}</span>
  7. <span>{{ comment.user.username }}</span>
  8. <span><a class="pull-right my_reply" user="{{ comment.user.username }}"
  9. comment_id="{{ comment.pk }}">回复</a></span>
  10. {% if comment.parent_comment %}
  11. <div class="well">
  12. <span>@{{ comment.parent_comment.user.username }}</span>
  13. <p>{{ comment.parent_comment.content }}</p>
  14. </div>
  15. {% endif %}
  16. <p>{{ comment.content }}</p>

同时再做个ajax显示功能,及一直带有render的显示功能,需要什么参数,需要什么参数,去后台返回

  1. $(".btn_submit").click(function () {
  2. var content = $("#comment_content").val()
  3. if (par_id) {
  4. var num = content.indexOf('\n') +
  5. content = content.slice(num)
  6. alert(content)
  7. }
  8. $.ajax({
  9. url: '/comment/',
  10. type: 'post',
  11. data: {
  12. 'csrfmiddlewaretoken': '{{ csrf_token }}',
  13. 'article_id':{{ article.pk }},
  14. 'comment': content,
  15. 'par_id': par_id
  16. },
  17. success: function (data) {
  18. $("#comment_content").val("")
  19. console.log(data)
  20. if (data.status) {
  21. var username = data.user_name;
  22. var time = data.time;
  23. var content = data.content
  24. var par_name=data.par_name
  25. var par_content=data.par_content
  26.  
  27. var ss=''
  28. if(par_name){
  29. ss = `
  30. <li class="list-group-item">
  31. <span>${ time }</span>
  32. <span>${ username}</span>
  33. <div class="well">
  34. <span>@ ${par_name}</span>
  35. <p>${par_content}</p>
  36. </div>
  37. <p>${ content }</p>
  38. </li>
  39. `
  40. }else{
  41. ss = `
  42. <li class="list-group-item">
  43. <span>${ time }</span>
  44. <span>${ username}</span>
  45. <p>${ content }</p>
  46. </li>
  47. `
  48. }
  49. $(".comment_list").append(ss)
  50.  
  51. }
  52.  
  53. }
  54. })
  55.  
  56. })

6、总的,有根评论、子评论的ajax、render显示功能

  1. {% extends 'base.html' %}
  2.  
  3. {% block detail %}
  4. <style>
  5. #div_digg {
  6. float: right;
  7. margin-bottom: 10px;
  8. margin-right: 30px;
  9. font-size: 12px;
  10. width: 125px;
  11. text-align: center;
  12. margin-top: 10px;
  13. }
  14.  
  15. .diggit {
  16. float: left;
  17. width: 46px;
  18. height: 52px;
  19. background: url(/static/img/upup.gif) no-repeat;
  20. text-align: center;
  21. cursor: pointer;
  22. margin-top: 2px;
  23. padding-top: 5px;
  24. }
  25.  
  26. .buryit {
  27. float: right;
  28. margin-left: 20px;
  29. width: 46px;
  30. height: 52px;
  31. background: url(/static/img/downdown.gif) no-repeat;
  32. text-align: center;
  33. cursor: pointer;
  34. margin-top: 2px;
  35. padding-top: 5px;
  36. }
  37.  
  38. .clear {
  39. clear: both;
  40. }
  41.  
  42. </style>
  43. <div>
  44. <p><h4 class="text-center">{{ article.title }}</h4></p>
  45. <p>{{ article.content|safe }}</p>
  46.  
  47. <div class="clearfix">
  48.  
  49. <div id="div_digg">
  50. <div class="diggit action">
  51. <span class="diggnum" id="digg_count">{{ article.up_count }}</span>
  52. </div>
  53. <div class="buryit action">
  54. <span class="burynum" id="bury_count">{{ article.down_count }}</span>
  55. </div>
  56. <div class="clear"></div>
  57. <div class="diggword" id="digg_tips" style="color: red;"></div>
  58. </div>
  59.  
  60. </div>
  61. {# 评论列表#}
  62. <div>
  63. <ul class="list-group comment_list">
  64. {% for comment in comment_list %}
  65. {# #17楼 -- : 隔壁古二蛋 #}
  66. <li class="list-group-item">
  67. <span>#{{ forloop.counter }}楼</span>
  68. <span>{{ comment.create_time|date:'Y-m-d' }}</span>
  69. <span>{{ comment.user.username }}</span>
  70. <span><a class="pull-right my_reply" user="{{ comment.user.username }}"
  71. comment_id="{{ comment.pk }}">回复</a></span>
  72. {% if comment.parent_comment %}
  73. <div class="well">
  74. <span>@{{ comment.parent_comment.user.username }}</span>
  75. <p>{{ comment.parent_comment.content }}</p>
  76. </div>
  77.  
  78. {% endif %}
  79.  
  80. <p>{{ comment.content }}</p>
  81.  
  82. </li>
  83. {% endfor %}
  84. </ul>
  85. </div>
  86. {# 评论 #}
  87. <div>
  88. <p>发表评论</p>
  89. <p>
  90. 昵称:<input type="text" id="tbCommentAuthor" class="author" disabled="disabled" size="" value="刘清政">
  91. </p>
  92. <p>评论内容</p>
  93. <p><textarea name="" id="comment_content" cols="" rows=""></textarea></p>
  94. <button class="btn btn-primary btn_submit">回复</button>
  95. </div>
  96.  
  97. </div>
  98.  
  99. <script>
  100. var par_id = '';
  101. $(".action").click(function () {
  102. var is_up = $(this).hasClass('diggit')
  103. //alert(is_up)
  104. var obj = $(this).children('span')
  105.  
  106. $.ajax({
  107. url: '/diggit/',
  108. type: 'post',
  109. //谁对那篇文章点赞或点踩
  110. data: {
  111. 'csrfmiddlewaretoken': '{{ csrf_token }}',
  112. 'article_id':{{ article.pk }},
  113. 'is_up': is_up
  114. },
  115. success: function (data) {
  116. console.log(data)
  117. $(".diggword").text(data.msg)
  118. if (data.status == ) {
  119. obj.text(Number(obj.text()) + )
  120. }
  121. }
  122. })
  123. })
  124. $(".btn_submit").click(function () {
  125. var content = $("#comment_content").val()
  126. if (par_id) {
  127. var num = content.indexOf('\n') +
  128. content = content.slice(num)
  129. alert(content)
  130. }
  131. $.ajax({
  132. url: '/comment/',
  133. type: 'post',
  134. data: {
  135. 'csrfmiddlewaretoken': '{{ csrf_token }}',
  136. 'article_id':{{ article.pk }},
  137. 'comment': content,
  138. 'par_id': par_id
  139. },
  140. success: function (data) {
  141. $("#comment_content").val("")
  142. console.log(data)
  143. if (data.status) {
  144. var username = data.user_name;
  145. var time = data.time;
  146. var content = data.content
  147. var par_name=data.par_name
  148. var par_content=data.par_content
  149.  
  150. var ss=''
  151. if(par_name){
  152. ss = `
  153. <li class="list-group-item">
  154. <span>${ time }</span>
  155. <span>${ username}</span>
  156. <div class="well">
  157. <span>@ ${par_name}</span>
  158. <p>${par_content}</p>
  159. </div>
  160. <p>${ content }</p>
  161. </li>
  162. `
  163. }else{
  164. ss = `
  165. <li class="list-group-item">
  166. <span>${ time }</span>
  167. <span>${ username}</span>
  168. <p>${ content }</p>
  169. </li>
  170. `
  171. }
  172. $(".comment_list").append(ss)
  173.  
  174. }
  175.  
  176. }
  177. })
  178.  
  179. })
  180.  
  181. $(".my_reply").click(function () {
  182.  
  183. var name = "@" + $(this).attr('user') + '\n'
  184. par_id = $(this).attr('comment_id')
  185. $("#comment_content").focus()
  186. $("#comment_content").val(name)
  187. })
  188. </script>
  189. {% endblock %}
  1. from django.db import transaction
  2. def comment(request):
  3. back_msg={'status':False,'msg':None}
  4. if request.user.is_authenticated:
  5. # 评论表添加数据
  6. # 文章表,修改评论个数
  7. article_id=request.POST.get('article_id')
  8. comment=request.POST.get('comment')
  9. par_id=request.POST.get('par_id')
  10. # 事物
  11. with transaction.atomic():
  12. ret=models.Comment.objects.create(user=request.user,article_id=article_id,content=comment,parent_comment_id=par_id)
  13. models.Article.objects.filter(pk=article_id).update(comment_count=F('comment_count')+)
  14. if par_id:
  15. back_msg['par_name']=ret.parent_comment.user.username
  16. back_msg['par_content']=ret.parent_comment.content
  17. back_msg['status']=True
  18. back_msg['user_name']=ret.user.username
  19. back_msg['time']=ret.create_time.strftime('%Y-%m-%d')
  20. back_msg['content']=ret.content
  21. back_msg['msg']='评论成功'
  22. else:
  23. back_msg['status'] = False
  24. back_msg['msg'] = '您没有登录'
  25.  
  26. return JsonResponse(back_msg)

Django ---- blog项目学习所得的更多相关文章

  1. Django blog项目知识点总结

    数据库操作部分 当我们在Django项目中的models.py下写好创建表的代码后.为了创建好这些数据库表,我们再一次请出我的工程管理助手 manage.py.激活虚拟环境,切换到 manage.py ...

  2. Django快速学习搭建blog项目

    新手学习Django,本文学习的文档是<Django Web开发指南>.好了我也是新手,没什么好说了,go!- 首先先确定环境,我是在linux(Ubuntu14.04 gnome)下. ...

  3. Django学习笔记(20)——BBS+Blog项目开发(4)Django如何使用Bootstrap

    本文学习如何通过Django使用Bootstrap.其实在之前好几个Django项目中已经尝试使用过了Bootstrap,而且都留有学习记录,我已经大概有了一个大的框架,那么本文就从头再走一遍流程,其 ...

  4. Django学习笔记(19)——BBS+Blog项目开发(3)细节知识点补充

    本文将BBS+Blog项目开发中所需要的细节知识点进行补充,其中内容包括KindEditor编辑器的使用,BeautifulSoup 模块及其防XSS攻击,Django中admin管理工具的使用,me ...

  5. Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程

    这篇博客主要完成一个BBS+Blog项目,那么主要是模仿博客园的博客思路,使用Django框架进行练习. 准备:项目需求分析 在做一个项目的时候,我们首先做的就是谈清楚项目需求,功能需求,然后才开始写 ...

  6. Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现

    本文主要学习验证码功能的实现,为了项目BBS+Blog项目打下基础. 为了防止机器人频繁登陆网站或者破坏分子恶意登陆,很多用户登录和注册系统都提供了图形验证码功能. 验证码(CAPTCHA)是“Com ...

  7. django form使用学习记录

    Django forms使用容易, 又方便扩展, 因此Django admin和CBVs基本都基于forms使用. 事实上, 由于django forms的强大验证功能, 大多数Django API ...

  8. Django练习项目之搭建博客

    背景:自从今年回家过年后,来到公司给我转了试用,我的学习效率感觉不如从前,而且刚步入社会我总是想要怎么想明白想清楚一些事,这通常会花掉,消耗我大量的精力,因为我想把我的生活管理规划好了,而在it技术学 ...

  9. django创建项目

    django创建项目 安装django pip install django==1.9 Note: C:\Python34\Scripts\pip.exe 创建项目 django-admin star ...

随机推荐

  1. Oracle中如何停止正在执行SQL语句

    oracle的用P/SQL客户端中,如何停止正在执行的SQL语句? 我们使用oracle语句查询某个表时,如果查询的表数据太多,如何停止正在执行操作 如查询的表数据超过上万条时,如何停止查询操作

  2. laravel模型表建立外键约束的使用:

    模型: //表->posts class Post extends Model { //关联用户: public function user(){ //belongsTo,第一个参数:外键表,第 ...

  3. day040 数据库索引补充 存储过程 事务等

    1.正确使用索引 视图: 关键词 view 视图是体格虚拟表 创建视图 : create view 视图名称 as sql语句; 例: create view t_view as select * f ...

  4. 多态概念,C++

    body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...

  5. 运行java程序的方法-DOS命令和Eclipse方法

    ● 运行java程序的方法(使用DOS命令) 首先进行一个"文件夹选项"的设置: 在D:\Android\java_code目录下新建了一个Hello_World.java文件(不 ...

  6. python作业学员管理系统(第十二周)

    作业需求: 用户角色,讲师\学员, 用户登陆后根据角色不同,能做的事情不同,分别如下 讲师视图 管理班级,可创建班级,根据学员qq号把学员加入班级 可创建指定班级的上课纪录,注意一节上课纪录对应多条学 ...

  7. 输入三个double型的数据,放入到a,b,c三个变量中去,使用条件结构与交换逻辑将这三个变量中的值从小到大排列。

    import java.util.Scanner; public class C8{ public static void main(String []args){ /* 8.输入三个double型的 ...

  8. linux Bash 常用

    linux 帮助文档 man + [命令] eg: man ls[命令] + --help eg:ls --helphelp +[命令] eg:help ceinfo + [命令] eg:info l ...

  9. 解决MySQL不允许远程连接的问题

    进入MySQL:mysql  -u  root  -p mysql> GRANT ALL privileges ON *.* TO 'root' @'localhost' IDENTIFIED ...

  10. ubantu-vim操作

    vim其实就是vi的升级版,vi里的所有命令vim里都可以用,一般使用来说几乎没什么差别. 注:本篇文章区分大小写! vi / vim三级模式的关系: 命令行模式 任何时候,不管用户处于何种模式,只要 ...