用户账户 用户添加主题 在learning_log目录中创建forms.py文件,添加
1 2 3 4 5 6 7 8 9 10 from django import formsfrom .models import Topicclass TopicForm (forms.ModelForm): """A form for creating and updating topics.""" class Meta : model = Topic fields = ['text' ] labels = {'text' : '' }
添加URL:path('new_topic/', views.new_topic, name='new_topic')
创建视图函数new_topic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def new_topic (request ): """Add a new topic.""" if request.method != 'POST' : form = TopicForm() else : form = TopicForm(data=request.POST) if form.is_valid(): new_topic = form.save() return redirect('learning_log:topic' , topic_id=new_topic.id ) context = {'form' : form} return render(request, 'learning_log/new_topic.html' , context)
创建模板new_topic.html
1 2 3 4 5 6 7 8 9 10 11 {% extends 'learning_log/base.html' %} {% block content %} <p > Add a new topic:</p > <form action =" {% url 'learning_log:new_topic' %} " method ="post" > {% csrf_token %} {{ form.as_div }} <button type ="submit" > Add Topic</button > </form > {% endblock content %}
链接到页面:<a href = "{% url 'learning_log:new_topic' %}">Add a new topic</a>
代码解释 :当用户在网页中点击 <a href="{% url 'learning_log:new_topic' %}">Add a new topic</a>
这个链接时,浏览器会跳转到 /new_topic/
。这个路径在 urls.py
中由 path('new_topic/', views.new_topic, name='new_topic')
映射到 views.new_topic
函数。
当请求被送到这个视图函数时,Django 会调用 new_topic(request)
来处理。这个视图首先判断请求的方法是不是 POST。如果用户只是点击链接,还没提交表单,那么请求方法是 GET,程序会创建一个空的表单对象 form = TopicForm()
,此时这个表单中没有预填数据,只是用于初次显示在网页上。
如果用户已经在表单中输入了内容并点击了提交按钮,那么浏览器发送的是 POST 请求,此时视图就会执行 form = TopicForm(data=request.POST)
来从用户提交的数据构造一个表单实例。接着通过 form.is_valid()
检查数据是否合法(比如字段有没有漏填,格式是否正确)。如果验证通过,就执行 form.save()
,这会自动创建一条新的 Topic 记录并保存到数据库中。接着用 redirect('learning_log:topic', topic_id=new_topic.id)
跳转到刚创建的主题详情页。
不论是初次打开页面,还是提交失败(例如空表单或非法输入),视图最后都会调用 render(request, 'learning_log/new_topic.html', context)
来渲染模板,把表单对象传入 context
变量中。
这个模板 new_topic.html
继承自 base.html
,并在 {% block content %}
中放入了一段 HTML 表单。<form action="{% url 'learning_log:new_topic' %}" method="post">
声明了该表单提交回自己;{% csrf_token %}
是 Django 要求添加的防跨站攻击标记;{{ form.as_div }}
是将表单对象渲染为 HTML 元素。最后 <button type="submit">Add Topic</button>
是提交按钮。
添加条目 代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class EntryForm (forms.ModelForm): """A form for creating and updating entries.""" class Meta : model = Entry fields = ['text' ] labels = {'text' : '' } widgets = {'text' : forms.Textarea(attrs={'cols' : 80 })} path('new_entry/<int:topic_id>/' , views.new_entry, name='new_entry' ) def new_entry (request, topic_id ): """Add a new entry for a particular topic.""" 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 redirect('learning_log:topic' , topic_id=topic_id) context = {'topic' : topic, 'form' : form} return render(request, 'learning_log/new_entry.html' , context)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 {% extends 'learning_log/base.html' %} {% block content %} <p > <a href =" {% url 'learning_log:topic' topic.id %} " >{{ topic }} </a > </p > <p > Add a new entry:</p > <form action =" {% url 'learning_log:new_entry' topic.id %} " method ="post" > {% csrf_token %} {{ form.as_div }} <button type ="submit" > Add Entry</button > </form > {% endblock content %} <p > <a href =" {% url 'learning_log:new_entry' topic.id %} " > Add a new entry</a > </p >
代码解释 :这一套代码实现了在某个具体主题(Topic)下添加新学习内容(Entry)的功能。它包括一个基于模型的表单 EntryForm
,用于生成输入框,主要针对 Entry
模型中的 text
字段,并通过 widgets
设置了一个宽一些的多行文本区域。URL 配置中使用了路径参数 <int:topic_id>
来标识具体主题,这样不同主题就能通过不同链接添加各自的学习记录。视图函数 new_entry
首先获取对应的 Topic 实例,然后根据请求方法判断是初次访问还是表单提交。如果是提交请求,就根据提交内容构造表单并校验,若通过验证,就先创建一个未保存的 Entry 实例,手动指定其 topic 属性,然后保存到数据库,并跳转回该主题的详情页。
模板部分继承自基础模板 base.html
,显示了当前主题的名称,并提供一个表单用于输入新条目。提交按钮下方还包含一个返回原主题页面的链接。最后那段 HTML 链接代码负责在主题详情页中显示“Add a new entry”按钮,点击即可跳转到添加页面。
编辑条目 代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 path('edit_entry/<int:entry_id>/' , views.edit_entry, name='edit_entry' ) def edit_entry (request, entry_id ): """Edit an existing entry.""" 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 redirect('learning_log:topic' , topic_id=topic.id ) context = {'entry' : entry, 'topic' : topic, 'form' : form} return render(request, 'learning_log/edit_entry.html' , context)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 {% extends 'learning_log/base.html' %} {% block content %} <p > <a href =" {% url 'learning_log:topic' topic.id %} " >{{ topic }} </a > </p > <p > Edit entry:</p > <form action =" {% url 'learning_log:edit_entry' entry.id %} " method ="post" > {% csrf_token %} {{ form.as_div }} <button type ="submit" > Save Changes</button > </form > {% endblock content %} <li > <p > {{ entry.date_added|date :'M d, Y H:i' }} </p > <p > {{ entry.text|linebreaks }} </p > <p > <a href =" {% url 'learning_log:edit_entry' entry.id %} " > Edit entry</a > </p > </li >
创建用户 首先创建一个新的应用程序来管理账户相关的内容
1 python .\manage.py startapp accounts
在INSTALLED_APPS
中添加accounts
,并包含accounts
的URL:path('accounts/', include('accounts.urls'))
在accounts中新建urls.py,添加以下内容
1 2 3 4 5 6 7 from django.urls import path, includeapp_name = 'accounts' urlpatterns = [ path('' , include('django.contrib.auth.urls' )), ]
在templates中新建registration文件夹,添加login.html文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {% extends 'learning_log/base.html' %} {% block content %} {% if form.errors %} <p > Your username and password didn't match. Please try again.</p > {% endif %} <form action =" {% url 'accounts:login' %} " method ='post' > {% csrf_token %} {{ form.as_div }} <button name ="submit" > Log in</button > </form > {% endblock content %}
随后在settings中添加成功登录后的重定向地址:LOGIN_REDIRECT_URL = 'learning_log:index'
修改base.html如下
1 2 3 4 5 6 7 8 9 10 11 12 13 <p > <a href =" {% url 'learning_log:index' %} " > Learning Log</a > - <a href =" {% url 'learning_log:topics' %} " > Topics</a > - {% if user.is_authenticated %} Hello, {{ user.username }} ! - {% else %} <a href =" {% url 'accounts:login' %} " > Log in</a > - {% endif %} </p > {% block content %} {% endblock content %}
此时就可以通过http://localhost:8000/accounts/login/来登录了,可以使用我们的管理员账号来测试(提前在管理界面退出)
注销和注册用户 注销账户 在base.html中添加注销表单
1 2 3 4 5 6 7 {% if user.is_authenticated %} <hr /> <form action =" {% url 'accounts:logout' %} " method ='post' > {% csrf_token %} <button name ='submit' > Log out</button > </form > {% endif %}
如果账户登录,那么则在最下方显示一个注销按钮用于注销账户,注销后将页面链接到主页:LOGOUT_REDIRECT_URL = 'learning_log:index'
注册账户 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 path('register/' , views.register, name='register' ) def register (request ): """Register a new user.""" if request.method != 'POST' : form = UserCreationForm() else : form = UserCreationForm(data=request.POST) if form.is_valid(): new_user = form.save() login(request, new_user) return redirect('learning_log:index' ) context = {'form' : form} return render(request, 'registration/register.html' , context)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {% extends "learning_log/base.html" %} {% block content %} <form action =" {% url 'accounts:register' %} " method ='post' > {% csrf_token %} {{ form.as_div }} <button name ="submit" > Register</button > </form > {% endblock content %} <a href =" {% url 'accounts:register' %} " > Register</a > -
当用户登录后,base.html
会显示一个“注销”按钮,它触发的是 accounts:logout
路由(即 Django 默认的登出视图),并通过 LOGOUT_REDIRECT_URL = 'learning_log:index'
设置注销后跳转回主页。
对于注册功能,定义了一个新的 URL 路由 /accounts/register/
,并在对应视图函数 register()
中使用 Django 提供的 UserCreationForm
构建注册表单。如果请求为 POST,则处理表单提交并保存用户数据,注册成功后自动登录该用户,并重定向到主页。否则显示一个空白或验证失败的注册表单。
前端模板继承自 base.html
,通过 {% csrf_token %}
加入安全标记,表单本体使用 {{ form.as_div }}
渲染输入字段,点击“Register”按钮即可提交注册。
创建用户数据 在未登录的状态下,learning_log中的内容除了主页外,其余内容应该均不可访问,对此我们通过装饰器@login_required
来限制未登录用户的访问,如果用户未登录,我们将其重定向到登录界面,在settings中添加LOGIN_URL = 'accounts:login'
即可
对于不同的用户,除了主页外,其他的内容应该只能访问自己的部分,我们将数据关联到用户,在Topic
中添加owner = models.ForeignKey(User, on_delete=models.CASCADE)
,将主题与用户关联。然后把数据库中的内容进行迁移,由于我们新增了owner
字段,迁移时会提示我们选择哪一种方式,我们选择“1”让系统自动给我们添加一个默认值,然后将所有的内容迁移到管理账户中,即ID“1”
虽然把主题全部与管理账户进行了关联,但是目前任何用户登录均可访问所有主题,在topics
中添加topics = Topic.objects.filter(owner=request.user).order_by('date_added')
,将不属于当前用户的所有Topic
过滤掉,然后在topic
和edit_entry
中添加以下代码
1 2 if topic.owner != request.user: raise Http404
从而防止用户通过网址直接访问他人的数据
最后将新的topic
关联到当前用户中,添加代码如下
1 2 3 new_topic = form.save(commit=False ) new_topic.owner = request.user new_topic.save()
此时任何用户都可创建自己的账号并拥有自己独立的数据了
样式更改 我们的web已经具备基本的功能了,如创建主题、创建与之关联的条目,并且每个用户都有自己的数据而不必担心被其他人访问,但我们的界面还过于简陋,无法吸引用户,因此接下来我们将更改它的布局,使其更加美观
首先安装django-bootstrap5,我们将使用其中的模板
1 pip install django-bootstrap5
然后在INSTALLED_APPS
中我们的应用和默认应用之间添加django_bootstrap5
,接下来修改base.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 <!doctype html > <html lang ="en" > <head > <meta charset ="utf-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1" > <title > Learning Log</title > {% load django_bootstrap5 %} {% bootstrap_css %} {% bootstrap_javascript %} </head > <body > <nav class ="navbar navbar-expand-md navbar-light bg-light mb-4 border" > <div class ="container-fluid" > <a class ="navbar-brand" href =" {% url 'learning_log:index' %} " > Learning Log</a > <button class ="navbar-toggler" type ="button" data-bs-toggle ="collapse" data-bs-target ="#navbarCollapse" aria-controls ="navbarCollapse" aria-expanded ="false" aria-label ="Toggle navigation" > <span class ="navbar-toggler-icon" > </span > </button > <div class ="collapse navbar-collapse" id ="navbarCollapse" > <ul class ="navbar-nav me-auto mb-2 mb-md-0" > <li class ="nav-item" > <a class ="nav-link" href =" {% url 'learning_log:topics' %} " > Topics</a > </li > </ul > <ul class ="navbar-nav ms-auto mb-2 mb-md-0" > {% if user.is_authenticated %} <li class ="nav-item" > <span class ="navbar-text me-2" > Hello, {{ user.username }} . </span > </li > {% else %} <li class ="nav-item" > <a class ="nav-link" href =" {% url 'accounts:register' %} " > Register</a > </li > <li class ="nav-item" > <a class ="nav-link" href =" {% url 'accounts:login' %} " > Log in</a > </li > {% endif %} </ul > {% if user.is_authenticated %} <form action =" {% url 'accounts:logout' %} " method ='post' > {% csrf_token %} <button name ='submit' class ='btn btn-outline-secondary btn-sm' > Log out</button > </form > {% endif %} </div > </div > </nav > <main class ="container" > <div class ="pb-2 mb-2 border-bottom" > {% block page_header %} {% endblock page_header %} </div > <div > {% block content %} {% endblock content %} </div > </main > </body > </html >
修改主页index.html的样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {% extends 'learning_log/base.html' %} {% block page_header %} <div class ="p-3 mb-4 bg-light border rounded-3" > <div class ="container-fluid py-4" > <h1 class ="display-3" > Track your learning.</h1 > <p class ="lead" > Make your own Learning Log, and keep a list of the topics you're learning about. Whenever you learn something new about a topic, make an entry summarizing what you've learned.</p > <a class ="btn btn-primary btn-lg mt-1" href =" {% url 'accounts:register' %} " > Register » </a > </div > </div > {% endblock page_header %}
修改登录界面login.html的样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {% extends 'learning_log/base.html' %} {% load django_bootstrap5 %} {% block page_header %} <h2 > Log in to your account.</h2 > {% endblock page_header %} {% block content %} <form action =" {% url 'accounts:login' %} " method ='post' > {% csrf_token %} {% bootstrap_form form %} {% bootstrap_button button_type="submit" content="Log in " %} </form > {% endblock content %}
修改页面topics的样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 {% extends 'learning_log/base.html' %} {% block page_header %} <h1 > Topics</h1 > {% endblock page_header %} {% block content %} <ul class ="list-group border-bottom pb-2 mb-4" > {% for topic in topics %} <li class ="list-group-item border-0" > <a href =" {% url 'learning_log:topic' topic.id %} " > {{ topic.text }} </a > </li > {% empty %} <li class ="list-group-item border-0" > No topics have been added yet.</li > {% endfor %} </ul > <a href =" {% url 'learning_log:new_topic' %} " > Add a new topic</a > {% endblock content %}
修改页面topic条目的样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 {% extends 'learning_log/base.html' %} {% block page_header %} <h1 > {{ topic.text }} </h1 > {% endblock page_header %} {% block content %} <p > <a href =" {% url 'learning_log:new_entry' topic.id %} " > Add new entry</a > </p > {% for entry in entries %} <div class ="card mb-3" > <h4 class ="card-header" > {{ entry.date_added|date :'M d, Y H:i' }} <small > <a href =" {% url 'learning_log:edit_entry' entry.id %} " > edit entry</a > </small > </h4 > <div class ="card-body" > {{ entry.text|linebreaks }} </div > </div > {% empty %} <p > There are no entries for this topic yet.</p > {% endfor %} {% endblock content %}
修改后的界面如下
项目部署 这部分内容主要就是把项目部署到远程服务器上,让其他人访问,全是配置,没啥意思,不搞了