优秀的编程知识分享平台

网站首页 > 技术文章 正文

Django写自己的个人博客【纯小白】

nanyue 2024-08-04 17:12:41 技术文章 9 ℃

# 杨士航老师的:github里面有源代码

https://github.com/HaddyYang/django2.0-course/blob/master/26.%E5%9B%9E%E5%A4%8D%E5%8A%9F%E8%83%BD%E8%AE%BE%E8%AE%A1%E5%92%8C%E6%A0%91%E7%BB%93%E6%9E%84/comment/forms.py

1.早上把26课时的,评论树做了一遍。

1. 修改comment-model.py

```

from django.db import models

from django.contrib.contenttypes.fields import GenericForeignKey

from django.contrib.contenttypes.models import ContentType

from django.contrib.auth.models import User

# Create your models here.

class Comment(models.Model):

content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING)

object_id = models.PositiveIntegerField()

content_object = GenericForeignKey('content_type', 'object_id')

text = models.TextField()

user = models.ForeignKey(User,related_name='comments', on_delete=models.DO_NOTHING)

root = models.ForeignKey('self', related_name='root_comment', null=True, on_delete=models.DO_NOTHING)

parent = models.ForeignKey('self', related_name='parent_comment', null=True, on_delete=models.DO_NOTHING)

reply_to = models.ForeignKey(User, related_name='replies', null=True, on_delete=models.DO_NOTHING)

comment_time = models.DateTimeField(auto_now_add=True)

def __str__(self):

return self.text

class Meta:

ordering = ['-comment_time']

```

2.forms.py

```

from django import forms

from django.contrib.contenttypes.models import ContentType

from django.db.models import ObjectDoesNotExist

from ckeditor.widgets import CKEditorWidget

from .models import Comment

class CommentForm(forms.Form):

content_type = forms.CharField(widget=forms.HiddenInput)

object_id = forms.IntegerField(widget=forms.HiddenInput)

text = forms.CharField(widget=CKEditorWidget(config_name='comment_ckeditor'),

error_messages={'required': '评论内容不能为空'})

reply_comment_id = forms.IntegerField(widget=forms.HiddenInput(attrs={'id': 'reply_comment_id'}))

def __init__(self, *args, **kwargs):

if 'user' in kwargs:

self.user = kwargs.pop('user')

super(CommentForm, self).__init__(*args, **kwargs)

def clean(self):

# 判断用户是否登录

if self.user.is_authenticated:

self.cleaned_data['user'] = self.user

else:

raise forms.ValidationError('用户尚未登录')

# 评论对象验证

content_type = self.cleaned_data['content_type']

object_id = self.cleaned_data['object_id']

try:

model_class = ContentType.objects.get(model=content_type).model_class()

model_obj = model_class.objects.get(pk=object_id)

self.cleaned_data['content_object'] = model_obj

except ObjectDoesNotExist:

raise forms.ValidationError('评论对象不存在')

return self.cleaned_data

def clean_reply_comment_id(self):

reply_comment_id = self.cleaned_data['reply_comment_id']

if reply_comment_id < 0:

raise forms.ValidationError('回复出错')

elif reply_comment_id == 0:

self.cleaned_data['parent'] = None

elif Comment.objects.filter(pk=reply_comment_id).exists():

self.cleaned_data['parent'] = Comment.objects.get(pk=reply_comment_id)

else:

raise forms.ValidationError('回复出错')

return reply_comment_id

```

3.comment.py

```

from django.shortcuts import render, redirect

from django.contrib.contenttypes.models import ContentType

from django.urls import reverse

from django.http import JsonResponse

from .models import Comment

from .forms import CommentForm

def update_comment(request):

# referer = request.META.get('HTTP_REFERER', reverse('index'))

comment_form = CommentForm(request.POST, user=request.user)

data = {}

if comment_form.is_valid():

# 检查通过,保存数据

comment = Comment()

comment.user = comment_form.cleaned_data['user']

comment.text = comment_form.cleaned_data['text']

comment.content_object = comment_form.cleaned_data['content_object']

parent = comment_form.cleaned_data['parent']

if not parent is None:

comment.root = parent.root if not parent.root is None else parent

comment.parent = parent

comment.reply_to = parent.user

comment.save()

# 返回数据

data['status'] = 'SUCCESS'

data['username'] = comment.user.username

data['comment_time'] = comment.comment_time.strftime('%Y-%m-%d %H:%M:%S')

data['text'] = comment.text

if not parent is None:

data['reply_to'] = comment.reply_to.username

else:

data['reply_to'] = ''

data['pk'] = comment.pk

data['root_pk'] = comment.root.pk if not comment.root is None else ''

else:

#return render(request, 'error.html', {'message': comment_form.errors, 'redirect_to': referer})

data['status'] = 'ERROR'

data['message'] = list(comment_form.errors.values())[0][0]

return JsonResponse(data)

```

