Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

编程环境的安装请见我个人博客https://tianjuewudi.gitee.io/的另两篇文章《Pycharm安装方法》及《Pycharm配置和使用教程》,下面以你能正常使用pycharm为前提。

基础知识

变量及简单数据类型

变量名的使用

  1. 变量名只能包含字母,数字和下划线。可以以字母和下划线开头,但不能以数字开头。
  2. 变量名不能包含空格,可用下划线来分割单词,如:greeting_message。
  3. 不要将Python关键字和函数名用作变量名。
  4. 变量名应既简短又具有描述性。
  5. 慎用小写字母l和大写字母O,容易被看成1和0。

字符串

字符串是一串字符,引号括起的都是字符串,可以是单引号或双引号,如:

str = "ada lovelace"
str.title()
str.upper()
str.lower()
str = str1 + str2

str.rstrip()

整型(浮点型同理)

加减乘除不必多说

3 ** 2
3 % 2

注意,整型相除也是整型,会自行砍掉小数。

强制类型转换

str(age)
int(age)

列表

列表由一系列特定元素排列而成,如:

bicycles = ['trek','cannondale','redline','specialized']
list[0]
list[-1]

list.append('honda')
list.insert(0,'ducati')
del list[0]
str = list.pop()
str = list.pop(0)
list.remove('ducati')

list.sort()
list.sort(reverse = True)
sorted(list)
sorted(list,reverse = True)
list.reverse()

list[0:3]
list[:4]
list[2:]
list[-3:]

list2 = list1[:]

len(list)
for i in list:
    print(i)
range(1,5)
range(2,11,2)
list(range(1,5))
min(list_digits)
max(list_digits)
list = [value**2 for value in range(1,11)]

元组

不可修改的列表称为元组。

tuple = (200,50)
tuple[0] = 10
tuple = (100,10)

if语句


if car == 'bmw':
    print(car.upper())
elif car == 'saf':
    print(car.lower())
else:
    print(car.title())

if age_0 >= 21 and age_1 >= 21
if age_0 >= 21 or age_1 >= 21

if 'value' in list
if 'value' not in list

if list

字典

字典是一系列的键值对:

dict = {'color':'green','points':5}
dict['color']

dict['x_position'] = 0

dict['x_position'] = 10

del dict['points']

for key,value in dict.items():
    print(key/n)
    print(value)
for key in dict.keys():
    print(key);
for key in sorted(dict.keys()):
    print(key);
for value in dict.values():
    print(value)

用户输入和while循环

  • 函数input()让程序暂停运行,等待用户输入文本,获取用户输入后,Python将其储存在一个变量中,供你使用:
message = input("Tell me something,and I will repeat it back to you:")
print(message)
  • while循环不断执行,知道指定的条件不满足为止:
num = 1
while num  5:
    print(num)
    num++
    if city == 'quit':
        break
  • 在for循环中不应修改列表,否则将导致Python难以跟踪其中的元素,可使用while进行修改:
unconfirmed_users = ['alice','brian','candace']
confirmed_users = []
while unconfirmed_users:
    current_user = unconfirmed_users.pop()
    print(current_user.title())
    confirmed_users.append(current_users)
  • 删除包含特定值的所有列表元素
while 'cat' in pets:
    pets.remove('cat')

函数

函数是带名字的代码块,用于完成具体的工作。需要程序多次执行同一项任务是,无需反复编写代码,只需反复调用执行改任务的函数即可。

def greet_user():
    print('Hello!')

def greet_user(username):
    print("Hello" + username.title())
def describe_pet(animal_type = 'rabbit',pet_name = 'snoby'):
    print("I have a" + animal_type + '.The name is' + pet_name)
describe_pet('hamster','harry')
describe_pet(animal_type = 'hamster',pet_name = 'harry')

def git_fromatted_name(first_name,last_name):
    full_name = first_name + last_name
    return full_name.title()

def print_models(unprinted_designs,completed_models):
    while unprinted_designs:
        current_designs = unprinted_designs.pop()
        print('Printing model:' + current_design)
        completed_models.append(current_design)

def make_pizza(*toppings):
    print(toppings)
make_pizza('mushrooms','green peppers','extra cheese')
def make_pizza(size,*toppings):
    print("make a " + size + "pizza with the following toppings:")
    for topping in toppings:
        print("- " + topping)

def build_profile(first,last,**user_info):
    profile = {}
    profile['first_name'] = first
    profile['last_name'] = last
    for key,value in user_info.items():
        profile[key] = value
    return profile
bild_profile('albert','einstein',location = 'princeton',field = 'physics')

def make_pizza(*toppings):
    print(toppings)

import pizza
pizza.make_pizza('mushroom','green peppers','extra cheese')

from pizza import make_pizza as mp

from pizza import *

面向对象编程是最有效的软件编写方法之一。在面向对象编程是,你编写表示现实世界中的事物和场景的类,基于这些类来创造对象,每个对象都具备类的通用行为,也可根据需要赋予每个对象独特的个性。


class Dog():
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.model = 0
    def sit(self):
        print(self.name.title() + 'is now sitting.')
    def roll_over(self):
        print(self.name.title() + 'rolled over!')
    def update_model(self,model):
        self.model = model

my_dog = Dog('willie',6)
my_dog.name
my_dog.age
my_dog.sit()

my_dog.model = 10
my_dog.update_model(10)

类的继承

编写类是,并非总是从空白开始。一个类继承另一个类时,自动获得另一个类所有的属性和方法,原有的类称为父类,新类称为子类,子类也可定义属于自己的属性和方法。


class ElectricCar(Car):
    def __init__(self,make,model,year):
        super()._init_(make,model,year)

class Battery():
    --skip
class ElectricCar(car):
    def _init_(self,make,model,year):
        super()._init_(make,model,year)
        self.battery = Battery();

导入类

Python允许你将类储存在模块中,然后在主程序中导入所需的模块:


Class Car():
    --skip
Class Smartphone():
    --skip

from car import Car
my_new_car = Car('audi','a4',2016)

文件和异常

自此,你已掌握了编写组织有序易于使用的程序所需的基本技巧,为了让程序用途更广,本章将学习处理文件,让程序快速分析大量数据;处理异常,用于管理程序运行时出现的错误,还将学习模块json,保存用户数据,以免程序停止运行后丢失。本章的学习可提高程序的实用性,可用性,稳定性。

从文件中读取数据

假设已创建了一个文件pi_digits.txt


with open('pi_digits.txt') as file_object:
    contents = file_object.read()
    print(contents.rstrip())

with open(filename) as file_object:
    lines = file_object.readlines()
    for line in file_object:
        print(line)

写入文件


with open(filename,'w') as file_object:
    file_object.write("I love programming.")

with open(filename,'a') as file_object:
    file_object.write("I love programming.")

异常

发生程序异常时未对异常进行处理,程序会停止。使用try-except代码块时,即使出现异常,也会继续运行,显示你编写的友好的错误信息而不是traceback。

try:
    print(5/0)
except ZeroDivisionError:
    print('You can\'t divide by zero!')
else:
    print("answer")

try:
    with open(filename) as f_obj:
        contents = f_obj.read()
except FileNotFoundError:
    msg = "Sorry,the file" + filename + "does not exist."
    print(msg)

