Django base

[TOC]

1. 创建项目

django-admin startproject server_test
# 基本文件树如下
server_test/
    manage.py
    server_test/
        __init__.py
        settings.py
        urls.py
        wsgi.py

####################################################
# manage.py 基本的命令行工具
# inner server_test文件夹是真正的django项目
# __init__.py 标识python package
# setting.py 项目配置信息
# urls.py 项目url的声明,是一个内容表
# wsgi.py uwsgi服务的入口

收集静态文件

在setting.py文件中添加

STATIC_ROOT = os.join(BASE_DIR, 'static/')

然后执行

python3 manage.py collectstatic

根据nginx的配置,在主目录下创建script/文件夹,创建uwsgi.ini uwsgi.log uwsgi.sock 文件,创建media文件夹,存放静态媒体信息

2.创建第一个app

Django是基于MTV Design Partten Model Template and View

  • Model : 数据模型,构造了一个数据接口,包含了数据的所有特征,可以获取数据,验证数据,控制数据的行为可以在忽略底层数据库的构造情况下进行数据操作

  • Template: 显示层,包含了显示相关的决策:网页上的元素该如何显示

  • View: 模型和模板之间的桥梁,处理逻辑层面

在一个django项目中可以包含多个app,同时一个app可以包含在多个项目中,创建的项目位于根目录下

python manage.py startapp polls

创建第一个view

在polls/view.py中添加内容:

rom django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

为了调用该view,需要将其映射到一个url上,在app目录下创建urls.py,引入该view,同时将app的urls引入项目的urls中

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]
# 运行
python3 manage.py runserver
# 现在可以在localhost:8000/polls/ 得到该view

3.database config

django默认使用的数据库是sqllite,在配置文件中指定其位置在项目主目录下,还支持oracle mysql postgresql

./server_test/setting.py文件中还可以设置时区:

TIME_ZONE = 'Asia/Shanhai'
USE_TZ = True

配置文件中的installed_apps 是该项目需要使用的app,django app是类似于插件的方式安装的,需要显式添加到配置文中去

polls.app.PollsConfig, # 位于polls/app.py中

一些app至少需要一个table,所以需要进行数据库迁移

python3 manage.py migrate
# 执行该命令会检查`INSTALLED_APP`中所需的数据库的表是否已经创建,对于每一个app创建其model所需的数据模式的表

创建models

在polls/models.py文件中添加

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

每一个数据模型都有相应的变量,作为数据模式,会自动添加id作为主码,注意不可以用self来标注模式,否则模式下只有id一项属性,变量名即为属性名,后面是数据类型,同时可以添加外码,表示每一个chioce都与一个question对应

在配置文件中添加app后即可进行数据库的迁移

python3 manage.py makemigrations polls

通过执行该命令告诉django对model进行了更改,数据库的模式发生了变化,所以产生了一个控制迁移的文件polls/migrations/0001_initial.py 里面记录了迁移的步骤,可以通过命令得到相应的sql

python manage.py sqlmigrate polls 0001

可以通过check命令检查是否存在问题.此时并没有进行数据库的实际改动

python3 manage.py migrate

命令行查看数据库

python3 manage.py shell
>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>

# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> q.save()

# Now it has an ID.
>>> q.id
1

# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
>>> from polls.models import Choice, Question

# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>

# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>

# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Question matching query does not exist.

# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>

# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>

# Create three choices.
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>

# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3

# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

4.创建管理员

python3 manage.py createsuperuser
Username:xx
I

5.添加更多views

可以在已经创建的app中加入更多的views polls/views.py 文件中添加相应的函数实现,然后在新创建的poll/urls.py 中添加

from django.urls import path

from . import views

urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/5/  通过该url将question_id 传递给views.py中的detail
    path('<int:question_id>/', views.detail, name='detail'),
    # ex: /polls/5/results/
    path('<int:question_id>/results/', views.results, name='results'),
    # ex: /polls/5/vote/
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

当请求获取pllos/22 django会加载根项目的urls.py 由于在里面的urlpatterns=[path('polls/', include(polls.urls))] 将app polls中的urls记载到里面,回进入polls/url.py 中查找,就可以找到detail view

6.创建模板

为了避免对视图进行硬编码,可以使用模板进行定义,在polls文件夹下创建templates文件夹,django会自动在该文件夹寻找模板.Django项目关于模板的设置在默认setting.py 文件中,描述了项目如何记载呈送模板.定义了默认的template backendAPP_DIRS=True ,会在每个INSTALLED_APP中的子文件夹寻找templates文件夹作为模板文件夹