4.blogname=views.py

```

def blog_detail(request, blog_pk):

blog = get_object_or_404(Blog, pk=blog_pk)

read_cookie_key = read_statics_once_read(request, blog)

blog_content_type = ContentType.objects.get_for_model(blog)

comments = Comment.objects.filter(content_type=blog_content_type, object_id=blog.pk, parent=None)

context = {

"next_blog": Blog.objects.filter(created_time__lt=blog.created_time).first(),

'previous_blog': Blog.objects.filter(created_time__gt=blog.created_time).last(),

'blog': blog,

'comments': comments,

'comment_form': CommentForm(initial={'content_type': blog_content_type.model,

'object_id': blog_pk, 'reply_comment_id': 0}),

}

response = render(request, 'blog/blog_detail.html', context=context)

response.set_cookie(read_cookie_key, 'true', max_age=60, expires=datetime)

return response

```

[***ps:动态初始值

Form.initial

用于initial在运行时声明表单字段的初始值。例如,您可能希望username使用当前会话的用户名填写字段。

要完成此操作,请使用a的initial参数Form。如果给出此参数,则应该是将字段名称映射到初始值的字典。仅包括您要为其指定初始值的字段; 没有必要在表单中包含每个字段。例如:

f = ContactForm(initial={'subject': 'Hi there!'})

这些值仅针对未绑定的表单显示,如果未提供特定值,则不会将它们用作回退值。

如果Field定义和你包括实例化时,那么后者 将具有优先权。在此示例中,在字段级别和表单实例级别提供***](https://docs.djangoproject.com/en/2.1/ref/forms/apia/#dynamic-initial-values)

6.非前端人员基本没空搞懂里面的AJAX,简直是烦死了,这里的思路是,返回信息的同时不刷新页面,个人感觉直接拿去用算了,根本很难读懂JS语句,如果硬要学习,等于增加了学习成本。但是,实际工作中很少用到,因为前后端基本是分离的。

blog_detail.html

```

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>博客</title>

<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<link rel="stylesheet" href="{% static 'css/base.css' %}">

<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>

<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>

<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->

<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->

<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>

<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>

</head>

<body>

<div class="container">

<nav class="navbar navbar-default">

<div class="container-fluid">

<div class="navbar-header">

<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">

<span class="sr-only">Toggle navigation</span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

</button>

<a class="navbar-brand" href="/">博客详情</a>

</div>

<div id="navbar" class= "navbar-collapse collapse">

<ul class="nav navbar-nav">

<li><a href="/">首页</a></li>

<li><a href="{% url 'blog:blog_list' %} ">分类</a></li>

</ul>

</div><!--/.nav-collapse -->

</div><!--/.container-fluid -->

</nav>

<div class="row">

<div class="col-xs-10 col-xs-offset-1">

<h3>{{ blog.title }}</h3>

<ul class="blog_info_description">

<li>作者:{{ blog.author }}</li>

<li>发表日期:{{ blog.created_time |date:"Y-m-d G:i:s" }}</li>

<li>分类: <a href="{% url 'blog:blogs_with_type' blog_type_pk=blog.blog_type.pk %}">{{ blog.blog_type }}</a></li>

<li>阅读:{{ blog.get_read_num }}</li>

</ul>

<div class="blog-content">{{ blog.content|safe }}</div>

<div class="blog-more">

<p>上一篇:

{% if previous_blog %}

<a href="{% url 'blog:blog_detail' previous_blog.pk %}">{{ previous_blog.title }}</a>

{% else %}

没有了

{% endif %}

</p>

<p>下一篇:

{% if next_blog %}

<a href="{% url 'blog:blog_detail' next_blog.pk %}">{{ next_blog.title }}</a>

{% else %}

没有了

{% endif %}

</p>

</div>

</div>

</div>

<div class="row">

<div class="col-xs-10 col-xs-offset-1">

<div class="comment-area">

<h3 class="comment-area-title">提交评论</h3>

{% if user.is_authenticated %}

<form id='comment_form' action="{% url 'comment:update_comment' %}" method="POST" style="overflow:hidden">

<label for="comment_text">{{ user.username }},欢迎评论</label>

<div id="reply_content" style="display:none;">

<p>回复</p>

<div id="reply_content"></div>

</div>

{% csrf_token %}

{% for field in comment_form %}

{{ field }}

{% endfor %}

<span id='comment_error' class="text-danger pull-left"></span>

<input type="submit" value="评论" class="btn btn-primary pull-right" style="float:right;">

</form>

{% else %}

没有登录,登陆后方可评论

<a class="btn btn-primary" href="{% url 'login' %}?{{ request.get_full_path }}">登陆</a>

<span>or</span>

<a class="btn btn-primary" href="{% url 'register' %}?{{ request.get_full_path}}">注册</a>

{% endif %}

</div>

<div class="comment-area">

<h3 class="comment-area-title">评论列表</h3>

<div id="comment_list">

{% for comment in comments %}

<div id="root_{{ comment.pk }}" class="comment">

<span>{{ comment.user.username }}</span>

<span>({{ comment.comment_time|date:"Y-m-d H:i:s" }}):</span>

<div id="comment_{{ comment.pk }}">

{{ comment.text|safe }}

</div>

<a href="javascript:reply({{ comment.pk }});">回复</a>

{% for reply in comment.root_comment.all %}

<div class="reply">

<span>{{ reply.user.username }}</span>

<span>({{ reply.comment_time|date:"Y-m-d H:i:s" }})</span>

<span>回复</span>

<span>{{ reply.reply_to.username }}:</span>

<div id="comment_{{ reply.pk }}">

{{ reply.text|safe }}

</div>

<a href="javascript:reply({{ reply.pk }});">回复</a>

</div>

{% endfor %}

</div>

{% empty %}

<span id="no_comment">暂无评论</span>

{% endfor %}

</div>

</div>

<script type="text/javascript">

$("#comment_form").submit(function(){

// 判断是否为空

$("#comment_error").text('');

if(CKEDITOR.instances["id_text"].document.getBody().getText().trim()==''){

$("#comment_error").text('评论内容不能为空');

return false;

}

// 更新数据到textarea

CKEDITOR.instances['id_text'].updateElement();

$.ajax({

url: "{% url 'comment:update_comment' %}",

type: 'POST',

data: $(this).serialize(),

cache: false,

success: function(data){

console.log(data);

if(data['status']=="SUCCESS"){

if($('#reply_comment_id').val()=='0'){

// 插入评论

var comment_html = '<div id="root_' + data['pk'] + '" class="comment"><span>' + data['username'] + '</span><span> (' + data['comment_time'] + '):</span><div id="comment_' + data['pk'] + '">' + data['text'] + '</div><a href="javascript:reply(' + data['pk'] + ');">回复</a></div>';

$("#comment_list").prepend(comment_html);

}else{

// 插入回复

var reply_html ='<div class="reply"><span>' + data['username'] + '</span><span> (' + data['comment_time'] + ')</span><span> 回复 </span><span>' + data['reply_to'] + ':</span><div id="comment_' + data['pk'] + '">' + data['text'] + '</div><a href="javascript:reply(' + data['pk'] + ');">回复</a></div>';

$("#root_" + data['root_pk']).append(reply_html);

}

// 清空编辑框的内容

CKEDITOR.instances['id_text'].setData('');

$('#reply_content_container').hide();

$('#reply_comment_id').val('0');

$('#no_comment').remove();

}else{

// 显示错误信息

$("#comment_error").text(data['message']);

}

},

error: function(xhr){

console.log(xhr);

}

});

return false;

});

function reply(reply_comment_id){

// 设置值

$('#reply_comment_id').val(reply_comment_id);

var html = $("#comment_" + reply_comment_id).html();

$('#reply_content').html(html);

$('#reply_content_container').show();

$('html').animate({scrollTop: $('#comment_form').offset().top - 60}, 300, function(){

CKEDITOR.instances['id_text'].focus();

});

}

</script>

</div>

</div>

</div>

<!-- Bootstrap core JavaScript

================================================== -->

<!-- Placed at the end of the document so the pages load faster -->

{# <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->#}

{# <script src="../../assets/js/ie10-viewport-bug-workaround.js"></script>#}

</body>

</html>

今天主要目的就是利用评论树和AJAX优化详情页面。

```

最近发表
标签列表