分析文本

str.split()

def count_words(filename):
    try:
        with open(filename) as f_obj:
            contents = f_obj.read()
    except FileNotFoundError:
        print('Sorry,the file does not exist')
    else:
        word = contents.split()
        num_words = len(words)
        print("The file " + filename + "has about " + str(num_words) + " words.")

存储数据

程序需要把用户提供的数据存储在列表和字典等数据结构中,用户关闭程序时,需要保存他们提供的信息,一种简单的办法是用模块json来储存信息

json.dump()和json.load()

import json
numbers = [2,3,5,7,11,13]
filename = 'numbers.json'
with open(filename,'w') as f_obj:
    json.dump(numbers,f_obj)

with open(filename) as f_obj:
    numbers = json.load(f_obj)

测试代码

每个程序员都需要经常测试其代码,在用户发现问题前找到它,因此需要编写测试代码改进代码。

Python标准库中的模块unittest提供了测试工具,单元测试用于核实函数的某个方面没有问题。

import unittest
from name_function import get_formatted_name

class NamesTestCase(unittest.TestCase):
    def test_first_last_name(self):
        formatted_name = get_formatted_name('janis','joplin')
        self.assertEqual(formatted_name,'Janis Joplin')
unittest.main()

方法用途assertEqual(a,b)核实a == bassertNotEqual(a,b)核实a != bassertTrue(x)核实x为TrueassertFalse(x)核实x为FalseassertIn(item,list)核实item在list中assertNotIn(item,list)核实item不在list中

类的测试和函数的测试类似:


from survey import AnonymousSurvey
import unittest
class TestAnonmyousSurvey(unittest.TestCase):
    def test_store_single_response(self):
        question = 'what language did you first learn to speak?'
        my_survey = AnonymousSurvey(question)
        my_survey.store_response('English')
        my_survey.store_response('Chinese')
        self.assertIn('English',my_survey.responses)
unittest.main()

方法setUp(),TestCase类包含方法,对测试函数初始化

from survey import AnonymousSurvey
import unittest
class TestAnonmyousSurvey(unittest.TestCase):
    def setUp(self):
        question = 'what language did you first learn to speak?'
        my_survey = AnonymousSurvey(question)
        self.responses = ['English','Spanish','Mandarin']
    def test_store_single_response(self):
        my_survey.store_response(self.responses[0])
        self.assertIn(self.responses[0],my_survey.responses)
    def test_store_three_responses(self):
        for response in self.responses:
            self.my_survey.store_reponse(response)
        for response in self.responses:
            self.assertIn(response,self.my_survey.responses)
unittest.main()

项目

数据可视化

数据可视化是通过可视化来表示探索数据,与数据挖掘紧密相关,在基因研究,天气研究,政治经济分析等众多领域,大家都使用Python完成数据密集型工作,数据科学家编写了一系列令人印象深刻的可视化和分析工具,最流行的是matplotlib,它是一个数学绘图库,可以制作简单的图表。我们还将使用Pygal包,它专注于生成适合在数字设备上显示的图表。

pycharm安装matplotlib十分方便快捷,直接在File→setting→Project interpreter中一键导入即可,不再赘述。

绘制简单曲线图


import matplotlib.pyplot as plt
squares = [1,4,9,16,25]
plt.plot(squres)
plt.show()

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

import matplotlib.pyplot as plt

input_values = [1,2,3,4,5]
squares = [1,4,9,16,25]
plt.plot(input_values,squares,linewidth =  5)
plt.title("Square Numbers",fontsize = 24)
plt.xlabel("Value",fontsize = 14)
plt.ylabel("Square of Value",fontsize = 14)
plt.tick_params(axis = 'both',labelsize = 14)
plt.show()

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

绘制散点图

import matplotlib.pyplot as plt
x_values = [1,2,3,4,5]
y_values = [1,4,9,16,25]

plt.scatter(x_values,y_values,s = 100,edgecolor = 'none',c = 'red')

plt.title("Square Numbers",fontsize = 24)
plt.xlabel("Value",fontsize = 14)
plt.ylabel("Square of Value",fontsize = 14)
plt.tick_params(axis = 'both',labelsize = 14)
plt.show()

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

保存图表


plt.savefig('squares_plot.png',bbox_inches = 'tight')

随机漫步

from random import choice
import matplotlib.pyplot as plt

class RandomWalk():
    def __init__(self,num_points = 5000):
        self.num_points = num_points
        self.x_values = [0]
        self.y_values = [0]
    def fill_walk(self):
        while len(self.x_values) < self.num_points:
            x_direction = choice([1,-1])
            x_distance = choice([0,1,2,3,4])
            x_step = x_direction * x_distance
            y_direction = choice([1, -1])
            y_distance = choice([0, 1, 2, 3, 4])
            y_step = y_direction * y_distance
            if x_step == 0 and y_step == 0:
                continue
            next_x = self.x_values[-1] + x_step
            next_y = self.y_values[-1] + y_step

            self.x_values.append(next_x)
            self.y_values.append(next_y)

rw = RandomWalk()
rw.fill_walk()
point_numbers = list(range(rw.num_points))
plt.scatter(0,0,s = 100,c = 'green',edgecolor = 'none')
plt.scatter(rw.x_values[-1],rw.y_values[-1],s = 100,c = 'red',edgecolor = 'none')
plt.scatter(rw.x_values,rw.y_values,s = 15,c = point_numbers,
            cmap=plt.cm.Blues,edgecolor = 'none')

plt.show()

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

使用Pygal模拟掷骰子(柱状图)

from random import randint
import pygal

class Die():
    def __init__(self,numsides = 6):
        self.num_sides = numsides
    def roll(self):
        return  randint(1,self.num_sides)

die = Die()
results = []
for roll_num in range(10000):
    result = die.roll()
    results.append(result)

frequencies = []
for value in range(1,die.num_sides+1):
    frequency = results.count(value)
    frequencies.append(frequency)
print(frequencies)

hist = pygal.Bar()
hist.title = 'Results of rolling one D6 1000 times.'
hist.x_labels = ['1','2','3','4','5','6']
hist.x_title = "Result"
hist.y_title = "Frequency of Result"
hist.add('D6',frequencies)
hist.render_to_file('die_visual.svg')

{% asset_img 5.png %}

从网上下载数据并处理和绘图

本章中,你将从网上下载数据,并对数据进行可视化。我们将使用Python模块csv来处理以CSV(逗号分隔的值)格式存储的天气数据,使用模块json来访问以JSON存储的人口数据。

绘制天气情况图表

import csv
from matplotlib import pyplot as plt
from datetime import datetime

filename = 'death_valley_2014.csv'
with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    dates,highs,lows = [],[],[]

    for row in reader:
        try:
            current_date = datetime.strptime(row[0],"%Y-%m-%d")
            high = int(row[1])
            low = int(row[3])
        except ValueError:
            print(current_date,'missing data')
        else:
            dates.append(current_date)
            highs.append(high)
            lows.append(low)

