form组件
form组件出现的原因
- 当我们用普通的form表单提交时会刷新页面,如果这个我们表单中的某项填错了,刷新后我们正确的选项也没有了.
- 传统的form表单需要我们自己亲自校验每一项.工作量太大
Django 中form组件的2大功能:
1. 前端自动生成表单元素。
2.自动验证表单内容信息。
3. 保留用户上次输入的信息。
form组件自动生成form表单的方法
不要忘记,表单的输出不包含submit
按钮,和表单的<form>
标签。 你必须自己提供。
方法1
对于<input>
/<label>
对,还有几个输出选项:
{{ form.as_table }}
以表格的形式将它们渲染在<tr>
标签中{{ form.as_p }}
将它们渲染在<p>
标签中{{ form.as_ul }}
将它们渲染在<li>
标签中
注意,你必须自己提供<ul>
或<table>
元素。
views.py
from django.shortcuts import render from django import forms # Create your views here. class RegisterFrom(forms.Form): #定义一个类 user=forms.CharField() #和我们input表单一一对应 pwd=forms.CharField() def register(request): if request.method=="POST": form_obj=RegisterFrom() # 1 实例化对象 return render(request,"register.html",{"form_obj":form_obj})
#分析题目:
#1这步运行后到底干了什么 ,实例化了一个变量 self.fields={"user":"user规则","pwd":"pwd规则对象"}
html
<form action=""> {{ form_obj.as_p }} #p的意思就是p标签的意思 <p><input type="submit" value="注册"></p> </form> </body> </html>
前端页面:
方法二:手动渲染任意标签
可以自己写任意标签,但是方法1,只能用人家规定好的p标签
在上面的例子中,Django会根据{{ form.as_p }}自动生成表单,我们也可以自己来具体编写表单的HTML;每个字段都是表单的一个属性
除了HTML不同外其余的都相同
html
<form action=""> <P><label for="">用户名</label> {{ form_obj.user }} </P> <P><label for="">密码 </label> {{ form_obj.pwd }} </P> <p><input type="submit" value="注册"></p> </form>
方法三:循环表单字段
如果你为你的表单使用相同的HTML,你可以使用{% for %}
循环迭代每个字段来减少重复的代码
<div class="container"> <div> <h3>数据展示页面</h3> <hr> <a href="{{ add_url }}"><button class="btn btn_success ">添加按钮</button></a> <table class="table table-striped"> <thead> <tr> {# 运用了form组件的第三种方法#} {% for header in header_list %} <th>{{ header }}</th> {# 仅仅是获得了一个字符串名字 #} {% endfor %} </tr> </thead> <tbody> {% for data in new_display %}{# data为每条信息对象 #} <tr> {% for item in data %} <td>{{ item }}</td> {# 每条对象信息的每个属性值 #} {% endfor %} </tr> {% endfor %} </tbody> </table> </div> </div>
forms源码分析
views.py
def register(request): """ 注册函数 :param request: :return: """ if request.method=="POST": form_obj=RegisterForm(request.POST)#拿到前端返回的数据赋给form对象 response={"user":None,"error_msg":None} if form_obj.is_valid(): #进行表单验证 username1=form_obj.cleaned_data.get("username") pwd=form_obj.cleaned_data.get("password") email1=form_obj.cleaned_data.get("email") phone=form_obj.cleaned_data.get("phone") avatar1=request.FILES.get("avatar") obj=UserInfo.objects.create_user(username=username1,password=pwd,email=email1,telephone=phone,avatar=avatar1) response["user"]=obj.username else: response["error_msg"]=form_obj.errors return HttpResponse(json.dumps(response)) form_obj=RegisterForm() #get请求时返回给前端的对象 return render(request,"register.html",{"form_obj": form_obj})
源码分析:
form_obj=RegisterFrom() #实例化一个Form对象
通过源码来看这里:具体的源码我没有大看懂,以后有机会细看
得到最关键的东西就是:
self.fields={'user':验证规则‘,'pwd’:'验证规则} self.is_bound=True
self._errors = None
接着我们来看post的请求到来后数据验证的源码:
进入到is_valid()中我们可以看到:
def is_valid(self): """ Returns True if the form has no errors. Otherwise, False. If errors are being ignored, returns False. """ return self.is_bound and not self.errors
由于is_bound我们知道是True,所以要执行if form_obj.is_valid(),那么self.errors必须为False.那么我们就直接来看self.errors了:
@property def errors(self): "Returns an ErrorDict for the data provided for the form" if self._errors is None: #由于我们知道self._errors初始化的时候为None self.full_clean() return self._errors
我们就直接来看 full_clean()函数执行了啥?
def full_clean(self): """ Cleans all of self.data and populates self._errors and self.cleaned_data. """ self._errors = ErrorDict() if not self.is_bound: # Stop further processing. return self.cleaned_data = {} # If the form is permitted to be empty, and none of the form data has # changed from the initial data, short circuit any validation. if self.empty_permitted and not self.has_changed(): return self._clean_fields() #校验字段 self._clean_form() # self._post_clean() #
这里我们得到关键的信息:
self._errors=ErrorDict()
self.cleaned_data={}
我们接着看到三个方法,先看第一个方法 self._clean_fields()
def _clean_fields(self): for name, field in self.fields.items():#循环self.fields中每个字段名和字段的校验规则 # value_from_datadict() gets the data from the data dictionaries. # Each widget type knows how to retrieve its own data, because some # widgets split data over several HTML fields. if field.disabled: value = self.get_initial_for_field(field, name) else: value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))#得到从前端来的数据 try: if isinstance(field, FileField): #判断是不是文件 initial = self.get_initial_for_field(field, name) value = field.clean(value, initial) else: #如果不是文件,这里就开始进行校验 value = field.clean(value) # self.cleaned_data[name] = value # if hasattr(self, 'clean_%s' % name):# value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self.add_error(name, e) #
我们来看 field.clean(),这个方法主要是来验证用户输入的内容是否符合规则,符合规则就不返回值,不符合规则,把错误信息放到errors中,然后抛出错误?,注意这里的clean方法指的是fields类中的clean方法。
def clean(self, value): """ Validates the given value and returns its "cleaned" value as an appropriate Python object. Raises ValidationError for any errors. """ value = self.to_python(value) self.validate(value) self.run_validators(value) return value
我们来看to_python(value),这里我们要去Charfield类中找这个方法
def to_python(self, value): "Returns a Unicode object." if value not in self.empty_values: value = force_text(value) if self.strip: value = value.strip() if value in self.empty_values: return self.empty_value return value
to_python 方法通过force_text
方法,将填写进表单的数据转为字符串类型的。
我们再来看self.validate(value)
def validate(self, value): if value in self.empty_values and self.required: raise ValidationError(self.error_messages['required'], code='required')
我们可以猜到:如果表单内容为空就抛异常
接着我们来看:self.run_validators(value)
def run_validators(self, value): if value in self.empty_values: #判断是否为空 return errors = [] for v in self.validators: try: v(value) #验证规则 except ValidationError as e: if hasattr(e, 'code') and e.code in self.error_messages: e.message = self.error_messages[e.code] errors.extend(e.error_list) if errors: #如果有一个错误就抛异常 raise ValidationError(errors)
我们可以看到run_validators()就是来验证用户输入的内容和规则是否匹配,如果匹配,不返回任何值,如果不匹配,把错误放到errors列表中,并抛出错误。
我们接着来看self._clean_fields()方法剩下的内容:
如果用户输入的内容符合规则,就把字段名和值,存到字典self.cleaned_data字典中。
在往下走就看到局部钩子了:
if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value
局部钩子:如果用户输入的内容复合验证规则,就返回用户输入的信息,如果不符合就直接抛出ValidationError。
继续往下走:当try中出现异常时:就执行:self.add_error(name, e)
def add_error(self, field, error): """ Update the content of `self._errors`. The `field` argument is the name of the field to which the errors should be added. If its value is None the errors will be treated as NON_FIELD_ERRORS. The `error` argument can be a single error, a list of errors, or a dictionary that maps field names to lists of errors. What we define as an "error" can be either a simple string or an instance of ValidationError with its message attribute set and what we define as list or dictionary can be an actual `list` or `dict` or an instance of ValidationError with its `error_list` or `error_dict` attribute set. If `error` is a dictionary, the `field` argument *must* be None and errors will be added to the fields that correspond to the keys of the dictionary. """ if not isinstance(error, ValidationError): # Normalize to ValidationError and let its constructor # do the hard work of making sense of the input. error = ValidationError(error) if hasattr(error, 'error_dict'): if field is not None: raise TypeError( "The argument `field` must be `None` when the `error` " "argument contains errors for multiple fields." ) else: error = error.error_dict else: error = {field or NON_FIELD_ERRORS: error.error_list} for field, error_list in error.items(): if field not in self.errors: if field != NON_FIELD_ERRORS and field not in self.fields: raise ValueError( "'%s' has no field named '%s'." % (self.__class__.__name__, field)) if field == NON_FIELD_ERRORS: self._errors[field] = self.error_class(error_class='nonfield') else: self._errors[field] = self.error_class() self._errors[field].extend(error_list) if field in self.cleaned_data: del self.cleaned_data[field]
add_error()方法:把出现问题的字段名和错误类型放到self._errors字典中也就是self.errors静态方法中。
到这里为止:self._clean_fields()方法分析完毕
我们接着来分析full_clean(self):中的第二个self._clean_form()方法:
def _clean_form(self): try: cleaned_data = self.clean() except ValidationError as e: self.add_error(None, e) else: if cleaned_data is not None: self.cleaned_data = cleaned_data
我们进入到self.clean()中发现什么也没有干又把,clean_data返回了
def clean(self): """ Hook for doing any extra form-wide cleaning after Field.clean() has been called on every field. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field named '__all__'. """ return self.cleaned_data
我们来翻译下上面这段英文:
在每个字段上调用Field.clean()之后进行任何额外的表单范围清理。
此方法引发的任何ValidationError都不会与特定字段关联;
它将与名为'__all__'的字段建立特殊关联。
说明这个clean就是用来校验格外的规则,我们自己定义的规则,如果用户输入的内容符合我们自己的规则,就把self.cleaned_data返回来。这里的英文中写了,全局钩子的错误信息也在errors中对应的
字段是__all__.
也就是说self._clean_form()方法是用来校验全局钩子的。
我们接着来分析full_clean(self):中的第三个self._post_clean()方法。啥都没有
def _post_clean(self): """ An internal hook for performing additional cleaning after form cleaning is complete. Used for model validation in model forms. """ pass
英文意思:
内部挂钩,用于在表单清洁完成后执行额外清洁。
用于模型表单中的模型验证
到这里为止:full_clean()的所有方法源码都看完了。作用是:校验所有的表单数据,出现错误后把抛出对应异常,并把错误字段名和错误信息放到self._errors中和把有效内容的字段名和信息放到self.cleaned_data中
最后总结:is_valid方法得到一个布尔值,如果用户输入的内容有错误则返回False不执行后边的代码。
代码实现
Form.
is_valid
() :Form
对象的首要任务就是验证数据。 对于绑定的Form
实例,可以调用is_valid()
验证成功
不管表单提交的是什么数据,一旦通过is.valid() 的验证,返回True,则,验证后的表单数据将位于form.cleaned_data
字典中。 这些数据已经为你转换好为Python 的类型。
cleaned_data:,验证后的表单数据将位于form.cleaned_data
字典中
注意:此时你也可以从request.post获得验证成功的数据传到数据库(如果需要的话),但是我们更期待你从form.cleaned_data字典中来获得数据.
def register(request): if request.method=="POST": form=UserForm(request.POST) if formsis_valid(): user=form.cleaned_data["username"] pwd=form.cleaned_data["password"] obj=User.objects.create(name=username,password=pwd) return HttpResponse("注册成功")
验证失败
form.errors:验证失败后的表单数据将位于form.errors的字典中
在这个字典中,键为字段的名称,值为表示错误信息的Unicode 字符串组成的列表。 错误信息保存在列表中是因为字段可能有多个错误信息。
访问errors
属性可以获得错误信息的一个字典:
>>> f.errors {'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
Form.
add_error
(field, error)
该方法允许从Form.clean()
方法中或从表单外部向特定字段添加错误;例如从一个角度。
field
参数为字段的名称。 如果值为None
,error 将作为Form.non_field_errors()
返回的一个非字段错误。
error
参数可以是一个简单的字符串,或者最好是一个ValidationError
实例。 Raising ValidationError 中可以看到定义表单错误时的最佳实践。
注意,Form.add_error()
会自动删除cleaned_data
中的相关字段。
print(form_obj.errors)
结果为:html代码
<ul class="errorlist"><li>user<ul class="errorlist"><li>Ensure this value has at least 6 characters (it has 2).</li></ul></li></ul>
整理一下,我们得到: user的错误信息
<ul class="errorlist"> <li>user <ul class="errorlist"> <li>Ensure this value has at least 6 characters (it has 2).</li> </ul> </li> </ul>
print(type(form_obj.errors))
<class 'django.forms.utils.ErrorDict'> 是一个特殊的字典类型
那我们就用字典的方法取值: print(type(form_obj.errors["user"]))
<class 'django.forms.utils.ErrorList'> #表名这是一个列表类型,可以用列表方法取值
print(type(form_obj.errors["user"][0]))
class str 表名这是一个字符串类型
print((form_obj.errors["user"][0]))
Ensure this value has at least 6 characters (it has 2).
print(type(form_obj.fields)
<class 'collections.OrderedDict'>
局部钩子
字段校验有时候很不灵活,我们只能校验field的字段中有的规则,没有的规则,我们就不能在字段中校验,比如说,我们要校验用户名不能全为数字,这时候你在字段中就校验不出来了.这就是出现了局部钩子的概念
局部钩子:用来校验某一个字段,自己额外添加的规则, 定义函数的名称必须为 def clean_字段(self),为什么写的原因在下边源码中已经解释了
try:
if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() #self表示当前对象 self.cleaned_data[name] = value #这个校验的字段成功后放在了cleaned_data的字典中,你可以从这个字典中获得对应的数据
except ValidationError as e:
self.add_error(name, e) #如果校验不通过,就把它添加到errors的字典中去
例子:
def clean_user(self): """ 局部钩子 验证用户名是否注册过 :return: """ username=self.cleaned_data.get("user")#从已经符合字典中取出相应的字段,也就是说先判断is_valid(),然后再校验局部钩子 data1=models.UserInfo.objects.filter(username=username) if not data1: return username else: raise ValidationError("该用户已经注册")
全局钩子
当校验规则涉及到了多个字段时候,这时候就要用全局钩子了.
def clean(self): """ 全局钩子,验证两次密码是否一致 :return: """ pwd=self.cleaned_data.get("password") repeat_pwd=self.cleaned_data.get("repeat_password") if pwd and repeat_pwd: #判断是否有值,因为只要前边有错误,也会走全局钩子 if pwd == repeat_pwd: return self.cleaned_data else: raise ValidationError("密码不一致") else: return self.cleaned_data
源码解析,
全局钩子走这个源码: self._clean_form()
def _clean_form(self): try: cleaned_data = self.clean() except ValidationError as e: self.add_error(None, e) else: if cleaned_data is not None: self.cleaned_data = cleaned_data
def clean(self): """ Hook for doing any extra form-wide cleaning after Field.clean() has been called on every field. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field named '__all__'. """ return self.cleaned_data #这里面什么都没干,只是返回了cleaned_data中的数据,我们就可以扩展这个钩子
Form.
non_field_errors
()
这个方法返回Form.errors
中不是与特定字段相关联的错误。 它包含在Form.clean()
中引发的ValidationError
和使用Form.add_error(None, "...")
添加的错误。
注意在前端你要console.log(form.field.errors),你会发现在errors这个字典中会出现"__all__",这个字段.这个就是存放全局钩子错误信息的字段
__all__: 在错误信息中全局钩子对应的字段是__all__
modelForm
modelForm说白了就是结合了model和form的特性
models.py
class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField(default=12) publishs=models.ForeignKey(to="Publish") authors=models.ManyToManyField(to="Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email = models.CharField(max_length=32,default=111) def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) def __str__(self): return self.name
views.py 拿bookmodel来举例子:
from django import forms
from django.shortcuts import render,HttpResponse,redirect
from .models import *
class BookForm(forms.Form): title=forms.CharField(max_length=8) price=forms.IntegerField() publish_list = Publish.objects.all().values_list("pk", "name") author_list = Author.objects.all().values_list("pk", "name") t=((1,"北京"),(2,"天津")) state=forms.ChoiceField(choices=publish_list) state1=forms.MultipleChoiceField(choices=author_list)
你会发现在model中定义了字段,但是又在form中定义了一遍,是不是感觉到冗余,ModelForm就是基于这个问题出现的.
像这样:
from .models import * from django.forms import ModelForm #导入ModelForm这个类 class BookModelForm(ModelForm): class Meta: model=Book # fields=["title","price"] #只展示"title"和"price"字段 fields="__all__" #展示所有的字段 labels={ "title": "书名", "price":"价格", "publishs":"出版社", "authors":"作者" }#自定义字段在前端显示的名称 error_messages={ "title":{"required":"不能为空"} } form_obj=BookModelForm()#生成一个ModelForm类的对象
字段类型
生成的Form
类中将具有和指定的模型字段对应的表单字段,顺序为fields
属性中指定的顺序。
每个模型字段有一个对应的默认表单字段。具体字段详情见中文文档
QuerySet
表示成ChoiceField
,它是一个django.forms.ModelChoiceField
,其选项是模型的ForeignKey
。QuerySet
表示成MultipleChoiceField
,它是一个django.forms.ModelMultipleChoiceField
,其选项是模型的ManyToManyField
。
- 如果模型字段设置了blank=True,那么表单字段的required字段会设置为False值。
否则,
required=True
。 - 表单字段的
verbose_name
设置为模型字段的label
,并将第一个字母大写。 - 表单字段的
help_text
设置为模型字段的help_text
。 - 如果模型字段设置了choices,那么表单字段的widget将会设置为Select,选择项从模型字段的choices而来。
选项通常会包含空选项,并且会默认选择。如果字段是必选的,它会强制用户选择一个选项。 如果模型字段的
default
且具有一个显示的default
值,将不会包含空选项(初始将选择blank=False
值)。
选择要展示的字段:
强烈建立选择展示所有的字段
fields=["title","price"] #只展示"title"和"price"字段
(1)设置'__all__'
属性为特殊的值fields
以表示需要使用模型的所有字段.
(2)设置Meta
内联的ModelForm
类的exclude
属性为一个要从表单中排除的字段的列表
lass PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title']
save()
每个ModelForm还具有一个save()方法。 这个方法根据表单绑定的数据创建并保存数据库对象。
ModelForm
的子类可以接受现有的模型实例作为关键字参数instance
;如果提供此功能,则save()
将更新该实例
如果没有提供,save()
将创建模型的一个新实例:
这就是和form本质的区别
def add(request): if request.method=="GET": forms_obj=BookModelForm() return render(request,"add.html",locals()) else: forms_obj=BookModelForm(request.POST)#创建一个对象 if forms_obj.is_valid(): forms_obj.save() #在modelform中直接一句话就等于取出数据添加到数据库了操作了.
#如果不用modelForm用form的话,要取出传进来的参数,创建对象然后通过create添加到数据库
return redirect("/index/") else: return render(request, "add.html", locals()) def edit(request,id): edit_book = Book.objects.filter(pk=id).first() if request.method=="GET": forms_obj=BookModelForm(instance=edit_book) return render(request,"edit.html",locals()) else: forms_obj= BookModelForm(data=request.POST,instance=edit_book)#更新对象 if forms_obj: forms_obj.save() return redirect("/index/")
form和modelForm的区别
- 减少了代码重复
- 提交和更新数据更简洁
Django的权限管理auth模块
如果你想用这个auth模块: 大前提要用Django生成的auth_user 表,不能自己再创建user表了
auth模块是什么?
auth模块是Django提供的标准权限管理系统,可以提供用户身份认证, 用户组和权限管理。详细参考该博客:https://www.cnblogs.com/ccorz/p/6358074.html
auth可以和admin模块配合使用, 快速建立网站的管理系统。
在INSTALLED_APPS中添加'django.contrib.auth'使用该APP, django中的auth模块默认启用.
django.contrib.auth.middleware.AuthenticationMiddleware 这个是auth模块的中间件
认证登录
1 #导入auth模块: 2 from django.contrib import auth
auth模块提供了老多的方法在这里我们只介绍三种方法:
authenticate() 验证用户
提供了用户认证,即验证用户名以及密码是否正确,一般需要username password两个关键字参数
如果认证信息有效,会返回一个 User 对象。authenticate()会在User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登录过程中是需要的。当我们试图登陆一个从数据库中直接取出来不经过authenticate()的User对象会报错返回None!!
user = authenticate(username='someone',password='somepassword') #把从前端传过来的用户名和密码在这里进行验证
login(request,user) 给用户绑定session
该函数接受一个request的对象以及认证了的user对象
此函数利用Django的session框架给某个已认证的用户加上session ID等信息.
当别的用户用同一浏览器登录该网站时,auth会检查session中的session_key如果在数据库中,就不需要添加了session了,如果不在,它会添加session,并把数据库中的以前用户的session给删掉,这点和
session组件不一样
from django.contrib.auth import authenticate,login #导入auth模块下的authenticate类和login类 def my_view(request): username=request.POST.get("username") password=request.POST.get("password") user=authenticate(username=username,password=password) if user is not None: login(request,user) #把request,和user传入login函数中进行session的写入操作,相当于request.session["name"]=username return redirect("/index/") else: return redirect("login")
logout(request)注销用户
from django.contrib.auth import logout def logout_view(request): logout(request)#其实这里相当于我们写的request.session.flush() return redirect("/login/")
该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错.
user对象的is_authenticated()
要求:
1 用户登陆后才能访问某些页面,
2 如果用户没有登录就访问该页面的话直接跳到登录页面
3 用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址
方法1:
def my_view(request): if not request.user.is_authenticated(): return redirect("%s?next=%s"%(settings.LOGIN_URL,request.path))##这句话不理解
方法2:login_required函数
django已经为我们设计好了一个用于此种情况的装饰器:login_requierd()
1
2
3
4
5
|
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ... |
若用户没有登录,则会跳转到django默认的 登录URL '/accounts/login/ ' (这个值可以在settings文件中通过LOGIN_URL进行修改)。并传递 当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。
user对象
当你用了auth模块后,request的会生成一个user属性,里边封装了你登录了的对象,这样你可以在request.user.用户的信息,如果你没有登录print(request.user)会打印出匿名用户对象的英文单词,
重点: 你可以在所有的视图函数和HTML页面的中用request.user来获取登录人的相关信息
User 对象属性:username, password(必填项)password用哈希算法保存到数据库
is_staff : 用户是否拥有网站的管理权限.
is_active : 是否允许用户登录, 设置为``False``,可以不用删除用户来禁止 用户登录
user对象的方法
is_authenticated()
如果是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经通过了认证。
通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了认证。 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name.
def index(request): user=request.user if not user.is_authenticated(): return redirect("/login/") return render(request,"index.html",local())
注册用户create_user()
使用 create_user 注册用户:
from django.contrib.auth.models import User #这个就是Django自己创建的auth_user表,你要对他进行操作就需要用它创建的这个类User user=User.objects.create_user(username="xiaohia",password="123456") #注意这里不能用create()函数这样也能注册新用户但是密码不会加密,必须用人家提供的create_user()方法
user.save()#别忘记了保存
验证密码check_password(passwd)
用户需要修改密码的时候 首先要让他输入原来的密码 ,如果给定的字符串通过了密码检查,返回 True
def my_view(request): user=request.user if request.method=="POST": old_password=request.POST.get("old_password") if user.check_password(old_password): return HttpResponse("ok")
这儿你可能会对request.user不理解 在这里我们来讲一下
补充request.user
关于request的更多内容请见https://www.cnblogs.com/renfanzi/p/5838243.html, 这里关于request的内容整理的很好
一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。
如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你可以通过 is_authenticated() 区分它们。
例如:
1
2
3
4
|
if request.user.is_authenticated(): # Do something for logged-in users. else : # Do something for anonymous users. |
user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。
匿名用户
class models.AnonymousUser
django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点:
1
2
3
4
5
6
7
8
9
|
id 永远为None。 username 永远为空字符串。 get_username() 永远返回空字符串。 is_staff 和 is_superuser 永远为False。 is_active 永远为 False。 groups 和 user_permissions 永远为空。 is_anonymous() 返回True 而不是False。 is_authenticated() 返回False 而不是True。 set_password()、check_password()、save() 和 delete () 引发 NotImplementedError。 |
New in Django 1.8:
新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。
修改密码set_password()
from django.contrib.auth.models import User user=User.objects.get(username="xiaohia") user.set_password(password="123456") user.save() #别忘记了保存
注册用户
def sign_up(request): state = None if request.method == 'POST': password = request.POST.get('password', '') repeat_password = request.POST.get('repeat_password', '') email=request.POST.get('email', '') if password == '' or repeat_password == '': state = 'empty' elif password != repeat_password: state = 'repeat_error' else: username = request.POST.get('username', '') if User.objects.filter(username=username): state = 'user_exist' else: new_user = User.objects.create_user(username=username, password=password,email=email) new_user.save() new_my_user = MyUser(user=new_user, telephone=request.POST.get('telephone', '')) ####这步是什么意思 new_my_user.save() return redirect('/book/') content = { 'state': state, 'user': None, } return render(request, 'book/sign_up.html', content)
修改密码:
@login_required def set_password(request): user = request.user state = None if request.method == 'POST': old_password = request.POST.get('old_password', '') new_password = request.POST.get('new_password', '') repeat_password = request.POST.get('repeat_password', '') if user.check_password(old_password): if not new_password: state = 'empty' elif new_password != repeat_password: state = 'repeat_error' else: user.set_password(new_password) user.save() return redirect("/log_in/") else: state = 'password_error' content = { 'user': user, 'state': state, } return render(request, 'book/set_password.html', content)
中介模型
用中介模型的原因
我们知道我们可以用manytomany创建出第三张关系表, 我们以前都做过这么几张表,学生表和课程表,学生表和课程表是多对多的关系, 但是我有这么一个需求,我想在第三张关系表中记录每个学生每门课程的分数,这时候应该怎么办,我们都知道不能在关系表中添加字段,这时候就需要第四章表了,第4张表就是中介模型,
建表代码:
class Student(models.Model): name=models.CharField(max_length=32) courses=models.ManyToManyField(to="Course",through="Student2Course") #在manytomany中增加一个through字段 关联到第4张表 class Course(models.Model): name = models.CharField(max_length=32) class Student2Course(models.Model): student=models.ForeignKey(to="Student") #用这步的原因的是外键关联到那两张表会自动建立id字段 course=models.ForeignKey(to="Course") score=models.IntegerField()
表结构:
发现了没? 以前用manytomany自动建立的第三张表student_courses 没有了,出现了一个新的表 student2course,让我们来看看这张表
是不是以前的关系表都可以继承过来了,还增加了新字段.
注意事项
以前\manytomany操作第3张表的方法,很多都不能用了比如add、 create,remove,但是clear方法却是可用的。它可以清空某个实例所有的多对多关系, 但是我们可以用操作普通表的方式来操作第4张表.