二、发布文章
1.写表单
Django中三种方式写form表单,可以参考这里,这里选用第三种,即继承django.forms.ModelForm类。
这种方式不需要逐个定义字段,只需将Book类和需要显示的字段传入,也可以用fields = '__all__'传和所有字段
class BookModelForm(forms.ModelForm):
class Meta:
model = models.Book
fields = '__all__' # 类似于fields = ['title','price','publish','author'],可以自定义需要显示的字段,__all__为所有字段
我的项目代码如下:
from django import forms
from markdownx.fields import MarkdownxFormField
from zanhuapp.models import Question
class QuestionForm(forms.ModelForm):
status = forms.CharField(widget=forms.HiddenInput()) # 隐藏
content = MarkdownxFormField()
class Meta:
model = Question
fields = ["title", "content", "tags", "status"]
2.前端表单
和forms.Form一样,在视图函数中向前端传入BookModelForm实例对象,就能自动生成form表单
不过我测试的时候,上面的测试不成功,只好用原生form表单测试。
可参考:
https://edu.51cto.com/course/2787.html
三、测试
1.question_form
<form action="/ask-question/" method="post">
{% csrf_token %}
{% for field in form %}
<div >
{{ field.label }}
{{ field }}
</div>
{% endfor %}
<input type="submit" value="保存"/>
</form>
2.views
@login_required
def myCreateQuestion(request):
'''更新个人资料'''
questiond = Question()
questiond.title = "my tlll"
questiond.content = "test content"
questiond.user_id = 1
questiond.tags = "mytag"
questiond.save()
return render(request, 'zanhuapp/question_form.html')
3.成果展示