fig = plt.figure(dpi = 128,figsize=(10,6))
plt.plot(dates,highs,c='red',alpha=0.5)
plt.plot(dates,lows,c='blue',alpha=0.5)
plt.fill_between(dates,highs,lows,facecolor = 'blue',alpha=0.1)
fig.autofmt_xdate()
plt.title('Daily high and low temperatures - 2014',fontsize=24)
plt.xlabel('',fontsize=16)
plt.ylabel('Temperature(F)',fontsize=16)
plt.tick_params(axis='both',which = 'major',labelsize = 16)
plt.show()

datetime.strptime方法第一个参数是传入的字符串,第二个参数规定字符串的格式,下表列出了一些这样的实参:

实参含义%A星期的名称,如Monday%B月份名称,如January%m用数字表示的月份(01-12)%d用数字表示的月份中的一天(01-31)%Y四位的年份,如2021%y两位的年份,如21%H24小时的小时数(00-23)%I12小时的小时数(01-12)%pam或pm%M分钟数(00-59)%S秒数(00-61)

制作世界人口地图

JSON文件其实是一个很长的列表,每个元素都是字典。

书本提供的链接已经不能下载数据,但感谢CSDN的兄弟,让我下载到了这份数据,链接附上:

https://pan.baidu.com/s/1FlwB2SQzn_z06SR3eM9mJg,提取码:q6vy

注意:原来的pygal.i18n的包已经弃用,改为pygal.maps.world,请自行下载pygal_maps_world模块。

代码如下:

import json
from pygal.maps.world import COUNTRIES
import pygal
from pygal.style import RotateStyle as RS,LightColorizedStyle as LCS

def get_country_code(country_name):
    for code,name in COUNTRIES.items():
        if name == country_name:
            return code
    return None

filename = "population_data.json"
with open(filename) as f:
    pop_data = json.load(f)

cc_populations = {}
for pop_dict in pop_data:
    if pop_dict['Year'] == '2010':
        country_name = pop_dict['Country Name']
        population = int(float(pop_dict['Value']))
        code = get_country_code(country_name)
        if code:
            cc_populations[code] = population
cc_pops_1,cc_pops_2,cc_pops_3 = {},{},{}
for cc,pop in cc_populations.items():
    if pop < 10000000:
        cc_pops_1[cc] = pop
    elif pop < 1000000000:
        cc_pops_2[cc] = pop
    else:
        cc_pops_3[cc] = pop

wm_style = RS('#336699',base_style=LCS)
wm = pygal.maps.world.World(style = wm_style)
wm.title = ('World Population in 2010, by Country')
wm.add('0-10m',cc_pops_1)
wm.add('10m-1bn',cc_pops_2)
wm.add('>1bn',cc_pops_3)

wm.render_to_file('americas.svg')

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

使用API

在本章,程序将使用Web应用编程接口(API)自动请求网站的特定信息而不是整个网站,再对信息进行可视化,这样信息是最新的。

使用API调用请求数据,在浏览器地址栏输入:

https://api.github.com/search/repositories?q=language:python&sort=stars

这个调用返回GitHub当前托管了多少个项目,还有最受欢迎的Python仓库的信息。

import requests

url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
r = requests.get(url)
print('Status code:',r.status_code)

response_dict = r.json()
print(response_dict.keys())
print("Total repositories:",response_dict['total_count'])

repo_dicts = response_dict['items']
print("Repositories returned:",len(repo_dicts))

print("\nSelected information about each repository:")
for repo_dict in repo_dicts:
    print("\nName:",repo_dict['name'])
    print("Owner:", repo_dict['owner']['login'])
    print('Stars:',repo_dict['stargazers_count'])
    print('Repository:',repo_dict['html_url'])
    print('Description:',repo_dict['description'])

监视API速率限制,在浏览器输入:https://api.github.com/rate_limit,内容如下:

{"resources":
    {"core":
        {"limit":60,"remaining":60,"reset":1610899050,"used":0},
        "graphql":{"limit":0,"remaining":0,"reset":1610899050,"used":0},
        "integration_manifest":{"limit":5000,"remaining":5000,"reset":1610899050,"used":0},
        "search":{"limit":10,"remaining":10,"reset":1610895510,"used":0}
    },
    "rate":{"limit":60,"remaining":60,"reset":1610899050,"used":0}
}

我们关心的是搜索API的速率限制,可知极限为每分钟10个请求,用完配额后会受到一条简单的响应,必须等待配额重置。很多API都要求你注册获得API秘钥后才能执行API调用。

使用API可视化仓库:

import requests
import pygal
from pygal.style import LightColorizedStyle as LCS,LightenStyle as LS

url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
r = requests.get(url)
print('Status code:',r.status_code)

response_dict = r.json()
print(response_dict.keys())
print("Total repositories:",response_dict['total_count'])
repo_dicts = response_dict['items']

names,plot_dicts,stars = [],[],[]
for repo_dict in repo_dicts:
    names.append(repo_dict['name'])
    plot_dict = {
        'value': repo_dict['stargazers_count'],
        'label': str(repo_dict['description']),
        'xlink': repo_dict['html_url'],
        }
    plot_dicts.append(plot_dict)
    stars.append(repo_dict['stargazers_count'])

my_style = LS('#333366',base_style=LCS)
my_config = pygal.Config()
my_config.x_label_rotation = 45
my_config.show_legend = False
my_config.title_font_size = 24
my_config.label_font_size = 14
my_config.major_label_font_size = 18
my_config.truncate_label = 15
my_config.show_y_guides = False
my_config.width = 1000
chart = pygal.Bar(my_config,style = my_style)

chart.title = "Most_Starred Python Projects on Github"
chart.x_labels = names
chart.add('',plot_dicts)
chart.render_to_file('python_repos.svg')

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

探索如何使用其他网站的API调用,可以自己去查API接口,这部分内容我会在以后更新放在其他博文里。

Django入门

建立项目

Python提供了一组开发Web开发的卓越工具,这时一个Web框架,一套用于帮助开发交互式网站的工具。

建立虚拟环境,新建一个目录,将终端切换到这个目录,使用如下命令建立虚拟环境:

python -m venv 11_env

建立虚拟环境后,需要使用如下命令 激活它:

source 11_env/Scripts/activate

环境处于激活状态时环境名包含在括号里,这时可以在环境中安装包,使用已安装的包,在11_env中安装的包在环境处于活动状态时才能使用。

如果要 停止虚拟环境,执行命令:

deactivate

安装Django:

pip3 install Django

在Django中创建项目:

django-admin.py startproject learning_log

这时在根目录下就会出现learning_log文件夹,内含几个.py文件。manage.py接受命令交给Django的相关部分执行,管理诸如使用数据库和运行服务器等任务。文件settings.py指定Django如何与你的系统交互,如何管理项目。urls.py告诉Django应该创建哪些网页来响应浏览器的请求。文件wsgi.py帮助Django提供它创建的文件。

创建数据库:

Django将大部分与项目相关的信息都储存在数据库中,因此我们需要创建一个供Django使用的数据库,活跃状态下进入manage.py的目录执行如下命令:

python manage.py migrate

我们将修改数据库成为迁移数据库,在使用SQLite的新项目中首次执行这个命令时,Django将新建一个数据库。在这里Django创建必要的数据库表,用于储存我们将在这个项目中使用的信息,确保数据库结构与当前代码匹配。

核实Django是否正确创建了项目:

python manage.py runserver