所以在polls app中的模板要放在 poll/templates/polls/ 文件夹中

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}
  • 所有的判断和循环语句都需要使用{%%}进行嵌套

  • 用{% * %} 表示条件结束

  • 输出变量值时需要用

  • 其他语法根据html

添加到view

from django.http import HttpResponse
from django.template import loader
from django.http import Http404

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

获取模板的方式:

  • 使用loader加载模板,然后使用模板调用render函数,将所需要的参数以dict形式传递

  • 使用shortcut.render 接受三个参数,httprequest templates cointext,返回一个根据给定模板和内容的HttpResponse 对象

获取404:

  • 可以使用抛出异常的方式得到404

  • 使用shortcut.get_object_or_404(), 给定对象model 以及选择条件

Templates urlspolls

  • 当使用一个连接以<a href="/polls/{{question.id}/">...</a> 的形式出现在模板中时,采用的是硬编码的形式,当项目中的url变化时,需要进行大量的更改,不符合复用原则,可以使用在polls/url.py中定义的 url `

    ` ,如果需要更改url时只需要在urls.py中修改,不需要改动templates

  • 命名域的url:

    当一个项目有很多app时,可能出现重名状况,所以需要在url前添加命名域,这时需要在polls/urls.py中添加 app_name='polls' 既可以在url中指定命名域

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
    
    <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

7.使用生成视图,减少代码量

detail() result() 视图较为简单,直接提供一个数据,index也只提供一个列表.这些视图展示了web开发的一个基本样例:根据url参数从数据库获取数据并显示,加载一个模板,并且返回一个rendered template ,django提供了一个便捷的方式去使用views ------generic

  • 修改URL conf

  • 更改views

主要使用了generic.ListView generic.DetailView ,以这两个类作为基类,需要注意的是DetaliView 需要从url获取的主码是pk 所以需要将其进行更改

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

对于views进行更改时,这两个类需要提供一个model 参数指明所使用的数据模型, template_name 参数指明模板

from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic

from .models import Choice, Question


class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        """Return the last five published questions."""
        return Question.objects.order_by('-pub_date')[:5]


class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'


class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'


def vote(request, question_id):
    ... # same as above, no changes needed.

Attention:

之前的模板中使用lasted_question_list 作为参数列表,对于DetailView,再使用Question model 时会自动生成内容变量question_list ,所以必须对该参数进行改写

8. 使用静态文件

通常使用的静态文件包括 images, js, css django.contrib.staticfiles 收集每个app的静态文件到特定文件夹,该app包含在INSTALLED_APPS中.django的STATICFILES_FINDERS 设置了寻找静态文件的方式默认是

[
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]

首选项是在STATICFILES_DIR ,可以在项目的setting.py中设置STATICFILES_DIRS=[os.path.join(BASE_DIR, 'static/')] ;如果没有该设置,使用第二个默认选项,会在每个app的一个 子文件夹static中寻找静态文件,而最常用的文件结构是在static文件夹下再建立app/ 放置静态文件

可以使用样式表(polls/static/polls/style.css)

ul {
    color green;
}
body {
    backgoround : black url('images/background.jpg') no-repeat;
}

然后在模板中引入css:

{%load static%}
<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">
<!--该标签产生静态文件的绝对路径-->

9.定制管理员站点 admin

:electric_plug: 关于模板文件组织:

和static files类似,所有的模板可以放在同一个文件夹中,但是分属于不同app的模板和静态文件要放在每个子文件夹中,可以在醒目的根目录下创建templates/admin 来放置admin站点的模板文件,需要在setting文件中设置

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        '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',
            ],
        },
    },
]

可以修改base_site.html模板,更改站点header,对于admin中的详细内容需要在polls/admin.py 中进行修改

from django.contrib import admin
from .models import Question, Choice


class ChoiceInline(admin.TabularInline):
    """
    作为Question的内连对象,可以继承自admin.StackedInline 但是相对与需要较大的页面空间
    使用这种方式所需的页面空间较小
    """
    model = Choice
    extra = 3



class QuestionAdmin(admin.ModelAdmin):
    # 指明父类参数, fields 列表形式, fieldsets 每个元素代表一个模块,模块内是标题,内容
    fieldsets = [
        (None, {'fields':['question_text']}),
        ('Date information', {'fields':['pub_date']}),
    ]
    inlines = [ChoiceInline]

    #在全部问题页面的列表设置,包含了三个纵列
    list_display = ('question_text', 'pub_date', 'published_recently')
    # 按日期筛选
    list_filter = ['pub_date']
    # 添加搜索框
    search_fields = ['question_text']



admin.site.register(Question, QuestionAdmin)
admin.site.register(Choice)

Last updated