二、发布文章
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