Django启动服务器,让你能够查看系统中的项目,当你在浏览器中输入URL请求网页时,Django服务器将进行响应。打开浏览器输入:http://localhost:8000/即可查看网页,控制台按Ctrl+C可以关闭服务器。

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

创建应用程序

在激活状态下,切换到manage.py的目录下执行命令:

python manage.py startapp learning_logs

Django创建程序应用learning_logs,项目文件新增一个文件夹learning_logs,里面有一些.py文件,其中models.py定义我们要在应用程序中管理的数据。

models.py:

from django.db import models

class Topic(models.Model):
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.text

然后打开learning_log目录下的setting.py,把修改一段代码:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'learning_logs',
]

接下来需要Django修改数据库,使其能够储存于模型Topic相关的信息,在终端执行:

python manage.py makemigrations learning_logs

命令makemigrations让Django确定如何修改数据库,使其储存与我们定义的新模型相关的数据,Django创建了一个名为0001——initial.py的迁移文件,这个文件在数据库中为模型Topic创建一个表。

应用这种迁移:

python manage.py migrate

当需要修改数据时,都需要采取如下三个步骤:修改models.py,对learning_logs调用makemigrations,让Django迁移项目。

Django管理网站:

为应用程序定义模型时,Django提供的管理网站(admin site)让你能轻松处理模型。

Django允许创建具备所有权限的用户——超级用户,命令如下(要在cmd执行否则不成功):

python manage.py createsuperuser

向管理网站注册模型:

Django自动在网站中添加了一些模型,如User和Group,但对于我们自己创建的模型,必须进行手工注册。

models.py所在的目录中有admin.py文件,为向管理网站注册Topic,输入下面代码:

from django.contrib import admin
from learning_logs.models import Topic

admin.site.register(Topic)

访问http://localhost:8000/admin/,输入用户名和密码,可以进入超级用户账户访问管理网站,可以让你添加修改用户和用户组,管理刚才定义的模型Topic相关的数据。

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

添加主题

单击Topics进入主题网页,单击Add,看到一个用于添加新主题的菜单,输入Chess单击Save。在Add一个Rock Climbing。这样就有两个主题了。

定义模型Entry:

要记录学到的国际象棋和攀岩知识,需要为用户可在学习笔记中添加的条目定义模型,每个条目都与特定主题相关联,这种关系被称为多对一关系,即多个条目可关联到同一个主题:

models.py

from django.db import models

class Topic(models.Model):
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.text

class Entry(models.Model):

    topic = models.ForeignKey(Topic,on_delete=models.CASCADE)
    text = models.TextField()
    date_added = models.DateTimeField(auto_now_add=True)
    class Meta:
        verbose_name_plural = 'entries'
    def __str__(self):
        return self.text[:50] + "..."

修改完models.py后记得迁移模型:

python manage.py makemigrations learning_logs
python manage.py migrate

这时会生成一个新的迁移文件0002——entry.py,使数据库能储存于模型Entry相关的信息。

然后向管理网站注册Entry:

admin.py:

from django.contrib import admin
from .models import Topic,Entry

admin.site.register(Topic)
admin.site.register(Entry)

这时登录超级用户管理网站,就可以在主题下添加条目了:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

输入一些数据后,就可以通过交互式终端会话以编程方式查看这些数据了,这种交互式环境被称为Django Shell,是测试项目和排除故障的理想之地。下面是交互式Shell示例:

$ python manage.py shell
>>> from learning_logs.models import Topic
>>> Topic.objects.all()
<queryset [<topic: chess>, <topic: rock climbing>]>
>>> topics = Topic.objects.all()
>>> for topic in topics:
...     print(topic.id,topic)
...

1 Chess
2 Rock Climbing
</topic:></queryset>

知道对象的ID后,就可获取该对象并查看其任何属性,其中内容展示由于中文原因造成乱码:

>>> t = Topic.objects.get(id=1)
>>> t.text
'Chess'
>>> t.date_added
datetime.datetime(2021, 1, 18, 15, 29, 47, 62586, tzinfo=<utc>)
>>> t.entry_set.all()
<queryset [<entry: ▒й▒▒▒▒▒▒▒▒▒▒▒▒˫▒▒▒▒ִ16▒ӣ▒˫▒▒▒▒˫▒▒˫▒ڣ▒˫▒▒˫ʿ▒▒▒▒▒䣬һ▒▒▒▒▒▒▒▒▒▒▒▒90▒▒λ▒ÿɹ▒▒▒▒▒...>]>
</queryset></utc>

退出shell会话输入exit()按回车即可。

创建网页:学习笔记主页

使用Django创建网页的过程分为三个阶段:定义URL、编写视图、编写模板。

首先必须定义URL模式,URL模式描述了URL是如何设计的,让Django知道如何将浏览器请求与网站URL匹配,以确定返回哪个网页。

每个URL都被映射到特定的视图——视图函数获取并处理网页所需的信息。视图函数通常调用一个模板,后者生成浏览器能够理解的网页。

映射URL:

用户通过在浏览器中点击链接或输入URL来请求网页,因此我们需要确定项目需要哪些URL。主页的URL最重要,是用户访问项目的基础URLhttp://localhost:8000/。我们将这个基础URL映射到”学习笔记”的主页。

在这里书本上的代码是Django1.0时期的,而后来新出的Django2.0使代码有了很大变化,此处是书本的大坑。此处请参照下面代码:

urls.py:


from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('learning_logs.urls', namespace='learning_logs')),
]

此时我们需要在文件夹learning_logs中创建另一个urls.py文件,代码修改如下:

urls.py:

from django.urls import path
from . import views

app_name='learning_logs'

urlpatterns = [
    path('', views.index, name='index'),
   ]

视图函数接受请求中的信息,准备好生成网页所需要的数据,再将这些数据发送给浏览器,这通常定义了网页是什么样的模板实现的。

views.py:

from django.shortcuts import render

def index(request):
    return render(request,'learning_logs/index.html')

URL请求与我们刚才定义的模式匹配时,Django将在views.py文件中查找函数,再将请求对象传递给这个视图函数,在这里我们不需要处理任何数据。

编写模板:

模板定义了网页的结构,指定了网页是什么样的,每当网页被请求时,Django将填入相关的数据,模板能让你访问视图提供的任何数据,我们的主页视图没有提供任何的数据,因此相应的模板非常简单。

在文件夹learning_logs中新建一个文件夹,命名templates,在里面新建一个文件夹名为learning_logs,在learning_logs中新建文件命名index.html。

index.html:

<p>learning Logp>

<p>Learning Log helps you keep track of your learning.for any topic you're learning about.p>

标识段落。这里定义两个段落,第一个充当标题,第二个阐述内容。

此时运行服务器,进入首页,看到的不是默认的Django网页,而是调用函数view.index()后使用index.html模板来渲染的网页。

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

创建网页的过程看起来很复杂,但将URL、视图、模板分离的效果实际上很好,这让我们能够分别考虑到项目的不同方面,在项目很大时,每个参与者可专注于其擅长的方面。数据库专家专注于模型,程序员专注于视图代码,Web设计人员专注于模板。

创建其他网页

