第一章Django by example

项目前准备

1.创建env环境,virtualenv --no-site-packages 文件夹名
2.激活virtualenv中的python版本,source 文件夹名/bin/activate
3.在激活状态下,安装django 

第一个Blog项目

1.    djangoadmin startproject mysite       --创建mysite项目
2.    cd mysite                             --切换至项目文件夹
3.    python3 manage.py startapp blog       --创建blog应用
4.    python3 manage.py migrate             --数据库创建初始表
5.    python3 manage.py runserver 或
      python3 manage.py runserver  -- settings=mysite.settings     --启动测试服务器

在应用文件夹下的models中创建

from django.contrib.auth.models import User  # 导入django.扩展.认证.模型中的User
from django.db import models  # 导入django.数据库中的models模型
from django.utils import timezone  # 导入django.实用工具中的timezone,需要安装pip install pytz


# Create your models here.

class Post(models.Model):
    STATUS_CHOICES = (
        ('draft', 'Draft'),
        ('published', 'Published'),
    )
    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250, unique_for_date='publish')
    body = models.TextField()
    author = models.ForeignKey(User, related_name='blog_posts')
    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
    objects = models.Manager()  # The default manager.
    published = PublishedManager()  # Our custom manager.自定义管理器

    class Meta:
        ordering = ('-publish',)  # 查询数据库时候将以publish的倒序排序

    def __str__(self):
        return self.title  # str()方法是当前对象默认的可读表现。Django将会在很多地方用到它例如管理站点中。


# ------------ 添加自定义models manager模型管理器-----------------

要放在Post之前
class PublishedManager(models.Manager):
    def get_queryset(self):
        return super(PublishedManager, self).get_queryset().filter(status='published')
# -------------------------------------------------------------

在应用文件夹下admin中注册

from django.contrib import admin

from .models import Post


# Register your models here.
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'slug', 'author', 'publish',
                    'status')  # 在管理站点中的列表显示
    list_filter = ('status', 'created', 'publish', 'author')  # 过滤显示
    search_fields = ('title', 'body')  # 搜索显示
    prepopulated_fields = {'slug': ('title',)}  # slug自动填充,在填写title的时候,slug会自动填充为title内容
    raw_id_fields = ('author',)  # 显示成为搜索控件,以id形式
    date_hierarchy = 'publish'  # 日期层次结构
    ordering = ['status', 'publish']  # admin管理界面按列表中的内容排序


admin.site.register(Post, PostAdmin)  # 注册models中的模型,PostAdmin为个性化定制

创建与迁移数据表

1.python3 manage.py makemigrations
2.python3 manage.py migrate

部分settings设置

TIME_ZONE = 'Asia/Shanghai'
Django1.9以后language code 'zh-cn'就被丢弃了,使用'zh-hans'代替

使用查询集(QuerySet)

python3 manage.py shell进入shell
from django.contrib.auth.models import User
from blog.models import Post
user = User.objects.get(username='用户名')
post = Post.objects.create(author=user,title='内容',body='内容',slug='内容')
#使用create不需要使用post.save(),直接进行数据库操作
#-----------------------------------------------------------------------------
post = Post(title='Another post', slug='another-post', body='Postbody.', \
author=user)#这样使用必须使用post.save(),否则只保存在内存中,没有插入数据库
post.save()#对数据库执行操作

#更新对象字段
post.title='改变内容'
post.save()

post.delete()#删除对象
-------------------------------------------------------------------------------

使用filter()过滤、exclude()排除、order_by()排序

Post.objects.filter(publish__year=2015)#返回在2015年的对象

#返回2015年且author是'admin'的对象
Post.objects.filter(publish__year=2015, author__username='admin')
或Post.objects.filter(publish__year=2015).filter(author__username='admin')

#返回所有2015年发布的帖子但是这些帖子的题目开头不能是Why
Post.objects.filter(publish__year=2015).exclude(title__startswith='Why')

#返回所有且对title字段升序排序
Post.objects.order_by('title')
#返回所有且对title字段倒序排序
Post.objects.order_by('-title')

查询集(QuerySet)什么时候会执行

