咱们在开发一个网站的时候,无可避免的须要设计实现网站的用户系统。此时咱们须要实现包括用户注册,登陆,用户认证,注销,修改密码等功能。Django做为一个完美主义者的终极框架,固然也会想到用户的这些痛点,它内置了强大的用户认证系——auth,因此本文在不创建User模型的状况下实现用户的注册,登陆和认证。另外对Django Auth自带的User模型进行扩展,运行用户添加更多的我的信息。css
我在以前的Django学习笔记(9)——开发用户注册与登陆系统 中已经完成了这个注册与登陆系统,那么下面在这里写主要是向基于Django自带的User模型从新创建本身用户的模型。html
咱们用Django2.0开发一个叫users的APP,来实现如下六项功能:前端
因为Django Auth自带的User模型字段有限,咱们还须要自定义模型UserProfile对其扩展。html5
(Auth组件的相关内容能够参考:Django学习笔记(13)——Django的用户认证组件,视图层和QuerySet API)python
首先咱们看一下Django Auth模块自带User模型所包含字段:jquery
username:用户名 email: 电子邮件 password:密码 first_name:名 last_name:姓 is_active: 是否为活跃用户。默认是True is_staff: 是否为员工。默认是False is_superuser: 是否为管理员。默认是False date_joined: 加入日期。系统自动生成。
下面展现咱们自定义的UserProfile模型git
user: 与User是1对1关系 org:用户名 telephone: 电话 mod_date: 最后修改日期。系统自动生成
整个用户注册和登陆很是简单,好比没有邮箱验证,也不能经过第三方APP受权登陆,我作的页面也比较丑陋、一个更好的方式是使用以及成熟的第三方Django Package(好比django-allauth)来实现用户注册和登陆。程序员
编码规范需求: 1. 代码规范遵照pep8 (https://python.org/dev/peps/pep-0008/) 2. 函数有相应的注释 3. 程序有文档说明文件(README.md) 4. 程序的说明文档必须包含的内容:程序的开发环境(django版本)、程序的实 现的功能、程序的启动方式、登陆用户信息、程序的运行效果 5. 程序设计的流程图:
基本的项目开启什么的都在这里再也不阐述了,我直接写项目的主要思路,基础的Django操做流程看我以前的博客就行。github
到目前为止,我学习Django,第一步都是建立模型。为何呢?由于大多数语言都遵循软件开发的设计模式MVC。数据库
这里咱们创建一个名称为UserProfile的模型,代码以下:
from django.db import models # Create your models here. from django.contrib.auth.models import User class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') # 模型类中设置:blank=True,表示代码中建立数据库记录时该字段可传空白(空串,空字符串) org = models.CharField('Organization', max_length=128, blank=True) telephone = models.CharField('Telephone', max_length=50, blank=True) mod_data = models.DateTimeField('Last modified', auto_now=True) class Meta: verbose_name = 'User profile' def __str__(self): # return self.user.__str__() return "{}".format(self.user.__str__())
注意:咱们并没改变Django Auth 自带的User模型,也没有创建新的User模型。而UserProfile只是对User模型的扩展。找到users/models.py,并建立上面的UserProfile模型。因为咱们引入了Django Auth自带的User模型,因此咱们必须开始先把它import进来。
自定义的UserProfile模型新增字段的意思以下:
user: 与User是1对1关系 org:用户名 telephone: 电话 mod_date: 最后修改日期。系统自动生成
我比较习惯先写URL,再写视图View,从URL配置上,你应该能够理解咱们想实现的六个功能。
下面是users/urls.py代码内容:
from django.urls import re_path, path from users import views app_name = 'users' urlpatterns = [ re_path(r'^register/$', views.register, name='register'), re_path(r'^login/$', views.login, name='login'), re_path(r'^user/(?P<pk>\d+)/profile/$', views.profile, name='profile'), re_path(r'^user/(?P<pk>\d+)/profile/update/$', views.profile_update, name='profile_update'), re_path(r'^user/(?P<pk>\d+)/pwd_change/$', views.pwd_change, name='pwd_change'), re_path(r"^logout/$", views.logout, name='logout'), ]
上面,咱们使用 name= '?' ,好比第一个:咱们这是给动态连接 /register/取了个名字 ‘register’,这样咱们就能够在HTML模板里面经过 {% url 'users:register' %} 来调用这个连接了。
咱们须要编写六个视图函数,正如上面url路径所指。
因为这六个视图中,register , login,profile , pwdChange这个四个视图都须要用到表单,因此咱们先建立表单代码。
(表单代码咱们能够分离出去,在users目录下建立MyForms.py文件,而后再在里面建立form表单,最后将其导入视图函数,这样代码显得工整,清晰,并且后期容易维护。还有MyForms.py能够经过clean方法自定义表单验证,很是便捷,并且逻辑更清晰)。
from django import forms from django.contrib.auth.models import User import re def email_check(email): pattern = re.compile(r"\"?([-a-zA-Z0-9.'?{}]+@\w+\.\w+)\"?") return re.match(pattern, email) class RegistrationForm(forms.Form): username = forms.CharField(label='Username', max_length=50) email = forms.EmailField(label='Email') password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Password Confirmation', widget=forms.PasswordInput) #user clean methods to define custom validation rules def clean_username(self): username = self.cleaned_data.get('username') if len(username) < 3: raise forms.ValidationError("your username must be at least 3 characters log") elif len(username) > 20: raise forms.ValidationError("your username is too long") else: filter_result = User.objects.filter(username__exact=username) if len(filter_result) > 0: raise forms.ValidationError('your username already exists') return username def clean_email(self): email = self.cleaned_data.get('email') if email_check(email): filter_result = User.objects.filter(email__exact=email) if len(filter_result) > 0: raise forms.ValidationError("your email already exists") else: raise forms.ValidationError("Please enter a valid email") return email def clean_password1(self): password1 = self.cleaned_data.get('password1') if len(password1) < 3: raise forms.ValidationError("your password is too short") elif len(password1) > 20: raise forms.ValidationError("your password is too long") return password1 def clean_password2(self): password1 = self.cleaned_data.get('password1') password2 = self.cleaned_data.get('password2') if password1 and password2 and password1 != password2: raise forms.ValidationError('Password mismatch Please enter again') return password2 class LoginForm(forms.Form): username = forms.CharField(label='Username', max_length=50) password = forms.CharField(label='Password', widget=forms.PasswordInput) # use clean methods to define custom validation rules def clean_username(self): username = self.cleaned_data.get('username') if email_check(username): filter_result = User.objects.filter(email__exact=username) if not filter_result: raise forms.ValidationError('This emial does not exist') else: filter_result = User.objects.filter(username__exact=username) if not filter_result: raise forms.ValidationError('This username does not exist Please register first') return username class ProfileForm(forms.Form): first_name = forms.CharField(label='First Name', max_length=50, required=False) last_name = forms.CharField(label='Last Name', max_length=50, required=False) org = forms.CharField(label='Organization', max_length=50, required=False) telephone = forms.CharField(label='Telephone', max_length=50, required=False) class PwdChangeForm(forms.Form): old_password = forms.CharField(label='Old Password', widget=forms.PasswordInput) password1 = forms.CharField(label='New Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Password Confirmation', widget=forms.PasswordInput) # use clean methods to define custom validation rules def clean_password1(self): password1 = self.cleaned_data.get('password1') if len(password1) < 6: raise forms.ValidationError("your password is too short") elif len(password1) > 20: raise forms.ValidationError("your password is too long") return password1 def clean_password2(self): password1 = self.cleaned_data.get('password1') password2 = self.cleaned_data.get('password2') if password1 and password2 and password1 != password2: raise forms.ValidationError("Password mismatch Please enter again") return password2
上面的代码之因此很是长,是由于咱们用clean方法加入了不少表单验证项。好比检查用户名是否太短,是否过长,是否已经存在等等。
咱们在以前,Django学习笔记(14)——AJAX与Form组件知识补充(局部钩子和全局钩子详解) ,一文中详细学习了局部钩子和全局钩子,也就是clean方法的使用,若是不懂的话能够先学习这一篇博文。毕竟真实的网站开发都是须要加上这些验证规则的。
废话就说这么多,固然你也能够在View视图里面写Form验证规则。随你。下面直接看个人视图users/views.py的内容:
from django.shortcuts import render, HttpResponse, get_object_or_404 from .MyForms import RegistrationForm, LoginForm, ProfileForm, PwdChangeForm from .models import UserProfile from django.contrib.auth.models import User from django.http import HttpResponseRedirect from django.contrib import auth from django.urls import reverse from django.contrib.auth.decorators import login_required # Create your views here. @login_required def profile(request, pk): user = get_object_or_404(User, pk=pk) return render(request, 'users/profile.html', {'user': user}) @login_required def profile_update(request, pk): user = get_object_or_404(User, pk=pk) user_profile = get_object_or_404(UserProfile, user=user) if request.method == 'POST': form = ProfileForm(request.POST) if form.is_valid(): user.first_name = form.cleaned_data['first_name'] user.last_name = form.cleaned_data['last_name'] user.save() user_profile.org = form.cleaned_data['org'] user_profile.telephone = form.cleaned_data['telephone'] user_profile.save() return HttpResponseRedirect(reverse('users:profile', args=[user.id])) else: default_data = {'first_name': user.first_name, 'last_name': user.last_name, 'org': user_profile.org, 'telephone': user_profile.telephone,} form = ProfileForm(default_data) return render(request, 'users/profile_update.html', {'form': form, 'user': user}) def register(request): if request.method == 'POST': form = RegistrationForm(request.POST) if form.is_valid(): username = form.cleaned_data['username'] email = form.cleaned_data['email'] password = form.cleaned_data['password2'] # 使用内置User自带create_user方法建立用户,不须要使用save() user = User.objects.create_user(username=username, password=password, email=email) # 若是直接使用objects.create()方法后不须要使用save() user_profile = UserProfile(user=user) user_profile.save() return HttpResponseRedirect("/accounts/login/") else: form = RegistrationForm() return render(request, 'users/registration.html', {'form': form}) def login(request): if request.method == 'POST': form = LoginForm(request.POST) if form.is_valid(): username = form.cleaned_data['username'] password = form.cleaned_data['password'] user = auth.authenticate(username=username, password=password) if user is not None and user.is_active: auth.login(request, user) return HttpResponseRedirect(reverse('users:profile', args=[user.id])) else: # 登陆失败 return render(request, 'users/login.html', {'form': form, 'message':'Wrong password Please Try agagin'}) else: form = LoginForm() return render(request, 'users/login.html', {'form': form}) @login_required def logout(request): auth.logout(request) return HttpResponseRedirect("/accounts/login/") @login_required def pwd_change(request, pk): user = get_object_or_404(User, pk=pk) if request.method == "POST": form = PwdChangeForm(request.POST) if form.is_valid(): password = form.cleaned_data['old_password'] username = user.username user = auth.authenticate(username=username, password=password) if user is not None and user.is_active: new_password = form.cleaned_data['password2'] user.set_password(new_password) user.save() return HttpResponseRedirect('/accounts/login/') else: return render(request, 'users/pwd_change.html', {'form': form, 'user': user, 'message': 'Old password is wrong Try again'}) else: form = PwdChangeForm() return render(request, 'users/pwd_change.html', {'form': form, 'user': user})
下面咱们分别看看几个视图函数是如何工做的:
register视图函数:
login视图函数:
profile视图函数:
pwd_change视图函数:
在users目录下建立/templates/users/文件夹,编写HTML模板。其目录结构以下:
展现一个 login.html 代码:
{% extends 'base.html' %} {% load staticfiles %} {% block title %}<h1 class="my-con1">Login Page</h1>{% endblock %} {% block css %} <link rel="stylesheet" href="{% static '/CSS/login.css' %}"> {% endblock %} {% block content %} {% if message %} {{ message }} {% endif %} <div class="form-group"> <form method="post" action="" enctype="multipart/form-data"> {% csrf_token %} {% for field in form %} <div class="form-group "> {{ field.errors }} {{ field.label_tag }}{{ field }} {% if field.help_text %} <p class="help">{{ field.help_text|safe }}</p> {% endif %} </div> {% endfor %} <div class="btn"> <input type="submit" value="Login"> </div> </form> <h2 class="my-href pull-right"> <a href="/accounts/register" >Register Link</a> </h2> </div> {% endblock %}
若是你要开发一个好的网站或者网络应用,你就必须了解经典的软件开发所遵循的MVC设计模式。Django做为最优秀的基于Python语言的网站开发框架,固然也遵循了这种设计模型。下面就学习一下什么是MVC框架以及Django网站开发是如何遵循这种软件开发设计模型的。
MVC是Model-View-Controller(模型-试图-控制器)模式
若是把MVC比喻成一个粽子,那么View就是最外面一层的绿色玉米叶,是咱们能够直接看到的。Controller就是中间那层熟糯米,而粽子的的核心天然然是最里面的那一层的肉馅Model模型了。
MVC最大的优势是实现了软件或网络应用开发过程当中数据,业务逻辑和界面的分离,使软件开发更清晰,也是维护变得更容易。这与静态网页设计中使用HTML和CSS实现了内容和样式的分离是同一个道理。
Django网站开发全靠四件套:Model(模型),URL(连接),View(视图)和Template(模板)。他们看似与MVC设计模式不太一致,其实本质是相同的。可是Django的View和经典的View确实有很是大的不一样。Django四件套与经典的MVC对应关系以下。
使用Django开发网站的第一步绝对是定义模型(Model),若是写个不须要使用数据库的小应用,也彻底能够不定义模型,直接写URL和View。固然先写URL仍是View,其实均可以,彻底取决于我的偏好。通常来讲,从上向下思考问题的话,先写URL。不过不影响。
Django自带的User Admin 只能编辑管理基础字段如用户名和电子邮件。咱们如今但愿能扩展User Admin,在编辑 User的同时编辑咱们User Profile 里的额外字段(如电话)。去实现这个功能,咱们须要按照以下代码修改 users/admin.py:
from django.contrib import admin from django.contrib.auth.models import User from django.contrib.auth.admin import UserAdmin from .models import UserProfile admin.site.unregister(User) class UserProfileInline(admin.StackedInline): model = UserProfile class UserProfileAdmin(UserAdmin): inlines = [UserProfileInline, ] admin.site.register(User, UserProfileAdmin)
下面是实际效果,咱们会发现UserAdmin后多了一栏 user Profiles。
进入后台的方法(前提是项目URL等东西要配置好): 1,启动项目 python manage.py runserver 2,在浏览器中打开地址: 127.0.0.1:8000/users/admin
(咱们进入admin后台,向下拉,会发现)
根目录下新建一个static目录,并将其解压后的Bootstrap-3.3.7目录,总体拷贝到static目录中,并且因为Bootstrap依赖于jQuery,因此咱们须要引入jQuery,而且在static目录下,新建一个CSS和JS目录,做为之后的样式文件和JS文件的存放地,最后以下图所示:
而后打开项目的settings文件,在最下面添加配置,用于指定静态文件的搜索目录:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
一个网站要有本身的统一风格和公共部分,能够将这部份内容集中到一个基础模板 base.html中。如今,在根目录下的template中建一个 base.html 文件做为站点的基础模板。
在Bootstrap文档中,为咱们提供了一个很是简单并且又实用的基础模板,代码以下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其余内容都*必须*跟随其后! --> <title>Bootstrap 101 Template</title> <!-- Bootstrap --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 --> <!-- 警告:经过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起做用 --> <!--[if lt IE 9]> <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script> <![endif]--> </head> <body> <h1>你好,世界!</h1> <!-- jQuery (Bootstrap 的全部 JavaScript 插件都依赖 jQuery,因此必须放在前边) --> <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script> <!-- 加载 Bootstrap 的全部 JavaScript 插件。你也能够根据须要只加载单个插件。 --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script> </body> </html>
咱们将其总体拷贝到 base.html文件中。由于咱们将Bootstrap的目录都拷贝下来了,因此咱们不须要cdn引入,咱们能够将路径更改成本地的文件路径。
更改后的代码以下:
{% load staticfiles %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其余内容都*必须*跟随其后! --> <!-- Bootstrap --> <link href="/static/bootstrap-3.3.7/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 --> <!-- 警告:经过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起做用 --> <!--[if lt IE 9]> <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script> <![endif]--> </head> <body> <!-- jQuery (Bootstrap 的全部 JavaScript 插件都依赖 jQuery,因此必须放在前边) --> <script src="/static/JS/jquery-3.2.1.js"></script> <!-- 加载 Bootstrap 的全部 JavaScript 插件。你也能够根据须要只加载单个插件。 --> <script src="/static/bootstrap-3.3.7/dist/js/bootstrap.min.js"></script> </body> </html>
注意:Bootstrap的全部JavaScript插件都依赖jQuery,因此必须将jQuery放在前面。
<!-- jQuery (Bootstrap 的全部 JavaScript 插件都依赖 jQuery,因此必须放在前边) --> <script src="/static/JS/jquery-3.2.1.js"></script> <!-- 加载 Bootstrap 的全部 JavaScript 插件。你也能够根据须要只加载单个插件。 --> <script src="/static/bootstrap-3.3.7/dist/js/bootstrap.min.js"></script>
{% static ‘相对路径’ %} 这个Django为咱们提供的静态文件加载方法,能够将页面与静态文件连接起来。
{% block title%} AAA {% endblock %} 设置了专门的 title。
经过block css 引入了针对性的 css样式文件。
更改后的 base.html 内容以下:
{% load staticfiles %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其余内容都*必须*跟随其后! --> {% block title %} <title>base</title> {% endblock %} <!-- Bootstrap --> <link href="/static/bootstrap-3.3.7/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 --> <!-- 警告:经过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起做用 --> <!--[if lt IE 9]> <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script> <![endif]--> <link rel="stylesheet" href="/static/CSS/base.css"> {% block css %} {% endblock %} </head> <body> <div class="container my-con"> {% block content %} {% endblock %} </div> <!-- jQuery (Bootstrap 的全部 JavaScript 插件都依赖 jQuery,因此必须放在前边) --> <script src="/static/JS/jquery-3.2.1.js"></script> <!-- 加载 Bootstrap 的全部 JavaScript 插件。你也能够根据须要只加载单个插件。 --> <script src="/static/bootstrap-3.3.7/dist/js/bootstrap.min.js"></script> </body> </html>
由于有四五个,这里只举其中一个例子来讲。其余的大同小异。(注意:个人前端水平有点差,因此页面不是那么美观,见谅见谅啊)
好比登陆页面,首先咱们继承 base.html的内容,而后将本身的后端逻辑代码填入其中,以下:
{% extends 'base.html' %} {% load staticfiles %} {% block title %}<h1 class="my-con1">Login Page</h1>{% endblock %} {% block css %} <link rel="stylesheet" href="{% static '/CSS/login.css' %}"> {% endblock %} {% block content %} {% if message %} {{ message }} {% endif %} <div class="form-group"> <form method="post" action="" enctype="multipart/form-data"> {% csrf_token %} {% for field in form %} <div class="form-group "> {{ field.errors }} {{ field.label_tag }}{{ field }} {% if field.help_text %} <p class="help">{{ field.help_text|safe }}</p> {% endif %} </div> {% endfor %} <div class="btn"> <input type="submit" value="Login"> </div> </form> <h2 class="my-href pull-right"> <a href="/accounts/register" >Register Link</a> </h2> </div> {% endblock %}
而后,咱们写样式的话,须要在 static/CSS目录下新建一个样式文件,而后里面写样式(注意:我就写了简单的样式,哈哈哈哈哈)。
body{ background: #e6e6e6; } .my-con1{ text-align: center; color: steelblue; } .my-href{ margin-top: 50px; }
这样一个简单的登陆页面就出来了:
默认的Field是Model中定义的Field,如需更改,可在Form类内以同名字段覆盖,好比自定义widget和required属性等。
下面来学习class Meta内嵌类的全部元数据选项(meta options),
abstract
Optional.abstract 若是abstract = True ,这个model就是一个抽象基类
app_label
Options.app_label 若是一个model定义在默认的models.py以外(例如,若是你的APP的models在myapp.models 子模块下),你必须定义app_label 让DJango知道他属于哪个APP app_label = 'myapp'
db_table
Options.db_table 定义该model在数据中的表名称: db_table = 'music_album'
(注意:在MySQL中使用小写字母做为数据库表名称)
为了节省时间,Django会自动的使用你的model class的名称和包含这个model的APP名称来构建数据库的表名称,一个model的数据库表名称是经过将‘app label’ (你在manage.py startapp 中使用的名称 和model的类名称,加上一个下划线在他们之间来构成)。
例如,若是你有一个APP叫作bookstore(使用manage.py startapp bookstore建立),以及一个model定义为class Book这样将会建立一个名为bookstore_book的数据库表。
若是想自定义数据库的表名称,须要在class Meta 使用db_table参数来自定义。
若是你的数据库表名称是一个SQL保留字,或者它包含不容许出如今Python变量中的字符(好比连字符)这是没问题的,由于Django会自动给列名添加引号。
Django模型中的verbose_name咱们经常可能须要使用,好比将数据库里面的数据导出成csv文件。那么,csv文件的表头的名字能够经过取每一个字段的verbose_name来获取,数据能够经过queryset语句来获取。这样制做出来的csv表就能像数据库同样,字段名和字段值一一对应了。
Options.verbose_name 指明一个易于理解和表述的对象名称,单数形式: verbose_name = 'user_name' 若是这个值没有设定,Django将会使用该model的类名的分词形式做为其对象的表述名 CamelCase将会转换为camel case
from django.db import models class Meta: # 按 Priority降序, order_date升序排列 get_latest_by = ['-priority', 'order_date'] # 自定义数据库里表格的名称 db_table = 'music_album' # 按照什么排序 ordering = ['pub_date'] # 定义APP的标签 app_label = 'myapp' # 声明此类是否抽象 abstract = True # 添加受权 permissions = (('Can deliver pizzas', 'Can deliver pizzas'))
假设咱们须要在模板中经过连接指向一篇具体的文字,下面哪一种方式更好?
方法1: 使用命名URL <a href="{% url 'article' id %}Article</a> 方法2:使用常规URL——不建议 <a href="blog/article/id">Article</a>
若是你尚未意识到方法1的好处,那么想一想假设你须要把所有模板连接由 blog/article/id 改成blog/articles/id,那种方法更快?,改全部模板,仍是改URL配置里的一个字母?
惋惜的是命名的URL通常只在模板里使用,不能直接在视图里使用。若是咱们有了命名的URL,咱们如何把它转化为常规的URL在视图里使用呢?Django提供的reverse()方法很容易实现这点。假设不一样的APP(好比news和blog)里都有article这个命名URL,咱们怎么样区分呢?咱们只须要在article前面加上blog这个命名空间便可。
from django.urls import reverse # output blog/article/id reverse('blog:article', args=[id])
并且,咱们须要在urls.py 里面,给动态连接 取名字,好比:
from django.urls import re_path from . import views app_name = 'blog' urlpatterns = [ re_path(r'^articles/$', views.articles_content, name='articles'), ]
这样咱们就能够在HTML模板中经过{% url 'arrticle' id %} 来调用这个连接了。
Django的模型(Model)的本质是类,并非一个具体的对象(object)。当你设计好模型后,你就能够对Model进行实例化从而建立一个具体的对象。Django对于建立对象提供了两种不一样的save与create方法,下面来具体分析一下二者的不一样。
咱们先来看一下下面的例子,咱们已经设计好了一个Person的模型:
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name
用save方法建立一个名为 james 的具体对象,咱们能够这么作。记住你只有用了save()方法后,Django才会将这个对象的信息存储到数据库中。
james_obj = Person(name="LeBron james") james_obj.save()
正由于用save方法建立对象有两步,并且程序员容易忘记加上 save(),Django提供了一个更便捷的create方法,以下:若是你使用create方法,无需再加上save(),create方法不只建立了新的对象,并且直接将信息存储到数据库里。
james_obj = Person.objects.create(name="LeBron james")
create只能用于建立新的对象,在数据库层老是执行 insert 的操做。save不只用于建立新的对象,也能用于更新对象的现有数据,在数据库老是先执行update,找不到具体对象后再执行 insert 的操做,对于建立全新的对象,二者均可以。若是更新已有对象信息,只能用save()方法。
若是你要用Auth自带的User模型建立新对象,你须要使用create_user方法,而不是create方法,以下所示。(create_user方法颇有用,自动会给密码加hash )。
user1 = User.objects.create_user(username=username, username=password)
当咱们注册一个harden,注册成功后会自动跳转到登陆页面。
下面咱们登陆harden的信息,登陆成功后跳转到harden的用户信息页面。
进入详细信息页面,咱们会发现下面会有三个按钮,分别是编辑,修改密码,退出按钮。首先咱们点击编辑按钮。进入编辑页面。更新信息,结果以下:
下面咱们进入修改密码页面:
当咱们点击退出,就会从新进入登陆页面。再次进入我的信息页面,咱们就会发现新的信息。
若是登陆的用户名不存在,会出现下面报错信息:
其余的就很少作演示了。
请移步小编的GitHub:传送门
参考文献: https://blog.csdn.net/weixin_42134789/article/details/80194532
原文出处:https://www.cnblogs.com/wj-1314/p/10558765.html