我们将创建两个显示数据的网页,一个列出所有主题,一个显示特定主题下的所有条目,每个网页都将指定URL模式,编写一个视图函数,并编写一个模板,但这样做之前,要先创建一个父模板,项目中的其他模板都继承它。

模板继承:

在创建网站时,有一些所有网页都包含的元素,可编写一个包含通用元素的模板,让所有网页继承这个模板。

首先创建一个名为base.html的模板,储存在index.html所在的目录中,所有页面都包含顶端的标题,这个标题设置为到主页的链接:

base.html:

<p>
    <a href="{% url 'learning_logs:index' %}">learning Loga>
p>

{% block content %}{% endblock content %}

第一部分创建了包含项目名的段落,该段落也是一个到主页的链接。为创建链接,我们使用了一个模板标签,它生成了一个URL,该URL与learning_logs/urls.py中定义的名为index的URL模式匹配。此时,learning_logs是一个命名空间,index是该命名空间中一个名称独特的URL模式,在简单的HTML页面中,链接是使用锚标签定义的:

<a href="link_url">link texta>

使用模板标签生成URL可让链接保持最新容易得多,修改项目中的URL只需修改urls.py的URL模式。

第二部分插入了一对块标签,名为content,是一个占位符,其中的内容由子模板指定。在父模板中,可使用任意多的块来预留空间,而子模板可根据需要定义相应数量的块。

重新编写index.html:

{% extends "learning_logs/base.html" %}

{% block content %}
    <p>Learning Log helps you keep track of your learning.for any topic you're learning about.p>
{% endblock content %}

与原来的代码相比,我们将标题替换为了从父模板继承的代码。在子模板中只需包含当前网页特有的内容,简化了模板,让网站修改容易得多。

显示所有主题的页面:

首先定义显示所有主题页面的URL,我们将使用单词topics,因此http://localhost:8000/topics/将返回这个页面, learning_logs/urls.py修改如下:

from django.urls import path
from . import views

app_name='learning_logs'

urlpatterns = [
    path('', views.index, name='index'),
    path('topics/', views.topics, name='topics'),
   ]

views.py:

from django.shortcuts import render
from .models import Topic

def index(request):
    return render(request,'learning_logs/index.html')

def topics(request):

    topics = Topic.objects.order_by('date_added')
    context = {'topics':topics}
    return render(request,'learning_logs/topics.html',context)

同样在index.html所在目录中,创建topics.html:

{% extends "learning_logs/base.html" %}
{% block content %}
    <p>Topicsp>
    <ul>
        {% for topic in topics %}
        <li>{{ topic }}li>
        {% empty %}
        <li>No topics have been added yet.li>
        {% endfor %}
    ul>
{% endblock content %}

在content块中,首先显示一个主题名称的文本。然后使用了一个for循环的模板标签,遍历字典context中的列表topics,这里for循环必须使用 endfor 标签来显式指出其结束位置。在循环中,需要将变量名用双花括号括起来,告诉Django使用了一个模板变量,这样{{ topic }}每次循环都会被替换成topic当前值。在标签对的内部,
* 与之间的内容都是一个项目列表项。 empty 标签告诉Django列表topics为空怎么办。

此时修改父模板,将其包含到显示所有主题的页面的链接:

base.html:

<p>
    <a href="{% url 'learning_logs:index' %}">learning Loga> -
    <a href="{% url 'learning_logs:topics' %}">Topicsa>
p>

{% block content %}{% endblock content %}

主页链接后面添加了一个连字符。后面新加一行让Django生成一个链接,与learning_logs/urls.py中名为topics的URL模式匹配。

现在可以在浏览器看到效果了:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

显示特定主题的页面

显示特定主题的URL模式与前面所有的URL模式有所不同,它将使用主题的id属性来指出请求的是哪个主题,例如用户查看主题Chess(id为1)的详细页面,URL将为http://localhost:8000/topics/1/。

urls.py:

from django.urls import path,re_path
from . import views

app_name='learning_logs'

urlpatterns = [
    path('', views.index, name='index'),
    path('topics/', views.topics, name='topics'),
    re_path('topics/(?P\d+)/', views.topic, name='topic'),

   ]

这里采用了正则表达式?P

views.py:

from django.shortcuts import render
from .models import Topic

def index(request):
    return render(request,'learning_logs/index.html')

def topics(request):

    topics = Topic.objects.order_by('date_added')

    context = {'topics':topics}
    return render(request,'learning_logs/topics.html',context)

def topic(request,topic_id):

    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {"topic":topic,'entries':entries}
    return render(request,'learning_logs/topic.html',context)

topic.html:

{% extends "learning_logs/base.html" %}