只要你喜欢,你可以连接许多的过滤给查询集(QuerySet)而且不会立马在数据库中执行直到这个查询集(QuerySet)被执行。查询集(QuerySet)只有在以下情况中才会执行:

  • 在你第一次迭代它们的时候
  • 当你对它们的实例进行切片:例如Post.objects.all()[:3]
  • 当你对它们进行了打包或缓存
  • 当你对它们调用了repr()或len()方法
  • 当你明确的对它们调用了list()方法
  • 当你在一个声明中测试它,例如bool(), or, and, or if

创建templates(模板文件夹)与static(静态文件夹)

  • 默认创建在应用文件夹下,自定义需要在DIRS列表中定义,BASE_DIR是指mysite项目的绝对路径,可以使用完整路径
    TEMPLATES = [
    {    
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],#如果需要自定定义位置,需要在此处列表内加入
        'APP_DIRS': True,#默认在app文件夹内
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
STATIC_URL = '/static/'
 
# 当运行 python manage.py collectstatic 的时候
# STATIC_ROOT 文件夹 是用来将所有STATICFILES_DIRS中所有文件夹中的文件,以及各app中static中的文件都复制过来
# 把这些文件放到一起是为了用apache等部署的时候更方便
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
 
# 其它 存放静态文件的文件夹,可以用来存放项目中公用的静态文件,里面不能包含 STATIC_ROOT
# 如果不想用 STATICFILES_DIRS 可以不用,都放在 app 里的 static 中也可以
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "common_static"),
    '/path/to/others/static/',  # 用不到的时候可以不写这一行
)
 
# 这个是默认设置,Django 默认会在 STATICFILES_DIRS中的文件夹 和 各app下的static文件夹中找文件
# 注意有先后顺序,找到了就不再继续找了
STATICFILES_FINDERS = (
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder"
)

编辑视图文件views

from django.shortcuts import render, get_object_or_404  # 从django.快捷工具中导入render,get_object_or_404

from .models import Post  # 从模型中导入Post


# Create your views here.
def post_list(request):
    posts = Post.objects.all()  # 提取实例Post
    return render(request, 'blog/post/list.html', {'posts': posts})  # 渲染至list.html,给html文件posts对象


def post_detail(request, year, month, day, post):  # year,month,day,post等参数是从urls文件中获取
    post = get_object_or_404(Post, slug=post,
                             status='draft',
                             publish__year=year,
                             publish__month=month,
                             publish__day=day)  # 与Post.objects.get()方法相似,get_object_or_404获取不到对象会返回404

    return render(request, 'blog/post/detail.html', {'post': post})

为视图添加URL模式

在APP的文件夹下创建urls文件,惯例

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.post_list, name='post_list'),
    url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<post>[-\w]+)/$',
        views.post_detail,
        name='post_detail'),
]
  • year:需要四位数
  • month:需要两位数。不及两位数,开头带上0,比如 01,02
  • day:需要两位数。不及两位数开头带上0
  • post:可以由单词和连字符组成

在项目urls文件中(mysite/mysite/urls.py)中导入APP的urls文件

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)), 
    url(r'^blog/', include('blog.urls',
        namespace='blog',
        app_name='blog')),
]

模型(models)的标准URLs

在models中添加生成url的代码

from django.core.urlresolvers import reverse#从django.核心.解析器中导入reverse
Class Post(models.Model):
    # ...
    def get_absolute_url(self):
        return reverse('blog:post_detail',
                        args=[self.publish.year,
                              self.publish.strftime('%m'),
                              self.publish.strftime('%d'),
                              self.slug])
