Django笔记
安装:pip install django
查看版本号:python -m django --version
新建项目:django-admin startproject mysite或使用Pycharm创建Django项目,file-->new project
各文件和目录解释:
- 外层的projectName
/
目录与Django无关,只是你项目的容器,可以任意命名。 manage.py
:一个命令行工具,用于与Django进行不同方式的交互脚本,非常重要!- 内层的projectName
/
目录是真正的项目文件包裹目录,它的名字是你引用内部文件的包名,例如:projectName.urls
。 projectName/__init__.py
:一个定义包的空文件。projectName/settings.py
:项目的主配置文件,非常重要!projectName/urls.py
:路由文件,所有的任务都是从这里开始分配,相当于Django驱动站点的内容表格,非常重要!projectName/wsgi.py
:一个基于WSGI的web服务器进入点,提供底层的网络通信功能,通常不用关心。
启动项目:在项目根目录下,运行Python manage.py runserver
启动项目-指定端口:python manage.py runserver 8080
启动项目-指定IP端口:python manage.py runserver 0.0.0.0:8000
配置项目:打开projectName/settings.py
配置文件,这是整个Django项目的设置中心。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'other': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mysite', 'HOST': '192.168.1.1', 'USER': 'root', 'PASSWORD': 'pwd', 'PORT': '3306', } } """ 1、ENGINE(引擎):可以是django.db.backends.sqlite3、django.db.backends.postgresql、django.db.backends.mysql、django.db.backends.oracle,当然其它的也行。 2、NAME(名称):类似Mysql数据库管理系统中用于保存项目内容的数据库的名字。如果你使用的是默认的SQLite,那么数据库将作为一个文件将存放在你的本地机器内,此时的NAME应该是这个文件的完整绝对路径包括文件名,默认值os.path.join(BASE_DIR, ’db.sqlite3’),将把该文件储存在你的项目目录下。 注意: 1、在使用非SQLite的数据库时,请务必预先在数据库管理系统的提示符交互模式下创建数据库,你可以使用命令:“CREATE DATABASE database_name;”。Django不会自动帮你做这一步工作。 2、确保你在settings文件中提供的数据库用户具有创建数据库表的权限 3、如果你使用的是SQLite,那么你无需做任何预先配置,直接使用就可以了。 """ # 将TIME_ZONE设置为国内所在的时区 TIME_ZONE = 'Asia/Shanghai' # 默认情况,INSTALLED_APPS中会自动包含下列条目,它们都是Django自动生成的: INSTALLED_APPS = [ 'django.contrib.admin', # admin管理后台站点 'django.contrib.auth', # 身份认证系统 'django.contrib.contenttypes', # 内容类型框架 'django.contrib.sessions', # 会话框架 'django.contrib.messages', # 消息框架 'django.contrib.staticfiles', # 静态文件管理框架 ] """ 上面的一些应用也需要建立一些数据库表,所以在使用它们之前我们要在数据库中创建这些表。使用下面的命令创建数据表: $ python manage.py migrate。 migrate命令将遍历INSTALLED_APPS设置中的所有项目,在数据库中创建对应的表,并打印出每一条动作信息。如果你感兴趣,可以在你的数据库命令行下输入:\dt (PostgreSQL)、 SHOW TABLES;(MySQL)或 .schema(SQLite) 来列出 Django 所创建的表。 提示:对于极简主义者,你完全可以在INSTALLED_APPS内注释掉任何或者全部的Django提供的通用应用。这样,migrate也不会再创建对应的数据表。 """
项目的主urls文件
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^appName/', include('appName.urls')),
url(r'^admin/', admin.site.urls),
]
说明:
1、include语法相当于多级路由,它把接收到的url地址去除前面的正则表达式,将剩下的字符串传递给下一级路由进行判断。include的背后是一种即插即用的思想。项目根路由不关心具体app的路由策略,只管往指定的二级路由转发,实现了应用解耦。app所属的二级路由可以根据自己的需要随意编写,不会和其它的app路由发生冲突。app目录可以放置在任何位置,而不用修改路由。这是软件设计里很常见的一种模式。
2、在实际环境中,为了站点的安全性,我们不能将管理后台的url随便暴露给他人,不能用/admin/
这么简单的路径。修改其中admin.site.urls对应的正则表达式,换成你想要的
app应用与project项目的区别:
- 一个app实现某个功能,比如博客、公共档案数据库或者简单的投票系统;
- 一个project是配置文件和多个app的集合,这些app组合成整个站点;
- 一个project可以包含多个app;
- 一个app可以属于多个project!
创建App:进入项目根目录,python manage.py startapp appName
注意:要将应用添加到项目中,需要在projectName/settings.py中的INSTALLED_APPS
设置中增加指向该应用的配置文件的链接。直接添加"appName"即可
App的urls.py文件
from django.conf.urls import url from . import views
app_name = 'appName' # URLconf的命名空间
urlpatterns = [
url(r'^$', views.index, name='index'),
# ex: /polls/
url(r'^$', views.index, name='index'),
# ex: /polls/5/
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
# ex: /polls/5/results/
url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
# ex: /polls/5/vote/
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
模型:
1、根据现有数据库创建models:python manage.py inspectdb > appName/models.py
2、手动创建
Django通过自定义Python类的形式来定义具体的模型,每个模型的物理存在方式就是一个Python的类Class,每个模型代表数据库中的一张表,每个类的实例代表数据表中的一行数据,类中的每个变量代表数据表中的一列字段。Django通过模型,将Python代码和数据库操作结合起来,实现对SQL查询语言的封装。也就是说,你可以不会管理数据库,可以不会SQL语言,你同样能通过Python的代码进行数据库的操作。Django通过ORM对数据库进行操作,奉行代码优先的理念,将Python程序员和数据库管理员进行分工解耦。
import datetime from django.db import models from django.utils import timezone class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') question = models.ForeignKey(Question, on_delete=models.CASCADE) votes = models.IntegerField(default=0) def __str__(self): # 在python2版本中使用的是__unique__ return self.question_text def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
保存模型变更:python manage.py makemigrations appName
通过运行makemigrations
命令,相当于告诉Django你对模型有改动,并且你想把这些改动保存为一个“迁移(migration)”。
migrations
是Django保存模型修改记录的文件,这些文件保存在磁盘上。在例子中,它就是appName/migrations/0001_initial.py
,你可以打开它看看,里面保存的都是人类可读并且可编辑的内容,方便你随时手动修改。
接下来有一个叫做migrate
的命令将对数据库执行真正的迁移动作。但是在此之前,让我们先看看在migration的时候实际执行的SQL语句是什么。有一个叫做sqlmigrate
的命令可以展示SQL语句,例如:
$ python manage.py sqlmigrate appName 0001
请注意:
- 实际的输出内容将取决于您使用的数据库会有所不同。上面的是PostgreSQL的输出。
- 表名是自动生成的,通过组合应用名 (polls) 和小写的模型名
question
和choice
。 ( 你可以重写此行为。) - 主键 (IDs) 是自动添加的。( 你也可以重写此行为。)
- 按照惯例,Django 会在外键字段名上附加 "_id" 。 (你仍然可以重写此行为。)
- 生成SQL语句时针对你所使用的数据库,会为你自动处理特定于数据库的字段,例如 auto_increment (MySQL), serial (PostgreSQL), 或integer primary key (SQLite) 。 在引用字段名时也是如此 – 比如使用双引号或单引号。
- 这些SQL命令并没有在你的数据库中实际运行,它只是在屏幕上显示出来,以便让你了解Django真正执行的是什么。
如果你感兴趣,也可以运行python manage.py check
命令,它将检查项目中的错误,并不实际进行迁移或者链接数据库的操作。
现在,我们可以运行migrate命令,在数据库中进行真正的表操作了。
$ python manage.py migrate
migrate命令对所有还未实施的迁移记录进行操作,本质上就是将你对模型的修改体现到数据库中具体的表上面。Django通过一张叫做django_migrations的表,记录并跟踪已经实施的migrate动作,通过对比获得哪些migrations尚未提交。
migrations的功能非常强大,允许你随时修改你的模型,而不需要删除或者新建你的数据库或数据表,在不丢失数据的同时,实时动态更新数据库。我们将在后面的章节对此进行深入的阐述,但是现在,只需要记住修改模型时的操作分三步:
- 在models.py中修改模型;
- 运行
python manage.py makemigrations
为改动创建迁移记录; - 运行
python manage.py migrate
,将操作同步到数据库。
之所以要将创建和实施迁移的动作分成两个命令两步走是因为你也许要通过版本控制系统(例如github,svn)提交你的项目代码,如果没有一个中间过程的保存文件(migrations),那么github如何知道以及记录、同步、实施你所进行过的模型修改动作呢?毕竟,github不和数据库直接打交道,也没法和你本地的数据库通信。但是分开之后,你只需要将你的migration文件(例如上面的0001)上传到github,它就会知道一切。
模型API
>>> from polls.models import Question, Choice # 导入我们写的模型类 # 创建一个新的question对象 # Django推荐使用timezone.now()代替python内置的datetime.datetime.now() # 这个timezone就来自于Django唯一的依赖库pytz >>> from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) # 你必须显式的调用save()方法,才能将对象保存到数据库内 >>> q.save() # 默认情况,你会自动获得一个自增的名为id的主键 >>> q.id 1 # 通过python的属性调用方式,访问模型字段的值 >>> q.question_text "What's new?" # 通过修改属性来修改字段的值,然后显式的调用save方法进行保存。 >>> q.question_text = "What's up?" >>> q.save() # objects.all() 用于查询数据库内的所有questions # 先看看__str__()的效果,直观多了吧? >>> Question.objects.all() <QuerySet [<Question: What's up?>]> # Django提供了大量的关键字参数查询API >>> Question.objects.filter(id=1) >>> Question.objects.filter(question_text__startswith='What') # 获取今年发布的问卷 >>> current_year = timezone.now().year >>> Question.objects.get(pub_date__year=current_year) # 查询一个不存在的ID,会弹出异常 >>> Question.objects.get(id=2) Traceback (most recent call last): ... DoesNotExist: Question matching query does not exist. # Django为主键查询提供了一个缩写:pk。下面的语句和Question.objects.get(id=1)效果一样. >>> q = Question.objects.get(pk=1) # 看看我们自定义的方法用起来怎么样 >>> q.was_published_recently() True # 显示所有与q对象有关系的choice集合,目前是空的,还没有任何关联对象。 >>> q.choice_set.all() # 创建3个choices. >>> q.choice_set.create(choice_text='Not much', votes=0) >>> q.choice_set.create(choice_text='The sky', votes=0) >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0) # Choice对象可通过API访问和他们关联的Question对象 >>> c.question # 同样的,Question对象也可通过API访问关联的Choice对象 >>> q.choice_set.all() >>> q.choice_set.count() # API会自动进行连表操作,通过双下划线分割关系对象。连表操作可以无限多级,一层一层的连接。 # 下面是查询所有的Choices,它所对应的Question的发布日期是今年。(重用了上面的current_year结果) >>> Choice.objects.filter(question__pub_date__year=current_year) # 使用delete方法删除对象 >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete()
admin后台管理站点
1. 创建管理员用户
首先,我们需要通过下面的命令,创建一个可以登录admin站点的用户:
$ python manage.py createsuperuser
输入用户名:
Username: admin
输入邮箱地址:
Email address: xxx@xxx.xxx
输入密码:
Password: ********** Password (again): ********* Superuser created successfully.
注意:Django1.10版本后,超级用户的密码强制要求具备一定的复杂性,不能再偷懒了。
2. 在admin中注册应用
现在还无法看到投票应用,必须先在admin中进行注册,告诉admin站点,请将appName的模型加入站点内,接受站点的管理。
打开appName/admin.py
文件,加入下面的内容:
from django.contrib import admin
from .models import Question
admin.site.register(Question)
快捷方式:render()
在实际运用中,加载模板、传递参数,返回HttpResponse对象是一整套再常用不过的操作了,为了节省力气,Django提供了一个快捷方式:render函数,一步到位!
from django.shortcuts import render from .models import Question def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'polls/index.html', context)
快捷方式:get_object_or_404()
from django.http import Http404 from django.shortcuts import render from .models import Question # ... 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})
以上代码可简写为:
from django.shortcuts import get_object_or_404, render from .models import Question # ... def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question})
同样,还有一个get_list_or_404()
方法,和上面的get_object_or_404()
类似,只不过是用来替代filter()
函数,当查询列表为空时弹出404错误。(filter是模型API中用来过滤查询结果的函数,它的结果是一个列表集。而get则是查询一个结果的方法,和filter是一个和多个的区别!)
删除模板中硬编码的URLs
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
注意引用方法是冒号而不是圆点也不是斜杠!!!!!!!!!!!!
- forloop.counter是DJango模板系统专门提供的一个变量,用来表示你当前循环的次数,一般用来给循环项目添加有序数标。
- 由于我们发送了一个POST请求,就必须考虑一个跨站请求伪造的安全问题,简称CSRF(具体含义请百度)。Django为你提供了一个简单的方法来避免这个困扰,那就是在form表单内添加一条{% csrf_token %}标签,标签名不可更改,固定格式,位置任意,只要是在form表单内。这个方法对form表单的提交方式方便好使,但如果是用ajax的方式提交数据,那么就不能用这个方法了。