{% block content %}
<p>Topic:{{ topic }}p>
<ul>
    {% for entry in entries %}
        <li>
            <p>{{ entry.date_added|date:'M d, Y H:i }}p>
            <p>{{ entry.text|linebreaks }}p>
        li>
    {% empty %}
        <li>
         There are no entries for this topic yet.

        li>
    {% endfor %}
ul>
{% endblock content %}

首先使用了包含在字典context里的topic作为主题。然后遍历entries条目,显示出属性date_added的值,竖线|表示模板过滤器,对模板变量的值修改的函数,过滤器date:’M d, Y H:i以这样的格式显示时间戳:January 1,2021 23:00。接下来的一行享受text的完整值,过滤器linebreaks将包含换行符的长条目转换为浏览器能够理解的格式,以免显示一个不间断的文本块。

将所有主题页面中的每个主题都设置为链接:

topics.html:

{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topicsp>
<ul>
    {% for topic in topics %}
    <li>
        <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}a>
    li>
    {% empty %}
    <li>No topics have been added yet.li>
    {% endfor %}
ul>
{% endblock content %}

再次访问网页发现功能已经实现:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

用户账户

Web应用程序的核心是让任何用户都能够注册账户并能够使用它。本章中,你将创建一些表单让用户能够添加主题和条目,以及编辑现有条目。你还将学习Django如何防范对基于表单的网页发起的常见攻击。然后,我们将实现一个用户身份验证系统,创建一个注册页面,供用户注册,并让有些页面只让已登录的用户访问。接下来,修改一些视图参数,使用户只能看到自己的数据。

让用户能输入数据

首先让用户能添加新主题,与前面的方法几乎一样,主要差别是需要导入包含表单的模块form.py。

用户输入并提交的信息都是表单,我们需要验证提供的信息是正确的数据类型且不是恶意信息,再对有效的信息进行处理。创建表单最简单的方式是使用ModelForm,它根据我们之前定义的模型中的信息自动创建表单。接下来在models.py的目录下创建forms.py文件:

form.py:

from django import forms
from .models import Topic

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ['text']
        labels = {'text':''}

URL模式new_topic:

当用户要添加新主题时,我们将切换到http://localhost:8000/new_topic/。

learning_logs/urls.py:

--snip--
urlpatterns = [
    --snip--
    path('new_topic/', views.new_topic, name='new_topic'),
   ]

views.py:

from django.shortcuts import render
from .models import Topic
from django.http import HttpResponseRedirect
from django.urls import reverse
from .forms import TopicForm

--snip--
def new_topic(request):
    if request.method != 'POST':
        form = TopicForm()
    else:
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form': form}
    return render(request,'learning_logs/new_topic.html',context)

创建Web应用程序时,用到的两种主要请求类型是GET请求和POST请求。只是从服务器读取数据的页面使用GET请求,用户需要通过表单提交信息使用POST请求,处理所有表单时,我们都将使用POST方法。

新建new_topic.html:

{% extends "learning_logs/base.html" %}

{% block content %}
<p>Add a new topic:p>

<form action="{% url 'learning_logs:new_topic' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">add topicbutton>
form>
{% endblock content %}

从form标签哪一行开始,定义了一个HTML表单,实参action告诉服务器提交的表单数据发送到视图函数new_topic(),以POST请求的方式。 csrf_token 标签防止攻击者利用表单获得对服务器未经授权的访问。{{ form.as_p }}显示表单,自动创建表单需要的全部字段,修饰符as_p以段落格式渲染所有表单元素。下边设置一个提交按钮。

topics.html添加一个到new_topic的链接:

{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topicsp>
<ul>
   --snip--
ul>

<a href="{% url 'learning_logs:new_topic' %}">Add a new topic:a>

{% endblock content %}

现在打开服务器,打开网页,可以看到效果了:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

添加新条目:

forms.py:

from django import forms
from .models import Topic,Entry

--snip--
class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        field = {'text'}
        labels = {'text':''}

        widgets = {'text': forms.Textarea(attrs={'cols':80})}

learning_logs/urls.py:

urlpatterns = [
    --snip--
    re_path('new_entry/(?P\d+)/', views.new_entry, name='new_entry'),
   ]

views.py:

--snip--
from .forms import TopicForm,EntryForm
--snip--
def new_entry(request,topic_id):
    topic = Topic.objects.get(id=topic_id)
    if request.method != 'POST':
        form = EntryForm()
    else:
        form = EntryForm(data=request.POST)
        if form.is_valid():
            new_entry = form.save(commit=False)
            new_entry.topic = topic
            new_entry.save()

            return HttpResponseRedirect(reverse('learning_logs:topic',args=[topic_id]))
    context = {'topic': topic,'form': form}
    return  render(request,'learning_logs/new_entry.html',context)

new_entry.html:

{% extends "learning_logs/base.html" %}

{% block content %}
<p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}a>p>

<p>Add a new entry:p>
<form action="{% url 'learning_logs:new_entry' topic.id %}" method="post">
    {% csrf_token %}
    {{ form.as_p}}
    <button name="submit">add entrybutton>
form>
{% endblock content %}

页面的顶端是主题名,同时也是一个链接。表单的实参action包含topic_id值,让视图函数能将新条目关联到正确的主题。

更改topic.html,添加链接:

{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topic:{{ topic }}p>
<p>Entriesp>
<p>
    <a href="{% url 'learning_logs:new_entry' topic.id %}">add new entrya>
p>
<ul>
--snip--
ul>
{% endblock content %}

这时网页就可以看到效果了:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

编辑条目

创建一个页面,让用户能编辑现有的条目。

urls.py:

--snip--
urlpatterns = [
    --snip--
    re_path('edit_entry/(?P\d+)/', views.edit_entry, name='edit_entry'),
   ]

views.py:

from django.shortcuts import render
from .models import Topic,Entry
from django.http import HttpResponseRedirect
from django.urls import reverse
from .forms import TopicForm,EntryForm

--snip--
def edit_entry(request,entry_id):
    entry = Entry.objects.get(id-entry_id)
    topic = entry.topic
    if request.method != 'POST':
        form = EntryForm(instance=entry)
    else:
        form = EntryForm(instance=entry,data=request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topic',args=[topic.id]))
    context = {'entry':entry,'topic':topic,'form':form}
    return render(request,'learning_logs/edit_entry.html',context)

新建edit_entry.html:

{% extends "learning_logs/base.html" %}

{% block content %}
<p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}a>p>

<p>Edit entry:p>
<form action="{% url 'learning_logs:edit_entry' entry.id %}" method="post">
    {% csrf_token %}
    {{ form.as_p}}
    <button name="submit">save changesbutton>
form>
{% endblock content %}

修改topic.html:

--snip--
    {% for entry in entries %}
        <li>
            <p>{{ entry.date_added|date:'M d, Y H:i' }}p>
            <p>{{ entry.text|linebreaks }}p>
            <a href="{% url 'learning_logs:edit_entry' entry.id %}">edit entrya>
        li>
--snip--

这时打开网页可以看到效果:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

创建用户账户

下面将建立用户注册和身份验证系统,我们将创建一个新的应用程序,包含与处理用户账户相关的所有功能。

首先使用命令创建名为users的应用程序,结构与learning_logs相同:

python manage.py startapp users

settings.py

--snip--
INSTALLED_APPS = [
    --snip--

    'learning_logs',
    'users',
]

learning_log/urls.py:

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('learning_logs.urls', namespace='learning_logs')),
    path('users/', include('users.urls', namespace='users')),
]

登录页面:

在users文件夹中新建urls.py:

from django.urls import path,re_path
from . import views
from django.contrib.auth.views import LoginView

app_name='users'

urlpatterns = [

    path('login/', LoginView.as_view(template_name='users/login.html'), name='login')
   ]

接下来在users中创建一个templates目录,在templates中创建一个users目录,又在这个users目录中创建login.html:

{% extends "learning_logs/base.html" %}

{% block content %}
  {% if form.errors %}
    <p>>Your username and password didn't match.Please try again.p>
   {% endif %}
   <form method="post" action=" {% 'users:login' %}">
       {% csrf_token %}
       {{ form.as_p }}
       <button name="submit">log inbutton>
       <input type="hidden" name="next" value="{% url 'learning_logs:index' %}" />
   form>
{% endblock content %}

首先如果表单的errors属性被设置,就显示错误信息,表示用户名密码不匹配。要让登录视图处理表单,因此将action设置为登录页面的URL,登录视图发送一个表单给模板,模板中显示这个表单并添加一个提交按钮。最后包含了一个隐藏表单元素next,其中的参数value告诉Django在用户登录成功后定位到主页。

下面在base.html添加到登录页面的链接:

<p>
    <a href="{% url 'learning_logs:index' %}">learning Loga> -
    <a href="{% url 'learning_logs:topics' %}">Topicsa> -
    {% if user.is_authenticated %}
      Hello,{{ user.username }}.

    {% else %}
      <a href="{% url 'users:login' %}">log ina>
    {% endif %}
p>

{% block content %}{% endblock content %}

在Django身份验证系统中,每个模板都可使用变量user,这个变量的is_authenticated属性在登录时为True,否则为False。

此时在admin页面退出管理员账户,就可以在网页中看到log in按钮,点击它:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

注销:

让用户点击一个按钮就可注销并返回主页:

users/urls.py:

--snip--
urlpatterns = [
    path('login/', LoginView.as_view(template_name='users/login.html'), name='login')
    path('logout/', views.logout_view, name='logout')
   ]

users/views.py(注意新版的包名有所变化):

from django.urls import reverse
from django.contrib.auth import logout
from django.http import HttpResponseRedirect

def logout_view(request):
    logout(request)
    return HttpResponseRedirect(reverse('learning_logs:index'))

建立链接,修改base.html,给登录后的用户名旁边加上log out按钮:

<p>
    <a href="{% url 'learning_logs:index' %}">learning Loga> -
    <a href="{% url 'learning_logs:topics' %}">Topicsa> -
    {% if user.is_authenticated %}
      Hello,{{ user.username }}.

      <a href="{% url 'users:logout' %}">log outa>
    {% else %}
      <a href="{% url 'users:login' %}">log ina>
    {% endif %}
p>
{% block content %}{% endblock content %}

注册页面:

users/urls.py:

--snip--
urlpatterns = [
    path('login/', LoginView.as_view(template_name='users/login.html'), name='login'),
    path('logout/', views.logout_view, name='logout'),
    path('register/', views.register, name='register'),
   ]

users/views.py:

from django.urls import reverse
from django.contrib.auth import logout,login,authenticate
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.contrib.auth.forms import UserCreationForm

def logout_view(request):
    logout(request)
    return HttpResponseRedirect(reverse('learning_logs:index'))

def register(request):
    if request.method != 'POST':
        form = UserCreationForm()
    else:
        form = UserCreationForm(data=request.POST)
        if form.is_valid():
            new_user = form.save()
            authenticated_user = authenticate(username=new_user.username,password=request.POST['password1'])
            login(request,authenticated_user)
            return HttpResponseRedirect(reverse('learning_logs:index'))
    context = {'form':form}
    return render(request,'users/register.html',context)

保存用户信息后,我们调用authenticate(),将实参用户名和密码传递给它,返回一个通过了 身份验证的用户对象,然后调用login登录。用户注册时被要求输入密码两次,输入两次相同表单才可能有效。

在login.html目录下新建register.html:

{% extends "learning_logs/base.html" %}

{% block content %}

<form action="{% url 'users:register' %}" method="post">
    {% csrf_token %}
    {{ form.as_p}}
    <button name="submit">registerbutton>
    <input type="hidden" name="next" value="{% url 'learning_logs:index' %}" />
form>
{% endblock content %}

在base.html添加用户在没有登录时显示到注册页面的链接:

--snip--
    {% else %}
      <a href="{% url 'users:register' %}">registera> -
      <a href="{% url 'users:login' %}">log ina>
    {% endif %}
--snip--

现在可以看到效果了:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

让用户拥有自己的数据

这里将创建一个系统,确定各项数据所属用户,再限制对页面的访问,让用户只能使用自己的数据。

Django提供了修饰器@login_required,可以实现某些页面只允许已登录的用户访问。

修改learning_logs/views.py:

--snip--
from django.contrib.auth.decorators import login_required

--snip--
@login_required
def topics(request):
    --snip--
@login_required
def topic(request,topic_id):

@login_required
def new_topic(request):
    --snip--
@login_required
def new_entry(request,topic_id):
    --snip--
@login_required
def edit_entry(request,entry_id):
    --snip--

login_required()检查用户是否已登录,当用户已登录时,才运行topics的代码,否则重定向到登录页面。

修改settings.py,让Django知道到哪里查找登录页面,在末尾添加:

LOGIN_URL = '/users/login/'

现在未登录状态下点击topics或输入编辑添加主题条目的链接,就会跳转到登录页面。

将数据关联到用户:

我们只需将最高层的数据关联到用户,这样更底层的数据自动管理到用户。下面修改模型Topic,添加一个关联到用户的外键,完成后必须对数据库进行迁移。最后必须对一些视图进行修改,使其只显示与当前登录的用户相关联的数据。

models.py

from django.db import models
from django.contrib.auth.models import User

class Topic(models.Model):
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(User,on_delete=models.CASCADE)
    def __str__(self):
        return self.text
--snip--

确定当前数据库有哪些用户,输入命令启动Django shell会话:

$ python manage.py shell
Python 3.9.1 (tags/v3.9.1:1e5d33e, Dec  7 2020, 17:08:21) [MSC v.1927 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> User.objects.all()
, ]>
>>> for user in User.objects.all():
...     print(user.username,user.id)
...

tianjue 1
测试账户 2
>>>

迁移数据库:

$ python manage.py makemigrations learning_logs
You are trying to add a non-nullable field 'owner' to topic without a default; we can't do that (the database needs something to populate existing rows).

Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 1
Migrations for 'learning_logs':
  learning_logs\migrations\0003_topic_owner.py
    - Add field owner to topic

执行迁移:

$ python manage.py migrate

现在可以在shell中验证是否符合预期:

>>> from learning_logs.models import Topic
>>> for topic in Topic.objects.all():
...     print(topic,topic.owner)
...

Chess tianjue
Rock Climbing tianjue
Learning tianjue

可以看到每个主题都属于用户tianjue了。

只允许访问自己的主题:

learning_logs/views.py:

--snip--
from django.http import HttpResponseRedirect,Http404
--snip--
@login_required
def topics(request):

    topics = Topic.objects.filter(owner=request.user).order_by('date_added')
    context = {'topics':topics}
    return render(request,'learning_logs/topics.html',context)

@login_required
def topic(request,topic_id):
    topic = Topic.objects.get(id=topic_id)
    if topic.owner != request.user:
        raise  Http404
    entries = topic.entry_set.order_by('-date_added')
    context = {'topic':topic,'entries':entries}
    return render(request,'learning_logs/topic.html',context)
@login_required
def new_topic(request):
    if request.method != 'POST':
        form = TopicForm()
    else:
        form = TopicForm(request.POST)
        if form.is_valid():
            new_topic = form.save(commit=False)
            new_topic.owner = request.user
            new_topic.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))
    context = {'form': form}
    return render(request,'learning_logs/new_topic.html',context)
