这篇文章可能会有点长,因为我会写很长时间,是整个项目的记录文件
首先介绍背景:pycharm(2018.3 professional) python(3.7) mysql(8.0.11)
这都是比较新的版本,基本就是这些了
pycharm可以使用create peoject选择django直接创建,记得可以在创建的时候写上APP的名称,可以省下后面一步
创建后的目录如下:
默认会使用sqlite3的数据库,这里可以看到它的文件,接下来试运行一下,这里直接运行manage.py,如果顺利则是下面的界面
这两个界面代表成功运行了,但是我遇到过一个坑的问题,就是运行了后只是一些配置生成,不会有对应的app运行起来,这里经过一番折腾,找到了结果
这里是因为manage的运行端口没有设置,按照上面两张图点进去设置即可,一般是因为是空白的,写上runserver 127.0.0.1:5000
即可,端口不限哈
到这里第一步就完成了,我们的Django算是运行起来
django默认使用轻量级数据库sqlite3,不需要安装,所以很方便,一个.sqlite文件即可,这里我们对应实际情况,改变一下,使用了mysql的数据库来完成,所以需要配置一下,安装数据库到本地这个我这里就不写啦,百度教程安装上吧,反正都是要用的东西
首先我们找到settings文件下,修改database的参数,默认是:
我们修改为mysql的参数:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'testdb', #数据库名称
'USER': 'root', #名字
'PASSWORD': '123456', #密码
'HOST': 'localhost', #可以不写,默认的
'PORT': '3306' #可以不写,默认的
}
}
接下来我们安装Pycharm上的pymysql,这个比较简单,pip安装即可
在settings.py同目录下的__init__.py中写入下列代码:
import pymysql
pymysql.install_as_MySQLdb()
#前面两行是重要的,后面这些是测试用的,这里打印出mysql的版本,显示在程序运行界面上
db = pymysql.connect(host='localhost', user='root', password='123456', db='testdb')
cursor = db.cursor()
cursor.execute('SELECT VERSION()')
data = cursor.fetchone()
print('DATABASE VERSION IS: %s' % data)
db.close()
这个成功的话,你可以再run界面看到打印的结果
E:\PyCharm\Django\venv\Scripts\python.exe E:/PyCharm/Django/manage.py runserver 127.0.0.1:5000
DATABASE VERSION IS: 8.0.11
DATABASE VERSION IS: 8.0.11
Performing system checks...
System check identified no issues (0 silenced).
You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
June 01, 2019 - 00:23:22
Django version 1.11.20, using settings 'Django.settings'
Starting development server at http://127.0.0.1:5000/
Quit the server with CTRL-BREAK.
也可以再Python Console中看到
E:\PyCharm\Django\venv\Scripts\python.exe "E:\PyCharm\PyCharm 2018.3.5\helpers\pydev\pydevconsole.py" --mode=client --port=57432
import sys; print('Python %s on %s' % (sys.version, sys.platform))
import django; print('Django %s' % django.get_version())
sys.path.extend(['E:\\PyCharm\\Django', 'E:\\PyCharm\\PyCharm 2018.3.5\\helpers\\pycharm', 'E:\\PyCharm\\PyCharm 2018.3.5\\helpers\\pydev'])
if 'setup' in dir(django): django.setup()
import django_manage_shell; django_manage_shell.run("E:/PyCharm/Django")
PyDev console: starting.
Python 2.7.16 |Anaconda, Inc.| (default, Mar 14 2019, 15:42:17) [MSC v.1500 64 bit (AMD64)] on win32
Django 1.11.20
DATABASE VERSION IS: 8.0.11
完成数据库连接后,开始创建数据库表
首先,在models.py中写入:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
class Web(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=150)
body = models.TextField()
timestamp = models.DateTimeField()
内容自定,然后 在terminal上面执行下面两条命令:
python manage.py makemigrations #检测app/models.py文件的改动
python manage.py migrate #把上面的改动翻译成sql语句,然后去数据库中执行
这两步完成后可以去数据库查看你的表了
mysql> show tables;
+----------------------------+
| Tables_in_testdb |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
| web_web | #这就是我的表
+----------------------------+
11 rows in set (0.00 sec)
在terminal端口,使用python manage.py shell可以进入shell端,其实和python Console一样的,使用下列命令:
>>> from datetime import datetime
>>> from web.models import Web
>>> Web.objects.all()
>>> web1 = Web(title='test', body='just see', timestamp=datetime.now())
>>> web1
>>> web1.save() #这里我遇到一个问题,错误原因:和UTC(世界标准时间)有关。
解决方法:.../settings.py 文件中设置:USE_TZ = False
E:\PyCharm\Django\venv\lib\site-packages\django\db\models\fields\__init__.py:1451: RuntimeWarning: DateTimeField Web.timestamp received a naive datetime (2019-06-0
1 11:40:39.526000) while time zone support is active.
RuntimeWarning)
>>> web1.save() #这样才能保存到数据库中
>>> web1.title #一些基本查询操作
'test'
>>> web1.body
'just see'
>>> web1.timestamp
datetime.datetime(2019, 6, 1, 11, 40, 39, 526000)
>>> web1.id
1
admin是创建用于web的创建、读取、更新、删除(CRUD)的工具,是一个自动化的后台管理应用
在amin.py中,我们注册一下
from django.conf.urls import url
from django.contrib import admin
from web import models
admin.site.register(models.Web) #这一行注册后,admin就可以管理数据库中这类对象了
注册后,我们可以使用http://127.0.0.1:5000/admin
进入管理界面,如下
这里,我们需要创建一个超级用户用于登录
venv) E:\PyCharm\Django>python manage.py createsuperuser
Username (leave blank to use 'administrator'): li
Email address: [email protected]
Password:
Password (again):
Superuser created successfully.
点击webs可以进入应用中,可以看到数据中的对象,也可以创建新的对象,点击右上角的ADD WEB可以添加,如下界面,点击SAVE可以保存,数据库同时得到更新
这样会生成两个Web object,但是没法区分对吧,所以我们这时候需要优化一下,我们修改admin.py的内容,使用新的WebAdmin类,并将其注册进去:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from web import models
from django.contrib import admin
class WebAdmin(admin.ModelAdmin):
list_display = ('title', 'timestamp')
admin.site.register(models.Web, WebAdmin)
这是新的admin函数,这里定义了WebAdmin类来指定输出,这样可以看到和清楚的输出,开头为标题,后面为时间戳
点击title或者timestamp可以排序,这些功能都是很使用的
用户是不会用到后台的,所以我们需要为用户定制内容界面,首先我们可以先写个模板,用于显示用户的内容如下的模板(archive.html):
{{ post.title|title }}
{{ post.timestamp }}
{{ post.body }}
这个是django标准html输出,用花括号{{...}}
表示输出内容,里面是变量,我们这里用到了一个特殊的知识,django的过滤器,就是第一行的post.title|title
,这输入本来是post.title
,加上一个管道符号(‘|’)
,后面是title过滤函数,这样就可以是标题的首字母大写,这是个很实用的功能
接下来我们在urls.py中加入我们的新的地址:
from django.conf.urls import url, include #引入include
from django.contrib import admin
from web.models import *
admin.autodiscover()
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^test/', include('web.urls')) #增加一个地址,这里用了include函数,里面是字符串,指向我们的web的urls中
]
所以我们需要创建新的urls文件,地址为.../web/urls.py
,写入
from django.conf.urls import url
import web.views
urlpatterns = [
url('^$', web.views.archive) #正则表达无内容,直接是http://127.0.0.1:5000/test/
]
这里把函数web.views.archive写进来了,所以我们要在views中创建(view.py)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from datetime import datetime
from django.shortcuts import render_to_response
from web.models import Web
def archive(request):
post = Web(title='mocktitle', body='mockbody', timestamp=datetime.now())
return render_to_response('archive.html', {'posts': [post]}) #前面的html中要接受一个post参数的
这里展示的是一条文章,我们接下来试试展示数据库所有的内容,修改views.py
文件
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render_to_response
from web.models import Web
def archive(request):
posts = Web.objects.all() #改了这个而已
return render_to_response('archive.html', {'posts': posts})
这里添加一波测试数据:
>>> from datetime import datetime
>>> from web.models import Web
>>> for i in range(10):
... wb = Web(title='post #%d' % i,body='body for post #%d' % i, timestamp=datetime.now())
... wb.save()
添加了十个数据进去,我们测试下排序,用oreder_by,修改下views中的archive函数
def archive(request):
posts = Web.objects.all().order_by('-timestamp')[:10]
return render_to_response('archive.html', {'posts': posts})
显示时间最新发布的十个数据,这里如果我们的数据是需要长期按照这个降序排列,可以设置一个模型,在models添加一个Meta的内部类:
class Meta:
ordering = ('-timestamp',)
同时views可以修改为:
def archive(request):
posts = Web.objects.all()[:10]
return render_to_response('archive.html', {'posts': posts})
我们前面实现了我们管理者的添加数据,现在给用户一个端口可以添加数据进去,首先先来一个html页面,作为输入端(还是在原来的archive.html):
{% for post in posts %}
{{ post.title|title }}
{{ post.timestamp }}
{{ post.body }}
{% endfor %}
这一步真的费了我好多时间,先前用render_to_response,很容易出错,查了很多才得出来用render直接就简单多了,所以还是要用新的函数,旧的不好用了,直接上views的代码:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render_to_response, render
from web.models import Web
from datetime import datetime
from django.http import HttpResponseRedirect
def archive(request):
posts = Web.objects.all().order_by('-timestamp')[:10]
return render(request, 'archive.html', {'posts': posts})
def create_context(request):
if request.method == 'POST':
Web(
title=request.POST.get('title'),
body=request.POST.get('body'),
timestamp=datetime.now(),
).save()
return HttpResponseRedirect('/test/')
在urls中加入新的连接:
from django.conf.urls import url
import web.views
urlpatterns = [
url('^$', web.views.archive),
url('^create/', web.views.create_context)
]
由于没有设置提示,所以提交完了没什么提示,只是下面的第一条就会更新为你刚输入tvfd的那一条
,直接就返回到首页了,这里倒是可以用一下flask的flash功能,但是我们一般后面都是给返回一个新的界面,现在先不管吧
django的表单验证功能是很出色的,在校验提交数据方面十分好用,这样可以规范用户输入的数据,也可以校验我们得到的数据,存储也很方便了。
这里我们先定义一种最原始的表单数据,直接在models中定义:
from django import forms
...
class WebForm(forms.ModelForm):
title = forms.CharField(max_length=150)
body = forms.CharField(widget=forms.Textarea(attrs={'row': 3, 'cols': 60}))
timestamp = forms.DateTimeField()
这里body是因为需要指定需求,因为他需要三行,是指定了的
因为在models中同样存在着我们的Web表,所以看上去form和model基本就是一样的了,这样看上去就重复了一样,所以有了ModelForm
class WebForm(forms.ModelForm):
class Meta:
model = Web
exclude = ('timestamp',)
可以看到直接指定一个model就可以了,然后exclude属性是不包含的意思,就是时间不用用户输入的,移除这个表单验证,只验证其他的,我们再shell中看一下我们的form表单:
>>> from web.models import WebForm
>>> form = WebForm()
>>> form
>>> str(form)
' \n '
我们的views中同样含有很多相同的内容,比如create_context函数,现在它是这样的:
def create_context(request):
if request.method == 'POST':
Web(
title=request.POST.get('title'),
body=request.POST.get('body'),
timestamp=datetime.now(),
).save()
return HttpResponseRedirect('/test/')
我们也要让它接受form表单的数据,所以,我们改写为:
def create_context(request):
if request.method == 'POST':
form = WebForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/test/')
是不是瞬间轻量级了,但是我们这儿有个问题,因为form表单中我们排除了timestamp,所以我们必须要单独修改一下:
def create_context(request):
if request.method == 'POST':
form = WebForm(request.POST)
if form.is_valid():
post = form.save(commit=False) #因为没有时间戳,所以先不提交,只是保存
post.timestamp = datetime.now() #添加了后再提交
post.save()
return HttpResponseRedirect('/test/')
单元测试是通过python扩展的测试功能,django自动生成一个test.py文件来保证创建测试,现在我们写几个简单的测试:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from datetime import datetime
from django.test import TestCase
from web.models import Web
class WebTest(TestCase):
#测试数据生成,用到create创建一个对象,再验证数据,比较简单
def test_obj_create(self):
Web.objects.create(title='raw title', body='raw body', timestamp=datetime.now())
self.assertEqual(1, Web.objects.count())
self.assertEqual('raw title', Web.objects.get(id=1).title)
#用于测试主页,我们的主页就是这个,用的是self.client这个django的测试实例,返回的是成功的状态码200
def test_home(self):
response = self.client.get('/test/')
self.failUnlessEqual(response.status_code, 200)
#之前设计的页面没有这个页面,所以返回404
def test_slash(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 404)
#这个测试的是重定向,这个界面返回到/test/界面,所以是301,可能会出现302,所以用了assertIn函数
def test_empty_create(self):
response = self.client.get('/test/create/')
self.assertIn(response.status_code, (301, 302))
def test_post_create(self):
response = self.client.post('/test/create/', {'title': 'post title', 'body': 'post body', 'timestamp': datetime.now()})
self.assertIn(response.status_code, (301, 302))
self.assertEqual(1, Web.objects.count())
self.assertEqual('post title', Web.objects.get(body='post body').title)
最后一个测试有点问题,用的是post,提交一个实例数据进去,再测试重定向,检测数据库内容,这里有个小问题,之前最后一句写的get(id=1),不知道为什么一直报错,就是查询不到了吧,然后我写了下面这句话:
def test_post_create(self):
response = self.client.post('/test/create/', {'title': 'post title', 'body': 'post body', 'timestamp': datetime.now()})
self.assertIn(response.status_code, (301, 302))
self.assertEqual(1, Web.objects.count())
print(Web.objects.get(body='post body').id)
self.assertEqual('post title', Web.objects.get(body='post body').title)
硬核查询id:
Testing started at 12:46 ...
E:\PyCharm\Django\venv\Scripts\python.exe "E:\PyCharm\PyCharm 2018.3.5\helpers\pycharm\django_test_manage.py" test web.tests E:\PyCharm\Django
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
2
Destroying test database for alias 'default'...
Process finished with exit code 0
结果是id为2,这我就有点懵了,我猜测是之前测试第一个的时候用掉了一个id位置,所以为2,因为default测试数据库是单次测试创建的,在测试中是通用的,所以会是2,有兴趣可以多测试测试得出结论
我们多创建一个页面作为演示整个过程,这里我写一个/test/description/页面用于展示我们的所有数据库目标的body属性:
重点第一是两个urls文件,首先我们总项目下的urls是用于指定各个app的总定向的,指定的各个起始位置,我们不做修改,用户还是用/test、作为起点:
# --django.urls.py--
from django.conf.urls import url, include
from django.contrib import admin
from web.models import *
admin.autodiscover()
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^test/', include('web.urls'))
]
我们在用户里面增加下一级:
# --web.urls.py--
from django.conf.urls import url
import web.views
urlpatterns = [
url('^$', web.views.archive),
url('^create/', web.views.create_context),
url('^description/', web.views.description) #新增加的url
]
再在views.py中增加一个函数:
......
def description(request):
webs = Web.objects.all()
return render(request, 'description.html', {'webs': webs})
最后增加一个HTML模板文件:
#--description.html--
{% for web in webs %}
Body Contents
{{ web.body }}
{% endfor %}
OK了,我们可以输入地址查看一下:
好了,django基础就介绍这么多了,大家可以测试下,这都是最新的,我都是每一步成功了才写出来的
希望看到觉得有帮助的点个赞或者点个关注,谢谢啦