美文网首页
2019-08-05 第七章Django高级实战 开发企业级问答

2019-08-05 第七章Django高级实战 开发企业级问答

作者: 吕阳 | 来源:发表于2019-08-21 10:05 被阅读0次
    • 2019-05-19 开始学习第六章,现在才开始编辑第七章.我真的好拖延啊.
      7-1 文章模块models.py设计
    • python manage.py startapp articles
    • zanhu/artivles/models.py
    from django.db import models
    from six import python_2_unicode_compatible
    
    
    @python_2_unicode_compatible
    class Article(models.Model):
        STATUS = (("D", "Draft"), ("P", "Published"))
    
        title = models.CharField(max_length=255, null=False, unique=True, verbose_name='标题')
        user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="author", on_delete=models.SET_NULL, verbose_name='作者')
        image = models.ImageField(upload_to='articles_pictures/%Y/%m/%d/', verbose_name='文章图片')
        slug = models.SlugField(max_length=80, null=True, blank=True, verbose_name='(URL)别名')
        status = models.CharField(max_length=1, choices=STATUS, default='D', verbose_name='动态')  # 默认存入草稿箱
        content = models.TextField(verbose_name='内容')
        edited = models.BooleanField(default=False, verbose_name='是否可编辑')
        created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
        updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
    
        class Meta:
            verbose_name = '文章'
            verbose_name_plural = verbose_name
            ordering = ("created_at",)
    
        def __str__(self):
            return self.title
    

    7-2 使用python-slugify和django-taggit

    • python-slugify 网址显示题目的拼音.

    7-3 models.py中自定义QuerySet

    from django.db import models
    from six import python_2_unicode_compatible
    from slugify import slugify
    from taggit.managers import TaggableManager
    from django.conf import settings
    
    @python_2_unicode_compatible
    class ArticleQuerySet(models.query.QuerySet):
        """自定义QuerySet,提高模型类的可用性"""
    
        def get_published(self):
            """返回已发表的文章"""
            return self.filter(status="P")
    
        def get_drafts(self):
            """返回草稿箱的文章"""
            return self.filter(status="D")
    
        def get_counted_tags(self):
            """统计所有已发布的文章中,每一个标签的数量(大于0的)"""
            tag_dict = {}
            query = self.filter(status='P').annotate(tagged=Count('tags')).filter(tags__gt=0)
            for obj in query:
                for tag in obj.tags.names():
                    if tag not in tag_dict:
                        tag_dict[tag] = 1
    
                    else:
                        tag_dict[tag] += 1
            return tag_dict.items()
    
    @python_2_unicode_compatible
    class Article(models.Model):
        STATUS = (("D", "Draft"), ("P", "Published"))
    
        title = models.CharField(max_length=255, null=False, unique=True, verbose_name='标题')
        user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name="author", on_delete=models.SET_NULL, verbose_name='作者')
        image = models.ImageField(upload_to='articles_pictures/%Y/%m/%d/', verbose_name='文章图片')
        slug = models.SlugField(max_length=80, null=True, blank=True, verbose_name='(URL)别名')
        status = models.CharField(max_length=1, choices=STATUS, default='D', verbose_name='动态')  # 默认存入草稿箱
        content = models.TextField(verbose_name='内容')
        edited = models.BooleanField(default=False, verbose_name='是否可编辑')
        tags = TaggableManager(help_text='多个标签使用,(英文)隔开', verbose_name='标签')
        created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
        updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
        objects = ArticleQuerySet.as_manager()
    
        class Meta:
            verbose_name = '文章'
            verbose_name_plural = verbose_name
            ordering = ("created_at",)
    
        def __str__(self):
            return self.title
    
        def save(self, *args, **kwargs):
            if not self.slug:
                # 根据作者和标题生成文章在URL中的别名
                self.slug = slugify(self.title)
            super(Article, self).save(*args, **kwargs)
    
    • zhanhu/config/setting/base.py
    THIRD_PARTY_APPS = [
        "crispy_forms",
        "allauth",
        "allauth.account",
        "allauth.socialaccount",
        "rest_framework",
        "sorl.thumbnail",
        "taggit",
    ]
    LOCAL_APPS = [
        'users.apps.UsersConfig',
        'news.apps.NewsConfig',
        'articles.apps.ArticlesConfig',
        # Your stuff: custom apps go here
    ]
    

    7-4 完成文章列表页开发

    • articles/views.py
    from django.contrib.auth.mixins import LoginRequiredMixin
    from django.shortcuts import render
    
    # Create your views here.
    from django.views.generic import ListView
    
    from dacall.articles.models import Article
    
    
    class ArticlesListView(LoginRequiredMixin, ListView):
        """已发布的文章列表"""
        model = Article
        paginate_by = 10
        context_object_name = "articles"
        template_name = "articles/article_list.html"  # 可省略
    
        def get_context_data(self, *args, **kwargs):
            context = super(ArticlesListView, self).get_context_data(*args, **kwargs)
            context['popular_tags'] = Article.objects.get_counted_tags()
            return context
    
        def get_queryset(self, **kwargs):
            return Article.objects.get_published()
    
    • /articles/urls.py
    #!/usr/bin/python3
    # -*- coding:utf-8 -*-
    # __author__ = '__Jack__'
    
    from django.urls import path
    
    from zanhu.articles import views
    
    app_name = 'articles'  # Django > 2.0,这样模板中可以使用"{% url 'articles:list' %}","articles"是总urls.py中定义的namespace
    
    urlpatterns = [
        path('', views.ArticlesListView.as_view(), name='list'),
        path('write-new-article/', views.CreateArticleView.as_view(), name='write_new'),
        path('drafts/', views.DraftsListView.as_view(), name='drafts'),
        path('edit/<int:pk>/', views.EditArticleView.as_view(), name='edit_article'),
        path('<slug>/', views.DetailArticleView.as_view(), name='article'),
    ]
    
    
    • config/urls.py
    from django.conf import settings
    from django.urls import include, path
    from django.conf.urls.static import static
    from django.views.generic import TemplateView
    from django.views import defaults as default_views
    
    from dacall.news.views import NewsListView
    
    urlpatterns = [
        path('', NewsListView.as_view(), name='home'),
    
    
    
        # User management
        path("users/", include("dacall.users.urls", namespace="users")),
        path("accounts/", include("allauth.urls")),
    
        path('news/', include('dacall.news.urls', namespace='news')),
        path('articles/', include('dacall.articles.urls', namespace='articles')),
        # Your stuff: custom urls includes go here
    ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    
    if settings.DEBUG:
        # This allows the error pages to be debugged during development, just visit
        # these url in browser to see how these error pages look like.
        urlpatterns += [
            path(
                "400/",
                default_views.bad_request,
                kwargs={"exception": Exception("Bad Request!")},
            ),
            path(
                "403/",
                default_views.permission_denied,
                kwargs={"exception": Exception("Permission Denied")},
            ),
            path(
                "404/",
                default_views.page_not_found,
                kwargs={"exception": Exception("Page not Found")},
            ),
            path("500/", default_views.server_error),
        ]
        if "debug_toolbar" in settings.INSTALLED_APPS:
            import debug_toolbar
    
            urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns
    
    
    • /templates/base.html
    {% load static compress thumbnail %}<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>{% block title %}赞乎{% endblock title %}</title>
        <link rel="icon" type="image/png" href="{% static 'img/favicon.png' %}">
        <meta name="description" content="赞乎问答社区">
        <meta name="author" content="__Jack__">
    
        <!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
        <!--[if lt IE 9]>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
        <![endif]-->
    
        {% compress css %}
            <!-- Latest compiled and minified Bootstrap 4 beta CSS -->
            <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
            <!-- Your stuff: Third-party CSS libraries go here -->
            <link rel="stylesheet" href="{% static 'fonts/font-awesome-4.7.0/css/font-awesome.min.css' %}">
            <!-- This file stores project-specific CSS -->
            <link rel="stylesheet" href="{% static 'css/zanhu.css' %}">
            {% block css %}{% endblock css %}
        {% endcompress %}
    
    </head>
    <body>
    <nav class="navbar fixed-top navbar-expand-sm bg-light">
        <div class="container">
            <a class="navbar-brand" href="{% url 'news:list' %}">赞 乎&nbsp;&nbsp;</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#mainMenu" aria-controls="mainMenu"
                    aria-expanded="false"
                    aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="mainMenu">
    
                <ul class="navbar-nav">
                    <li class="nav-item">
                        <a class="btn-sm" href="#" id="notifications" data-toggle="popover" data-title="通知">
                            <i class="fa fa-bell-o" aria-hidden="true"></i>
                        </a>
                    </li>
                </ul>&nbsp;&nbsp;
    
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item"><a class="nav-link" href="{% url 'news:list' %}">&nbsp;&nbsp;首页</a></li>&nbsp;&nbsp;
                    <li class="nav-item"><a class="nav-link" href="{% url 'articles:list' %}">文章</a></li>&nbsp;&nbsp;
    {#                <li class="nav-item"><a class="nav-link" href="{% url 'qa:unanswered_q' %}">问答</a></li>&nbsp;&nbsp;#}
    {#                <li class="nav-item"><a class="nav-link" href="{% url 'messager:messages_list' %}">私信</a></li>&nbsp;&nbsp;#}
                </ul>
    
                <form role="search" action="#}">
                    <div class="input-group">
                        <input name="q" type="search" id="searchInput" class="form-control" placeholder="搜索" aria-label="Search">
                        <div class="input-group-append">
                            <button class="input-group-text"><i class="fa fa-search" aria-hidden="true"></i></button>
                        </div>
                    </div>
                </form>
                {% if request.user.is_authenticated %}
                    <ul class="navbar-nav">
                        <li class="nav-item dropdown">
                            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true"
                               aria-expanded="false">
                                {% thumbnail request.user.picture "x40" as im %}
                                    <img src="{{ im.url }}" style="border-radius: 50%;" alt="用户头像" class="user-image">
                                {% empty %}
                                    <img src="{% static 'img/user.png' %}" height="40px" alt="没有头像"/>
                                {% endthumbnail %}
                                {{ request.user.username }}
                            </a>
                            <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                                <a class="dropdown-item" href="{% url 'users:detail' request.user.username %}">
                                    <i class="fa fa-cogs fa-fw" aria-hidden="true"></i> 设置</a>
                                <div class="dropdown-divider"></div>
                                <a class="dropdown-item" href="{% url 'account_logout' %}">
                                    <i class="fa fa-sign-out fa-fw" aria-hidden="true"></i> 退出</a>
                            </div>
                        </li>
                    </ul>
                {% endif %}
            </div>
        </div>
    </nav>
    <div class="mb-3"></div>
    <div class="container">
        {% if messages %}
            {% for message in messages %}
                <div id="messages" class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">
                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
                    {{ message }}
                </div>
            {% endfor %}
        {% endif %}
        {% block content %}{% endblock content %}
    </div>
    <!-- /container -->
    
    <!-- Le javascript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    
    <!-- Required by Bootstrap v4 -->
    {% compress js %}
        <script src="{% static 'js/jquery.min.js' %}"></script>
        <script src="{% static 'js/popper.min.js' %}" type="text/javascript"></script>
        <script src="{% static 'js/bootstrap.min.js' %}" type="text/javascript"></script>
        <!-- Your stuff: Third-party javascript libraries go here -->
        <script src="{% static 'js/jquery-ui.min.js' %}" type="text/javascript"></script>
        <!-- place project specific Javascript in this file -->
        <script src="{% static 'js/zanhu.js' %}" type="text/javascript"></script>
        <script src="{% static 'js/websocketbridge.js' %}" type="text/javascript"></script>
        <script type="text/javascript">
            const currentUser = "{{ request.user.username }}";
        </script>
        {% block js %}{% endblock js %}
    {% endcompress %}
    
    </body>
    
    <!-- /.container -->
    </html>
    
    

    7-5 用户发表文章与保存草稿

    • forms.py
    from django import forms
    
    
    from dacall.articles.models import Article
    
    
    class ArticleForm(forms.ModelForm):
        # status = forms.CharField(widget=forms.HiddenInput())  # 隐藏
        # edited = forms.BooleanField(widget=forms.HiddenInput(), required=False, initial=False)  # 隐藏
        # content = MarkdownxFormField()
    
        class Meta:
            model = Article
            fields = ["title", "content", "image"]
    
    
    • url
        path('write-new-article/', views.CreateArticleView.as_view(), name='write_new'),
        path('drafts/', views.DraftsListView.as_view(), name='drafts'),
        path('edit/<int:pk>/', views.EditArticleView.as_view(), name='edit_article'),
    
    
    • views.py
    
    class DraftsListView(ArticlesListView):
        """草稿箱文章列表"""
    
        def get_queryset(self, **kwargs):
            # 当前用户的草稿
            return Article.objects.filter(user=self.request.user).get_drafts()
    
    
    
    
    class CreateArticleView(LoginRequiredMixin, CreateView):
        """创建文章"""
        model = Article
        message = "您的文章已创建成功!"  # Django框架中的消息机制
        form_class = ArticleForm
        template_name = 'articles/article_create.html'
    
        def form_valid(self, form):
            form.instance.user = self.request.user
            return super(CreateArticleView, self).form_valid(form)
    
        def get_success_url(self):
            """创建成功后跳转"""
            messages.success(self.request, self.message)  # 消息传递给下一次请求
            return reverse_lazy('articles:list')
    
    

    7-6 实现Markdown编辑与实时预览

    • 安装markdownx
    • settting/base.py
    THIRD_PARTY_APPS = [
        "crispy_forms",
        "allauth",
        "allauth.account",
        "allauth.socialaccount",
        "rest_framework",
        "sorl.thumbnail",
        "taggit",
        "markdownx",
    ]
    
    • 总url.py
        # 第三方应用
        # path('comments/', include('django_comments.urls')),
        path('markdownx/', include('markdownx.urls')),
    
    • forms
    from django import forms
    
    from markdownx.fields import MarkdownxFormField
    
    from dacall.articles.models import Article
    
    
    class ArticleForm(forms.ModelForm):
        status = forms.CharField(widget=forms.HiddenInput())  # 隐藏
        edited = forms.BooleanField(widget=forms.HiddenInput(), required=False, initial=False)  # 隐藏
        content = MarkdownxFormField()
    
        class Meta:
            model = Article
            fields = ["title", "content", "image", "tags", "status", "edited"]
    

    pipenv install django-markdownx 没有成功

    后来是 pip install django-markdownx成功的.

    python manage.py makemigrations
    python manage.py migrate  
    python manage.py collectstatic 
    
    
    
    • python manage.py collectstatic 这步骤

    7-7 通用类视图CreateView源码详解
    7-8 用户浏览文章内容

    class DetailArticleView(LoginRequiredMixin, DetailView):
        """文章详情"""
        model = Article
        template_name = 'articles/article_detail.html'
    
        path('<slug>/', views.DetailArticleView.as_view(), name='article'),
    

    7-9 django-contrib-comments实现评论文章
    7-10 用户编辑文章
    7-11 通用类视图UpdateView源码详解
    7-12 Django Template Language语法精讲
    7-13 Django Template Language语法精讲
    7-14 模型类和视图的测试用例
    7-15 本章总结与课后作业.

    相关文章

      网友评论

          本文标题:2019-08-05 第七章Django高级实战 开发企业级问答

          本文链接:https://www.haomeiwen.com/subject/ulevdctx.html