@login_required
def new_entry(request,topic_id):
    topic = Topic.objects.get(id=topic_id)
    if topic.owner != request.user:
        raise Http404
    --snip--
@login_required
def edit_entry(request,entry_id):
    entry = Entry.objects.get(id=entry_id)
    topic = entry.topic
    if topic.owner != request.user:
        raise Http404
    --snip--

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

现在这个项目运行任何用户注册,每个用户可以随意添加主题和条目,并且只能访问自己的数据。

设置应用程序的样式并对其进行部署

设置项目的样式

当前,学习笔记的功能基本完成,但未设置样式。我们将使用Bootstrap库,这是一种工具,用于为Web应用程序设置样式,最后把这个项目部署到服务器端。

执行命令,安装django-bootstrap3:

$ pip3 install django-bootstrap3

在settings.py中添加代码:

--snip--
INSTALLED_APPS = [
    --snip--

    'bootstrap3',

    'learning_logs',
    'users',
]
--snip--
BOOTSTRAP3 ={
    'include_jquery': True,
}

需要让django-bootstrap3包含jQuery,这是一个JavaScript库,能够让你使用Bootstrap模板的一些交互性元素,这样无需手工下载jQuery。

Bootstarp是一个大型样式设置工具集,提供了大量的模板,具体可访问https://getbootstrap.com/。

首先需要修改base.html,在这个文件定义HTML头部,添加一些在模板中使用Bootstrap所需的信息,删除base.html全部代码,改为:

{% load bootstrap3 %}

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Learning Logtitle>
    {% bootstrap_css %}
    {% bootstrap_javascript %}
head>

