pycharm+python+django+mysql开发的web应用

这篇文章可能会有点长,因为我会写很长时间,是整个项目的记录文件
首先介绍背景:pycharm(2018.3 professional) python(3.7) mysql(8.0.11)
这都是比较新的版本,基本就是这些了

1、创建django项目

pycharm可以使用create peoject选择django直接创建,记得可以在创建的时候写上APP的名称,可以省下后面一步
pycharm+python+django+mysql开发的web应用_第1张图片
创建后的目录如下:
pycharm+python+django+mysql开发的web应用_第2张图片
默认会使用sqlite3的数据库,这里可以看到它的文件,接下来试运行一下,这里直接运行manage.py,如果顺利则是下面的界面
pycharm+python+django+mysql开发的web应用_第3张图片
pycharm+python+django+mysql开发的web应用_第4张图片
这两个界面代表成功运行了,但是我遇到过一个坑的问题,就是运行了后只是一些配置生成,不会有对应的app运行起来,这里经过一番折腾,找到了结果
pycharm+python+django+mysql开发的web应用_第5张图片
pycharm+python+django+mysql开发的web应用_第6张图片
这里是因为manage的运行端口没有设置,按照上面两张图点进去设置即可,一般是因为是空白的,写上runserver 127.0.0.1:5000即可,端口不限哈

到这里第一步就完成了,我们的Django算是运行起来

二、更改数据库为mysql

django默认使用轻量级数据库sqlite3,不需要安装,所以很方便,一个.sqlite文件即可,这里我们对应实际情况,改变一下,使用了mysql的数据库来完成,所以需要配置一下,安装数据库到本地这个我这里就不写啦,百度教程安装上吧,反正都是要用的东西
首先我们找到settings文件下,修改database的参数,默认是:
pycharm+python+django+mysql开发的web应用_第7张图片
我们修改为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

3、创建数据库表

完成数据库连接后,开始创建数据库表
首先,在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

4、admin管理

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进入管理界面,如下
pycharm+python+django+mysql开发的web应用_第8张图片
这里,我们需要创建一个超级用户用于登录

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.

挨着输入就行了,密码要八位数以上,登录后界面:
pycharm+python+django+mysql开发的web应用_第9张图片

点击webs可以进入应用中,可以看到数据中的对象,也可以创建新的对象,点击右上角的ADD WEB可以添加,如下界面,点击SAVE可以保存,数据库同时得到更新
pycharm+python+django+mysql开发的web应用_第10张图片
这样会生成两个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类来指定输出,这样可以看到和清楚的输出,开头为标题,后面为时间戳
pycharm+python+django+mysql开发的web应用_第11张图片
点击title或者timestamp可以排序,这些功能都是很使用的

5、创建用户界面

用户是不会用到后台的,所以我们需要为用户定制内容界面,首先我们可以先写个模板,用于显示用户的内容如下的模板(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参数的

pycharm+python+django+mysql开发的web应用_第12张图片
这里展示的是一条文章,我们接下来试试展示数据库所有的内容,修改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})

6、处理用户输入

我们前面实现了我们管理者的添加数据,现在给用户一个端口可以添加数据进去,首先先来一个html页面,作为输入端(还是在原来的archive.html):

{% csrf_token %} #没有这个是通不过检测的


{% 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功能,但是我们一般后面都是给返回一个新的界面,现在先不管吧

7、表单和模型

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/')

8、单元测试

单元测试是通过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,有兴趣可以多测试测试得出结论

9、总结一下

我们多创建一个页面作为演示整个过程,这里我写一个/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了,我们可以输入地址查看一下:
pycharm+python+django+mysql开发的web应用_第13张图片
好了,django基础就介绍这么多了,大家可以测试下,这都是最新的,我都是每一步成功了才写出来的
希望看到觉得有帮助的点个赞或者点个关注,谢谢啦

你可能感兴趣的:(个人学习记录)