4.进一步改进,使用下面的代码可以获得当前用户的ID
questiond.user_id = request.user.id
5.前端原生form表单
<form action="{% url 'qa:ask_question' %}" method="post">
{% csrf_token %}
<input type="text" name="title">
<input type="submit" value="POST请求">
</form>
前端用上面的代码,views用下面的代码:
questiond = Question()
if request.method=='POST':
questiond.title = request.POST.get('title')
就可以成功发布title了。
四、发布问题页面
最后终于搞定了发布问题的布面,
1.前端代码:
<form id="question_form" method="post" action="{% url 'qa:ask_question' %}">
{% csrf_token %}
<div class="publish-from">
<label class="publish-from--label">问题标题:</label>
<input class="form-control" type="text" name="title" value="" />
</div>
<div class="publish-from">
<label class="publish-from--label">问题描述:</label>
<textarea name="content" rows="15" class="form-control"></textarea>
</div>
<input type="submit" class="btn btn-large btn-success" value="发布">
</form>
备注:
from 对应的url是点击发布成功后返回的url,所以后面对应的url必须有一个view来处理数据。
2.url
path('ask-question/', views.myCreateQuestion, name='ask_question'),
3.后台接收数据
@login_required
def myCreateQuestion(request):
'''更新个人资料'''
questiond = Question()
if request.method=='POST':
questiond.title = request.POST.get('title')
questiond.content = request.POST.get('content')
questiond.user_id = request.user.id
questiond.save()
return render(request, 'zanhuapp/index.html')
五、升级发布问题
上面的代码虽然work,但是发布问题之后仍然停留在发布问题的页面,我想实现的是用户发布问题之后跳转到问题的列表页面,尝试用class base view来实现。
开始照着别人的代码来写,老是测试不成功,而且没有报错,后为不管它,回家想了想,在网上查了一下,终于找到解决方案了。(这也是解决困难问题的方法之一,先放下,再想解决办法,而不是坐在电脑前发脾气,死嗑)
只因为我没有定义form_invalid的方法,所以没有找到问题到底出在哪儿。
view的代码:
@method_decorator(cache_page(60 * 60), name='get')
class CreateQuestionView(LoginRequiredMixin, CreateView):
"""用户提问"""
form_class = QuestionForm
template_name = 'zanhuapp/question_form.html'
message = "问题已提交!"
def form_valid(self, form):
form.instance.user = self.request.user
return super(CreateQuestionView, self).form_valid(form)
def form_invalid(self, form): # 定义表对象没有添加失败后跳转到的页面。
print(form)
return HttpResponse("form is invalid.. this is just an HttpResponse object")
def get_success_url(self):
messages.success(self.request, self.message)
return reverse_lazy("qa:index")
后来通过print(form)才发现原来有一个字段没有填写数据,原来的代码是通过js来提交的,而且是在questionform.py中设定了为隐藏的字段,我直接将它注释掉,终于可以实现发布文章并自动返回文章列表页了。
class QuestionForm(forms.ModelForm):
# status = forms.CharField(widget=forms.HiddenInput()) # 隐藏
content = MarkdownxFormField()
class Meta:
model = Question
fields = ["title", "content", "tags"]
六、发布回复的页面
这里被一些冗余的代码耽误了,测试老是不成功,其实很简单的。
1.前端代码:
<form id="answer-form" method="post" action="/propose-answer/{{ question.id }}/" >
{% csrf_token %}
<textarea rows="5" name="dbwcontent" class="fe-editor form-control"></textarea>
<!-- <div class="aw-replay-box-footer clearfix">
<label class="pull-left">
<input type="checkbox" name="anonymous" value="1" />匿名回答</label>
</div> -->
<input type="submit" class="pull-right button button--green" value="发布回答">
</form>
2.url
path('propose-answer/<int:question_id>/', views.myCreateAnswer, name='propose_answer'),
3.views
@login_required
def myCreateAnswer(request,question_id):
'''更新个人资料'''
answerd = Answer()
if request.method=='POST':
answerd.content = request.POST.get('dbwcontent')
answerd.user_id = request.user.id
answerd.question_id = question_id
# print(question_id)
answerd.save()
return render(request, 'zanhuapp/hello.html',{'question_id': question_id})
七、回复者的头像
1.安装sorl-thumbnail(pip install sorl-thumbnail)
并在settings.py中添加“sorl.thumbnail”。
2.头像调用
{% load static thumbnail %}
{% thumbnail answer.user.picture "x50" as im %}
<a href="/" class="sw-qdata--author__name aw-user-img aw-border-radius-5" rel="nofollow" target="_blank">
<img src="{{ im.url }}" alt="用户头像">
{% empty %}
<a href="/" class="sw-qdata--author__name aw-user-img aw-border-radius-5" rel="nofollow" target="_blank">
<img src="{% static 'img/user.png' %}" alt="没有头像"/></a>
{% endthumbnail %}
3.头像文件
需要在项目根目录建立static文件夹,即在zanhu\static\img\文件夹下面放置user.png文件。
这样就能显示图像了。
八、回复者的用户名
1.显示回复者的用户名
<a class="aw-user-name" href="/">{{ answer.user.get_profile_name }}</a>
下面的代码最开始测试的时候必须加上,后来测试好了之后发现不加也可以了。
2.相关url
path('update/', views.UserUpdateView.as_view(), name='update'),
path('<username>/', views.UserDetailView.as_view(), name='detail'),
3.相关Views
class UserUpdateView(LoginRequiredMixin, UpdateView):
"""用户只能更新自己的信息"""
fields = ['nickname', 'email', 'picture', 'introduction', 'job_title', 'location',
'personal_url', 'weibo', 'zhihu', 'github', 'linkedin']
model = User
template_name = 'users/user_form.html'
def get_success_url(self):
"""更新成功后跳转到用户自己页面"""
return reverse('users:detail', kwargs={'username': self.request.user.username})
def get_object(self, queryset=None):
return self.request.user
class UserDetailView(LoginRequiredMixin, DetailView):
model = User
template_name = 'users/user_detail.html'
slug_field = 'username'
slug_url_kwarg = 'username'
def get_context_data(self, *args, **kwargs):
context = super(UserDetailView, self).get_context_data(**kwargs)
user = User.objects.get(username=self.request.user.username)
context["moments_num"] = user.publisher.filter(reply=False).count() # 'publisher'为News表中的related_name
context["article_num"] = user.author.filter(status='P').count() # 只算已发表的文章
context["comment_num"] = user.publisher.filter(reply=True).count() + user.comment_comments.all().count() # 文章+动态的评论数
context["question_num"] = user.q_author.all().count()
context["answer_num"] = user.a_author.all().count()
# 互动数 = 动态点赞数 + 问答点赞数 + 评论数 + 私信用户数(都有发送或接收到私信)
tmp = set()
# 我发送私信给了多少不同的用户
sent_num = user.sent_messages.all()
for i in sent_num:
tmp.add(i.recipient.username)
# 我接收的所有私信来自多少不同的用户
received_num = user.received_messages.all()
for r in received_num:
tmp.add(r.sender.username)
context["interaction_num"] = user.liked_news.all().count() + user.qa_vote.all().count() + context["comment_num"] + len(tmp)
return context
九、搜索功能
基本上按以前的代码就可以了。
def search(request):
allQuestions = Question.objects.all()
q = request.GET.get('q')
error_msg = ''
if not q:
error_msg = '请输入关键词'
return render(request, 'zanhuapp/index.html', {'error_msg': error_msg})
else:
questions_list = allQuestions.filter(title__icontains=q)
return render(request, 'zanhuapp/index.html', {'error_msg': error_msg,
'questions_list': questions_list})
十、侧边栏
使用views传多个参数
def get_context_data(self, *, object_list=None, **kwargs):
context = super(QuestionDetailView, self).get_context_data()
context["new_questions"] = Question.objects.all()[:10]
context["hot_users"] = User.objects.all()[:10]
return context
然后在模板中就可以使用hot_users、new_questions进行展示了。
十一、分页功能
1.上一次开发还要使用插件,现在直接修改下面代码中的paginate_by = 5的数量就可以了。
class QuestionListView(LoginRequiredMixin, ListView):
"""所有问题页"""
queryset = Question.objects.select_related('user')
paginate_by = 5
context_object_name = "questions"
template_name = "zanhuapp/question_list.html"
def get_context_data(self, *, object_list=None, **kwargs):
context = super(QuestionListView, self).get_context_data()
context["popular_tags"] = Question.objects.get_counted_tags() # 页面的标签功能
context["active"] = "all"
context["new_questions"] = Question.objects.all().order_by('-created_at')[:10]
context["hot_users"] = User.objects.all()[:10]
return context
2.然后在网上找了一段代码,直接复制上去就可以了:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<h3>Django Boostrap 4分页模板</h3>
{# 注释: page_obj不要改。for里的article可以改成自己对象 #}
{% if page_obj %}
<ul> {% for article in page_obj %}
<li><a href="{% url 'blog:article_detail' article.id %}"> {{ article.title }}</a> {{ article.pub_date | date:"Y-m-j" }}</li>
{% endfor %}
</ul>
{# 注释: 下面代码一点也不要动 #}
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Previous</span></li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active"><span class="page-link"> {{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Next</span></li>
{% endif %}
</ul>
{% endif %}
{% else %}
{# 注释: 这里可以换成自己的句子 #}
<p>No article yet</p>
{% endif %}
参考:https://blog.csdn.net/weixin_42134789/article/details/80568089