<body>

    <nav class="navbar navbar-default navbar-static-top">
        <div class="container">

            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed"
                        data-toggle="collapse" data-target="#navbar"
                        aria-expanded="false" aria-controls="navbar">button>
                <a class="navbar-brand" href="{% url 'learning_logs:index' %}">
                    Learning Log
                a>
            div>

            <div id="navbar" class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a href="{% url 'learning_logs:topics' %}">Topicsa>li>
                ul>

                <ul class="nav navbar-nav navbar-right">
                    {% if user.is_authenticated %}
                    <li><a>Hello, {{ user.username }}.a>li>
                    <li><a href="{% url 'users:logout' %}">log outa>li>
                    {% else %}
                    <li><a href="{% url 'users:register' %}">registera>li>
                    <li><a href="{% url 'users:login' %}">log ina>li>
                    {% endif %}
                ul>
            div>

        div>
    nav>

    <div class="container">
        <div class="page-header">
            {% block header %}{% endblock %}
        div>
        <div>
            {% block content %}{% endblock %}
        div>
    div>
body>
html>

首先,HTML分为两个主要部分:头部(head)和主体(body),头部不包含任何内容,只是将正确显示页面所需的信息告诉浏览器。

主体包含用户在页面上看到的内容 元素表示页面的导航链接部分,这个元素内的所有内容,都根据(selector)navbar、navbar-default、navbar-static-top定义的Bootstrap样式规则来设置样式,选择器决定了特定的样式规则应用于页面的哪些元素。

在class=”navbar-header”的地方,定义了一个按钮

定义了一组让用户能够在网站中导航的链接,导航栏是一个以
开头的列表,其中的每个链接都是列表项
* ,要添加更多的链接,可插入下述结构的行:

<li><a href="{% url 'learning_logs:title' %}">Titlea>li>

后面的部分是一个容器,包含一个名为header的块和content块,header块决定页面包含哪些消息以及用户可在页面上执行哪些操作,其属性page-header将一系列的样式应用于这个块。

现在打开浏览器可以看到网页发生了很大改变:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

设置登录页面的样式:

login.html:

{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}

{% block header %}
  <h2>Log in to your account.h2>
{% endblock header %}

{% block content %}
   <form method="post" action="{% url 'users:login' %}" class="form">
       {% csrf_token %}
       {% bootstrap_form form %}
       {% buttons %}
         <button name="submit" class="btn btn-primary">log inbutton>
       {% endbuttons %}
       <input type="hidden" name="next" value="{% url 'learning_logs:index' %}" />
   form>
{% endblock content %}

首先是加载bootstrap3模板,然后定义header块,原来的if form.error代码块删除了,因为bootstrap3会自动管理表单错误。

后面用bootstrap_form form显示表单,替换了原来的form.as_p。后面的按钮用了bootstrap3模板标签。

现在 访问login页面,样式已经改变:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

设置new_topic页面的样式:

修改new_topic.html:

{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}

{% block header %}
  <h2>Add a new topic:h2>
{% endblock header %}

{% block content %}
<form action="{% url 'learning_logs:new_topic' %}" method="post" class="form">
    {% csrf_token %}
    {% bootstrap_form form %}
    {% buttons %}
    <button name="submit" class="btn btn-primary">add topicbutton>
    {% endbuttons %}
form>
{% endblock content %}

可以看到效果:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

设置topics页面的样式:

topics.html:

{% extends "learning_logs/base.html" %}

{% block header %}
<h1>Add a new topic:h1>
{% endblock header %}

{% block content %}
<ul>
    {% for topic in topics %}
    <li>
        <h3>
            <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}a>
        h3>
    li>
    {% empty %}
    <li>No topics have been added yet.li>
    {% endfor %}
ul>
<h3><a href="{% url 'learning_logs:new_topic' %}">Add a new topic:a>h3>
{% endblock content %}

这里没有使用bootstrap3自定义标签,只是加了header块,改了字体大小。

设置topic页面中的条目样式:

topic.html:

{% extends "learning_logs/base.html" %}

{% block header %}
<h2>{{ topic }}h2>
{% endblock header %}

{% block content %}
<p>
    <a href="{% url 'learning_logs:new_entry' topic.id %}">add new entrya>
p>

{% for entry in entries %}
  <div class="panel panel-default">
      <div class="panel-heading">
          <h3>
              {{ entry.date_added|date:'M d, Y H:i' }}
              <small>
                  <a href="{% url 'learning_logs:edit_entry' entry.id %}">edit entrya>
              small>
          h3>
      div>
      <div class="panel-body">
          {{ entry.text|linebreaks }}
      div>
  div>
{% empty %}
    There are no entries for this topic yet.

{% endfor %}
{% endblock content %}

删除了以前使用的无序列表结构,创建了面板式div元素,而不是将每一个条目作为一个列表项,其中有两个嵌套div:面板标题(panel-heading)div和面板主体(panel-body)div。面板标题div包含条目的创建日期以及用于编辑条目的链接,还使用了标签small使其比时间戳小一些。面板主体包含实际文本。

效果如下:

Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

同样,给其他页面设置:

new_entry.html:

{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}

{% block header %}
<h2><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}a>h2>
<h3>Add a new entry:h3>
{% endblock header %}

{% block content %}
<form action="{% url 'learning_logs:new_entry' topic.id %}" method="post" class="form">
    {% csrf_token %}
    {% bootstrap_form form %}
    {% buttons %}
    <button name="submit" class="btn btn-primary">add entrybutton>
    {% endbuttons %}
form>
{% endblock content %}

edit_entry.html:

{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}

{% block header %}
<h2><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}a>h2>
<h3>Edit entry:h3>
{% endblock header %}

{% block content %}
<form action="{% url 'learning_logs:edit_entry' entry.id %}" method="post" class="form">
    {% csrf_token %}
    {% bootstrap_form form %}
    {% buttons %}
    <button name="submit" class="btn btn-primary">save changesbutton>
    {% endbuttons %}
form>
{% endblock content %}

部署学习笔记

由于在墙内是注册不了书本上要求的网站的,没办法还原书本的操作。我们需要按自己的方法部署。这部分需要话费的精力较多,还需要购买云服务器,需要一定的时间成本和金钱成本,具体可参考视频python3 django项目部署方案Nginx + uWsgi 部署 Django + Mezzanine 生产服务器。等我以后有需求时会写一篇新博文来完善此处内容。

最后的话

至此,pyhon的基本知识学习完毕了,我们掌握了python的基本语法,能够自己下载和处理一些数据了,还能用Django搭建自己的网站,现在已经具备了开发各种项目所需的python基本技能。在学习的过程中,肯定会遇到自己不能解决的问题,这时候,查阅资料的能力尤为关键,其中给我最大帮助的是CSDN,遇到的大多数问题都能在其中找到答案,最后郑重提醒:每学完一项技术时,一定要写博客,写博客,写博客,很重要!这对知识的巩固有至关重要的作用,博客的搭建可以参考我的另一篇博文《个人建立hexo博客Matery主题的过程心得》。

祝贺你在学习Python的道路上走出了坚实的一步,愿你在以后的学习中好运相伴!

Original: https://blog.csdn.net/tianjuewudi/article/details/112920545
Author: 微笑小星
Title: Python编程从入门到实践笔记(超详细的精华讲解+内有2021最新版本代码)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/735952/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球