我们通过使用strftime()方法来保证个位数的月份和日期需要带上0来构建URL,也就是01,02,03,对象的get_absolute_url方法,会reverse反向的去找urls的blog空间中的post_detail的urls,根据urls会生成一个标准的url(http://127.0.0.1:8000/blog/YEAR/MONTH/DAY),然后url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<post>[-\w]+)/$',views.post_detail,name='post_detail')会处理,然后(?P<year>)会有变量year=VALUE,urls中写的views文件处理,渲染。

html文件,模板标签语言

文档位置(https://docs.djangoproject.com/en/1.8/)

templates/
    blog/
        base.html
        post/
            list.html
            detail.html

base为基础部分,list.html,detail.html扩展
常用:

  • {% load staticfiles %}放置顶端,导入静态文件

  • {% static "css/文件.css" %} 以static文件夹为根,相对路径

  • {% for post in posts %}
    {{post.filed}}
    {{post.get_absolute_url}}调用models中的get_absolute_url方法,生成标准url
    {% end for %}

  • {% block content %} 在base.html中设定的填充区域,填充内容可写在其他html文件中
    {% endblock %}

  • {% extends "blog/base.html" %}继承blog/base.html 模板(template)。然后content区块(blocks)中填充内容。

  • {{ post.body|truncatewords:30|linebreaks }} truncatewords用来缩短内容限制在一定的字数内,linebreaks用来转换内容中的换行符为HTML的换行符。

  • {% include "pagination.html" with page=posts %} 导入 templates下pagination.html(相对路径), 且pagination中的变量page=posts

分页 使用django内置的paganitor来完成

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger   # 导入django.核心.分页处理中的分页器,空页,不是整数页
    def post_list(request):
    object_list = Post.objects.all()
    paginator = Paginator(object_list, 3)#每页显示3个对象
    page = request.GET.get('page')#获取到页码
    try:
        posts = paginator.page(page)
    except PageNotAnInteger:#如果页码不是一个整数,显示第一页
        posts = paginator.page(1)
    except EmptyPage:#如果页码是整数但是不存在,显示最大的页
        posts = paginator.page(paginator.num_pages)

    return render(request, 'blog/post/list.html', {'page': page, 'posts': posts}) 

以上代码工作流程:
1、打开主页-->127.0.0.1:8000/blog被urls.py中定义的规则捕获-->交由定义的views中的函数处理
2、paginator=Paginator(object_list,3),每页显示3个对象,paginator为分页处理对象:常用方法有:

paginator.count #一共有多少个对象
paginator.num_pages#最大页数
paginator.page(页码)#页码的对象

3、page=request.Get.get('page'),主页没有page参数,get的结果为None,所以page不是一个整数,posts=paginator.page(1)#返回一个Page类对象

Page对象常用方法,详细使用看源码,Page对象使用.paginator方法转回Paginator对象
    def has_next(self):
        return self.number < self.paginator.num_pages

    def has_previous(self):
        return self.number > 1

    def has_other_pages(self):
        return self.has_previous() or self.has_next()

    def next_page_number(self):
        return self.paginator.validate_number(self.number + 1)

    def previous_page_number(self):
        return self.paginator.validate_number(self.number - 1)

    def start_index(self):
        """
        Returns the 1-based index of the first object on this page,
        relative to total objects in the paginator.
        """
        # Special case, return zero if no items.
        if self.paginator.count == 0:
            return 0
        return (self.paginator.per_page * (self.number - 1)) + 1

    def end_index(self):
        """
        Returns the 1-based index of the last object on this page,
        relative to total objects found (hits).
        """
        # Special case for the last page because there can be orphans.
        if self.number == self.paginator.num_pages:
            return self.paginator.count
        return self.number * self.paginator.per_page

4、进行render,显示页面

使用基于类的视图(views)简介

编辑你的blog应用下的views.py文件,如下所示:

from django.views.generic import ListView  # 从django.视图.公共 导入ListView
class PostListView(ListView):
    queryset = Post.objects.all()  # 使用一个特定的查询集(QuerySet)代替取回所有的对象。代替定义一个queryset属性,我们可以指定model = Post然后Django将会构建Post.objects.all() 查询集(QuerySet)给我们。
    context_object_name = 'posts'  #使用环境变量posts给查询结果。如果我们不指定任意的context_object_name默认的变量将会是object_list。
    paginate_by = 3  #对结果进行分页处理每页只显示3个对象。
    template_name = 'blog/post/list.html'  #使用定制的模板(template)来渲染页面。如果我们不设置默认的模板(template),ListView将会使用blog/post_list.html。

 在urls文件中,我们要调用views.PostlistView.as_view()

为了保持分页处理能工作,我们必须将正确的页面对象传递给模板(tempalte)。Django的ListView通过叫做page_obj的变量来传递被选择的页面,所以你必须编辑你的post_list_html模板(template)去包含使用了正确的变量的分页处理,如下所示:

{% include "pagination.html" with page=page_obj %}

学习来源于夜夜月翻译的django by example

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容