通过一个简单的django项目的学习和实践,来加深对django的理解。
安装和创建
安装django
pip install django
创建项目
django-admin startproject learning_log .
这个命令末尾的句点让新项目使用合适的目录结构,这样开发完成后可轻松地将应用程序部署到服务器
千万别忘了这个句点,否则部署应用程序时将遭遇一些配置问题。如果忘记了这个句点,要删除已创建的文件和文件夹(虚拟环境venv文件夹除外),再重新运行这个命令
绿色文件是执行命令之后生成的文件
settings.py指定Django如何与系统交互以及如何管理项目。在开发项目的过程中,我们将修改其中一些设置,并添加一些设置。
urls.py告诉Django,应创建哪些页面来响应浏览器请求。
wsgi.py帮助Django提供它创建的文件,这个文件名是Web服务器网关接口(Web server gateway interface)的首字母缩写。
创建数据库
python manage.py migrate
执行结果
执行后会生成一个db.sqlite3
启动查看项目
此时项目已经生成,启动项目
python manage.py runserver
Development server已经启动在本地的8000端口,如果想关闭使用Ctrl+C
浏览器访问效果
创建应用程序
python manage.py startapp learning_logs
红色文件是执行命令之后生成的文件
编写实体模型models
最初的models.py文件
开始编写我们需要的实体
参考文档:https://docs.djangoproject.com/zh-hans/5.0/ref/models/fields/
classTopic(models.Model):#继承Model,即Django中定义了模型基本功能的类
"""用户学习主题"""
text=models.CharField(max_length=200)#由字符组成的数据,即文本
date_added=models.DateTimeField(auto_now=True)#日期和时间的数据,参数属性表示自动设置当前日期和时间
def__str__(self):
"""返回模型的字符串表示"""
returnself.text#告诉Django,默认使用哪个属性来显示有关主题的信息
激活模型
让Django将上面我们写的应用程序包含到项目中,settings.py中的INSTALLED_APPS片段就是这个作用。
添加我们的应用程序
INSTALLED_APPS=[
#我自己的应用程序
'learning_logs'
#默认添加的应用程序
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
迁移数据库
因为我们修改了models,所以为了同步到数据库,需要迁移
python manage.py makemigrations learning_logs
第一次报了一个错误:ModuleNotFoundError: No module named ‘learning_logsdjango’
原因是因为我忘记了写逗号
修改后执行结果,会在migrations目录下新建一个0001_initial.py迁移文件
然后应用这个迁移
python manage.py migrate
总结,每次修改数据模型的时候都要执行三个步骤:
1. 修改models.py
2. 生成迁移文件:python manage.py makemigrations learning_logs
3. 应用迁移:python manage.py migrate
django的管理网站
django会默认提供一个管理网站
创建超级用户
python manage.py createsuperuser
一些敏感信息可能会向网站管理员隐藏。例如,Django并不存储你输入的密码,而是存储从该密码派生出来的一个字符串,称为散列值。每当你输入密码时,Django都计算其散列值,并将结果与存储的散列值进行比较。如果这两个散列值相同,你就通过了身份验证。由于存储的是散列值,即便黑客获得了网站数据库的访问权,也只能获取其中存储的散列值,无法获得密码。在网站配置正确的情况下,几乎无法根据散列值推导出原始密码。
向管理网站注册模型
Django自动在管理网站中添加了一些模型,如User和Group,但对于我们创建的模型,必须手工进行注册
fromdjango.contribimportadmin
from.modelsimportTopic
#Registeryourmodelshere.
admin.site.register(Topic)#django通过管理网站管理模型
访问管理网站
http://127.0.0.1:8000/admin/
添加主题
点击add之后save添加
定义模型Entry
条目Entry和主题Topic多对一
classTopic(models.Model):#继承Model,即Django中定义了模型基本功能的类
"""用户学习主题"""
text=models.CharField(max_length=200)#由字符组成的数据,即文本
date_added=models.DateTimeField(auto_now=True)#日期和时间的数据,参数属性表示自动设置当前日期和时间
def__str__(self):
"""返回模型的字符串表示"""
returnself.text#告诉Django,默认使用哪个属性来显示有关主题的信息
classEntry(models.Model):
"""学到的某个主题的具体知识"""
topic=models.ForeignKey(Topic,on_delete=models.CASCADE)
#外键(foreignkey)是一个数据库术语,它指向数据库中的另一条记录,这里是将每个条目关联到特定主题
#创建每个主题时,都分配了一个键(ID)。需要在两项数据之间建立联系时,Django使用与每项信息相关联的键.
#on_delete=models.CASCADE让Django在删除主题的同时删除所有与之相关联的条目,这称为级联删除(cascadingdelete)
text=models.TextField()
date_added=models.DateTimeField(auto_now=True)
classMeta:
verbose_name_plural='entries'
#Django在需要时使用Entries来表示多个条目。如果没有这个类,Django将使用Entrys来表示多个条目
def__str__(self):
"""返回字符串表示"""
returnf"{self.text[:50]}..."
然后迁移数据库模型,三步骤。
向管理网站注册Entry
admin.site.register(Entry)
添加条目
django shell的使用
每次修改模型后,都需要重启shell,这样才能看到修改的效果。要退出shell会话,可按Ctr +
D。如果你使用的是Windows系统,应按Ctr + Z,再按回车键。
python manage.py shell
执行一些遍历等操作
根据id获取topic的属性
使用外键获取关联数据
要通过外键关系获取数据,可使用相关模型的小写名称、下划线和单词set
创建页面
三个部分:定义URL,编写视图(views),编写模板(html)。
映射URL
urls.py
fromdjango.contribimportadmin
fromdjango.urlsimportpath,include
urlpatterns=[
path('admin/',admin.site.urls),
path('',include('learning_logs.urls'))
]
默认的urls.py包含在文件夹learning_log中,现在需要在文件夹learning_logs中再创建一个urls.py文件。
"""定义learning_logs的URL模式"""
fromdjango.urlsimportpath
fromlearning_logsimportviews
app_name='learning_logs'#变量app_name让Django能够将这个urls.py文件同项目内其他应用程序中的同名文件区分开来
urlpatterns=[
path('',views.index,name='index')
]
实际的URL模式是对函数path()的调用,这个函数接受三个实参:
第一个是一个字符串’ ‘,帮助Django正确地路由(route)请求。收到请求的URL后,Django力图将请求路由给一个视图。为此,它搜索所有的URL模式,找到与当前请求匹配的那个。Django忽略项目的基础URL(http://localhost:8000/),因此空字符串(’')与基础URL匹配.
第二个实参views.index指定了要调用view.py中的哪个函数。请求的URL与前述正则表达式匹配时,Django将调用view.py中的函数index().
第三个实参name='index’将这个URL模式的名称指定为index,让我们能够在代码的其他地方引用它。每当需要提供到这个主页的链接时,都将使用这个名称,而不编写URL。
编写视图
fromdjango.shortcutsimportrender
#Createyourviewshere.
defindex(request):
"""学习笔记的主页"""
returnrender(request,'learning_logs/index.html')#函数render(),它根据视图提供的数据渲染响应
编写模板
一般保存在templates目录下
效果
父模板
创建网站时,一些通用元素几乎会在所有页面中出现。在这种情况下,可编写一个包含通用元素的父模板,并让每个页面都继承这个模板,而不必在每个页面中重复定义这些通用元素。
父模板的好处:
模板继承的优点开始显现出来了:在子模板中,只需包含当前页面特有的内容。这不仅简化了每个模板,还使得网站修改起来容易得多。要修改很多页面都包含的元素,只需修改父模板即可,所做的修改将传导到继承该父模板的每个页面。在包含数十乃至数百个页面的项目中,这种结构使得网站改进起来更容易、更快捷。
<p>
<ahref="{%url'learning_logs:index'%}">LearningLog</a>
</p>
{%blockcontent%}{%endblockcontent%}
模板标签:用花括号和百分号({% %})表示的
这里的模板标签{% url ‘learning_logs:index’%}生成一个URL,该URL与在learning_logs/urls.py中定义的名为’index’的URL模式匹配
learning_logs是一个命名空间,而index是该命名空间中一个名称独特的URL模式。这个命名空间来自在文件learning_logs/urls.py中赋给app_name的值。
作用:通过使用模板标签来生成URL,能很容易地确保链接是最新的:只需修改urls.py中的URL模式,Django就会在页面下次被请求时自动插入修改后的URL。
块标签:这个块名为content,是一个占位符,其中包含的信息由子模板指定。
子模板并非必须定义父模板中的每个块,因此在父模板中,可使用任意多个块来预留空间,而子模板可根据需要定义相应数量的块。
创建其他页面
创建页面的步骤均是:url+视图+模板+模板入口添加
创建topics页面
path('topics/',views.topics,name='topics')
deftopics(request):
"""显示所有主题"""
topics=Topic.objects.order_by('date_added')
context={'topics':topics}#定义一个将发送给模板的上下文。上下文是一个字典,其中的键是将在模板中用来访问数据的名称,而值是要发送给模板的数据。
returnrender(request,'learning_logs/topics.html',context)
将新的模板添加到base中
访问效果
创建单个topic页面
path('topics/<int:topic_id>',views.topic,name='topic')
#第一部分让Django查找在基础URL后包含单词topics的URL,第二部分(/<int:topic_id>/)与包含在两个斜杠内的整数匹配,并将这个整数存储在一个名为topic_id的实参中
deftopic(request,topic_id):
"""显示单个主题的所有条目"""
topic=Topic.objects.get(id=topic_id)
entries=topic.entry_set.order_by('date_added')
context={'topic':topic,'entries':entries}
returnrender(request,'learning_logs/topic.html',context)
“|”:模板过滤器,对模板变量的值进行修改的函数
创建表单
在Diango中,创建表单的最简单方式是使用ModelForm,根据models模型中的信息自动创建表单。
创建topic的表单
创建forms
fromdjangoimportforms
fromlearning_logs.modelsimportTopic
classTopicForm(forms.ModelForm):#继承forms.ModelForm
classMeta:
model=Topic#根据哪个模型创建表单
fields=['text']#表单只包含字段text
labels={'text':''}#不为字段text生成标签
创建页面
path('new_topic/',views.new_topic,name='new_topic')
defnew_topic(request):
"""添加新主题"""
ifrequest.method!='POST':#如果不是post而是get请求,那么提交空表单
form=TopicForm()#建一个TopicForm实例,将其赋给变量form,再通过上下文字典context将这个表单发送给模板
else:
form=TopicForm(data=request.POST)
ifform.is_valid():#方法is_valid()核实用户填写了所有必不可少的字段(表单字段默认都是必不可少的),且输入的数据与要求的字段类型一致
form.save()#将表单中的数据写入数据库
returnredirect('learning_logs:topics')#函数redirect将视图名作为参数,并将用户重定向到这个视图
context={'form':form}
returnrender(request,'learning_logs/new_topic.html',context)#无论是用户刚进入new_topic页面还是提交的表单数据无效,这些代码都将执行
然后在topics页面添加new_topic的链接。
效果图
创建entry表单
更新forms,url+views+html
classEntryForm(forms.ModelForm):
classMeta:
model=Entry
fields=['text']
labels={'text':'Entry'}
#小部件(widget)是一个HTML表单元素,如单行文本框、多行文本区域或下拉列表。通过设置属性widgets,可覆盖Django选择的默认小部件。通过让Django使用forms.Textarea,我们定制了字段
#'text'的输入小部件,将文本区域的宽度设置为80列,而不是默认的40列
widgets={'text':forms.Textarea(attrs={'cols':80})}
path('new_entry/<int:topic_id>/',views.new_entry,name='new_entry')
在topic.html中添加链接到new_entry
效果
编辑条目页面
path('edit_entry/<int:entry_id>/',views.edit_entry,name='edit_entry')
defedit_entry(request,entry_id):
"""编辑条目"""
entry=Entry.objects.get(id=entry_id)
topic=entry.topic
ifrequest.method!='POST':
#初次请求,回显当前条目
form=EntryForm(instance=entry)
else:
form=EntryForm(instance=entry,
data=request.POST)#传递实参instance=entry和data=request.POST,让Django根据既有条目对象创建一个表单实例
ifform.is_valid():
form.save()
returnredirect('learning_logs:topic',topic=topic.id)
context={'entry':entry,'topic':topic,'form':form}
returnrender(request,'learning_logs/edit_entry.html',context)
效果
添加users用户管理
创建应用程序users
python manage.py startapp users
添加到settings中
INSTALLED_APPS=[
#我自己的应用程序
'learning_logs',
'users',
#默认添加的应用程序
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
添加到根目录的urls映射中
urlpatterns=[
path('admin/',admin.site.urls),
path('users/',include('users.urls')),
path('',include('learning_logs.urls')),
]
login
添加到当前应用urls映射
"""
定义users的url模式
"""
fromdjango.urlsimportpath,include
app_name='users'#变量app_name让Django能够将这个urls.py文件同项目内其他应用程序中的同名文件区分开来
urlpatterns=[
#包含默认的身份认证url
path('',include('django.contrib.auth.urls'))
]
使用django默认的login视图,所以不需要编写views。
创建模板
默认的身份验证视图在文件夹registration中查找模板
在目录learning_log/users/中新建一个名为templates的目录,再在这个目录中新建一个名为registration的目录
在base.html添加登录的链接,让每个页面都显示
效果
logout
注销模板
默认的注销视图使用模板logged_out.html渲染注销确认页面。
效果
register
使用Django提供的表单UserCreationForm,不需要单独创建forms。
path('register/',views.register,name='register')
fromdjango.contrib.authimportlogin
fromdjango.contrib.auth.formsimportUserCreationForm
fromdjango.shortcutsimportrender,redirect
#Createyourviewshere.
defregister(request):
"""注册新用户"""
ifrequest.method!='POST':
form=UserCreationForm()
else:
form=UserCreationForm(data=request.POST)
ifform.is_valid():
new_user=form.save()
login(request,new_user)#django默认的登录视图方法
returnredirect('learning_logs:index')
context={'form':form}
render(request,'registration/register.html',context)
效果
登录访问限制
使用装饰器login_required包裹views中每个方法,除了首页index之外,如果你在未登录的情况下尝试访问这些页面,将被重定向到登录页面。
数据关联用户
需要将数据关联到提交它们的用户。只需将最高层的数据关联到用户,更低层的数据就会自动关联到用户。
修改模型models
获取admin用户的id,将所有主题关联到admin用户
因为新增了owner字段,需要执行数据库迁移
使用shell验证是否迁移成功(查看所有topic是否属于admin用户)
调整只允许用户访问自己的主题
对于单个主题的访问也需要添加限制,否则输入id之后依旧可以访问不属于自己的主题
限制编辑条目页面只允许修改属于自己的条目
因为topic添加了owner,所以新建topic的时候要输入owner
最终项目就编写完成,之后可以用过bootstrap调整项目的样式,让项目更加好看一些。